FRP Manager

Installation, configuration and uninstall

Requirements

  • Linux with systemd
  • Python 3.8+ (install script) or Docker
  • Architectures: amd64, arm64, arm

No need to install frp first: the script downloads frps/frpc and creates their systemd services. frp is then updated from the Updates page, when you decide: nothing is updated automatically.

Install script

curl -LO https://github.com/Gogowwww/frp-manager/releases/latest/download/frp-manager.zip
unzip frp-manager.zip && cd frp-manager
sudo bash install.sh

The script installs the panel in an isolated Python environment (/opt/frp-manager/venv), creates the frp-manager systemd service and starts it. It also prepares frp:

  • frps / frpc binaries in /usr/local/bin (latest version, SHA-256 checked);
  • default configs /etc/frp/frps.toml and /etc/frp/frpc.toml, only if they don't exist yet;
  • frps.service and frpc.service, created but neither enabled nor started: you configure them, then start them from the panel.

Running install.sh again from a newer archive updates the panel and keeps its configuration. The panel's Update button does the same without the command line.

Docker and Portainer

curl -O https://raw.githubusercontent.com/Gogowwww/frp-manager/main/docker-compose.yml
docker compose up -d

Portainer: Stacks → Add stack → Repository, URL https://github.com/Gogowwww/frp-manager, compose path docker-compose.yml, then Deploy the stack.

ImageContents
ghcr.io/gogowwww/frp-manager:latestlatest stable release (recommended)
ghcr.io/gogowwww/frp-manager:X.Y.Za specific version, to pin it or roll back
ghcr.io/gogowwww/frp-manager:devlatest pre-release, to test before everyone else

The container drives frp on the host through pid: host and nsenter, which gives it root-equivalent access to the host: read docker.md before deploying it.

First launch

As long as no password exists, the panel only shows an initial setup page: choose the administrator username and a password of at least 12 characters. No other page and no API route answers before that.

The HTTPS certificate is self-signed: your browser shows a warning on first access. For a valid certificate, put the panel behind a reverse proxy (nginx, Caddy) with Let's Encrypt.

Reaching the panel from another machine

A new install only listens on 127.0.0.1: the panel cannot be reached from the network until you decide so.

MethodHow
SSH tunnel (recommended)ssh -L 8765:127.0.0.1:8765 user@server, then https://127.0.0.1:8765
Local reverse proxynginx or Caddy listens on the network and forwards to 127.0.0.1:8765 (allow the WebSocket upgrade on /ws/)
Listen on the networkset bind_host to 0.0.0.0 in Settings → Network access (or in the config file), or FRP_MANAGER_HOST=0.0.0.0 with Docker, then restart the panel

If you open the panel to the network, filter access by IP (firewall, VPN). An install older than 0.0.51 keeps its current listening address.

nginx example (excerpt):

location / {
    proxy_pass https://127.0.0.1:8765;
    proxy_set_header Host $host;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Behind a TLS reverse proxy with ssl_enabled: false, add "cookie_secure": true to the config file so the session cookie stays Secure.

Versions and pre-releases

Every new version first ships as a pre-release, to be tested before it is offered to everyone. Numbers follow each other and a validated pre-release becomes the final release with the same number, without being rebuilt.

Pre-releaseRelease
GitHub pagemarked Pre-releaseLatest
Docker image:X.Y.Z + :dev:X.Y.Z + :latest
Offered to stable panelsnoyes
Offered to pre-release panels (install script)yesyes
  • Stable panel: it only sees releases.
  • Pre-release panel, install script: the "Update" button offers the most recently published version, pre-release or release.
  • Pre-release panel under Docker: change the image tag (:dev or :latest).

When the installed pre-release becomes a final release, the panel switches back to the stable channel on its own.

Panel configuration

Everything is set from the Settings page. The underlying file is /etc/frp-manager/frp-manager.json, readable by root only:

KeyDescriptionDefault
bind_hostListening address127.0.0.1 (new install)
bind_portPanel port8765
usernameLogin usernameadmin
password_hashHashed password (argon2id or scrypt, managed by the interface)created on first launch
secret_keySession signing key, drawn at random on first startgenerated
session_timeoutSession length in seconds3600
ssl_enabledHTTPS with a self-signed certificatetrue
cookie_secureSecure cookie even without HTTPS on the panel side (TLS reverse proxy)false
download_mirrorsThird-party mirrors as a fallback to github.com to download frptrue
nicknamesInstance nicknames (managed by the interface){}

bind_host, bind_port and ssl_enabled apply the next time frp-manager restarts.

Environment variables:

VariableEffect
FRP_MANAGER_HOSTListening address, takes precedence over bind_host
FRP_MANAGER_PORTPort of a new install (written to bind_port on first start)
FRP_MANAGER_NO_MIRRORS=1Forbids third-party download mirrors
FRP_MANAGER_CONFIGOther location for the configuration file

Download mirrors

If github.com does not respond, frp can be downloaded through third-party mirrors (mirror.ghproxy.com, ghfast.top, gh-proxy.com). These services see the file go through and could tamper with it: it is a supply-chain risk. The panel therefore only accepts an archive from a mirror if its SHA-256 matches the one published with the frp release, and says so in the install log. If github.com is reachable for you, disable the mirrors (Settings → Network access).

Lost password

sudo /opt/frp-manager/venv/bin/python3 /opt/frp-manager/app.py --reset-password
sudo systemctl restart frp-manager

With Docker: docker exec -it frp-manager python3 app.py --reset-password, then docker restart frp-manager.

File structure

Repository
  app.py                   Flask server + API
  install.sh               Install script
  Dockerfile, docker-compose.yml
  templates/
    index.html, login.html, setup.html
    partials/icons.html    SVG icons (embedded, no CDN)
    assets/
      css/app.css          Light and dark themes
      js/                  Application (ES modules, no build step)
      locales/fr.js, en.js Interface texts
  tests/                   pytest tests

/opt/frp-manager/          Installed panel (script)
/etc/frp-manager/          Panel configuration + SSL certificates
/etc/frp/                  frps/frpc configurations (TOML)
/var/log/frp/              frp logs
/var/lib/frp-manager/      State (installed versions)
/etc/systemd/system/       frp-manager.service, frps.service, frpc.service

The panel only writes frp configurations in the directories where it looks for them: /etc/frp, /usr/local/etc/frp, /opt/frp, /root/frp.

Uninstall

sudo systemctl disable --now frp-manager
sudo rm /etc/systemd/system/frp-manager.service
sudo rm -rf /opt/frp-manager /etc/frp-manager
sudo systemctl daemon-reload

frp configurations (/etc/frp/) and binaries (/usr/local/bin/) are kept.

History: go-mmproxy removal

Version 0.0.26 removed the "Real IP" option (go-mmproxy). If you used it, the panel automatically points the affected tunnels back to their real service on first start, restarts frpc, then removes the relays, the routing rules and the go-mmproxy binary. To pass on visitors' IP addresses, use the PROXY protocol option with a service that supports it (nginx, HAProxy…).

Edit this page on GitHub