Skip to content

Install the binary with systemd

Run gpm without a container runtime: one static CGO-free binary, a system user, a data directory and a hardened unit file.

No container runtime is required. git must be on PATH at runtime, not just to build: the config store shells out to it for every commit.

Build

git clone https://github.com/Rake-Pro/go-proxy-manager
cd go-proxy-manager
make build              # -> bin/gpm, with VERSION/COMMIT/DATE stamped via ldflags

go build -trimpath -o /usr/local/bin/gpm ./cmd/gpm works too, but skips the version stamping make build does, fine for a quick local test, not for something you will run gpm version against later. The container image installs git explicitly for the same reason.

User, data directory and secrets

sudo useradd --system --no-create-home --shell /usr/sbin/nologin gpm
sudo install -d -o gpm -g gpm -m 0750 /var/lib/gpm /var/lib/gpm/config /var/lib/gpm/certs
sudo install -d -o root -g gpm -m 0750 /etc/gpm

sudo install -m 0755 bin/gpm /usr/local/bin/gpm

# Password hash: create the file 0600 FIRST, the same reasoning as the
# Ingress-discovery token below - a plain redirect creates it world-readable
# for the window between creation and chmod.
sudo install -m 0600 -o gpm -g gpm /dev/null /etc/gpm/admin_hash
/usr/local/bin/gpm hashpw 'your-password' | sudo tee /etc/gpm/admin_hash >/dev/null

Environment file

/etc/gpm/gpm.env, mode 0640 root:gpm so only the service can read it:

GPM_CONFIG_DIR=/var/lib/gpm/config
GPM_CERT_DIR=/var/lib/gpm/certs
GPM_SESSION_DB=/var/lib/gpm/session.db
GPM_LOCAL_ADMIN_USER=admin
GPM_LOCAL_ADMIN_PASSWORD_HASH_FILE=/etc/gpm/admin_hash
GPM_LOG_LEVEL=info

Add any other flags from the table above as GPM_* lines here: there is no separate bare-metal flag surface.

Unit file

/etc/systemd/system/gpm.service:

[Unit]
Description=go-proxy-manager
Documentation=https://github.com/Rake-Pro/go-proxy-manager
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=gpm
Group=gpm
EnvironmentFile=/etc/gpm/gpm.env
ExecStart=/usr/local/bin/gpm
Restart=on-failure
RestartSec=2s

# Bind :80/:443 as the non-root gpm user, with no setuid binary and no root
# process - the systemd equivalent of the container image's non-root USER.
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true

# Filesystem hardening. A container gets most of this from its own root
# filesystem being throwaway; state it explicitly here instead.
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/gpm
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Start and verify

sudo systemctl daemon-reload
sudo systemctl enable --now gpm
sudo systemctl status gpm
curl -fsS http://127.0.0.1:8081/healthz && echo   # -> ok
journalctl -u gpm -f                              # structured JSON logs; add GPM_LOG_CONSOLE=1 for human-readable
Check Expected
systemctl status gpm active (running)
curl -fsS http://127.0.0.1:8081/healthz ok
curl -fsS http://127.0.0.1:8081/version The version you built
journalctl -u gpm No error lines about the config directory or the admin credential