Operations
This page covers running p2proxy as a long-lived service.
systemd
A minimal unit file:
# /etc/systemd/system/p2proxy.service
[Unit]
Description=Bitping p2proxy
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Environment=BITPING_API_KEY=<your-key>
ExecStart=/usr/local/bin/p2proxy --no-ui --config /etc/p2proxy/Config.yaml
Restart=on-failure
RestartSec=5
DynamicUser=yes
StateDirectory=p2proxy
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable --now p2proxy
journalctl -u p2proxy -f
DynamicUser=yes runs the daemon as a transient unprivileged user. StateDirectory=p2proxy gives it /var/lib/p2proxy for the libp2p keypair.
If you need to bind a SOCKS5 listener below port 1024, either pick a high port (recommended) or grant the binary the capability:
sudo setcap 'cap_net_bind_service=+ep' "$(which p2proxy)"
Docker
The official image is ghcr.io/bitpingapp/p2proxy:latest. NO_UI=true is set by default so the daemon doesn’t try to draw a TUI without a TTY.
docker run -d --name p2proxy \
-p 1080:1080 \
-p 45445:45445/udp \
-e BITPING_API_KEY=<your-key> \
-v "$PWD/Config.yaml:/app/Config.yaml:ro" \
ghcr.io/bitpingapp/p2proxy:latest
You need to expose:
- Every per-server SOCKS5 port (
-p 1080:1080, etc.) so clients on the host or network can reach it. - The libp2p UDP port (
-p 45445:45445/udp). Not strictly required, but improves direct-vs-relay connectivity.
docker-compose
A reference docker-compose.yml ships in the repo. Typical shape:
services:
p2proxy:
image: ghcr.io/bitpingapp/p2proxy:latest
restart: unless-stopped
environment:
BITPING_API_KEY: $BITPING_API_KEY
ports:
- "1080:1080"
- "45445:45445/udp"
- "9091:9091" # metrics
volumes:
- ./Config.yaml:/app/Config.yaml:ro
Prometheus metrics
p2proxy exposes Prometheus metrics on http://localhost:9091/metrics by default. Useful series:
| Series | Type | Notes |
|---|---|---|
p2proxy_peer_connections{state=...} | gauge | Peer connections by state. |
p2proxy_socks5_requests_total{outcome=...} | counter | Partitioned by success / error. |
p2proxy_bandwidth_bytes_total{direction=...} | counter | Bytes routed, both directions. |
p2proxy_stream_pool_size{server=..., state=...} | gauge | Pool size by state (idle / in-use). |
A minimal scrape config:
scrape_configs:
- job_name: p2proxy
static_configs:
- targets: ["localhost:9091"]
For dashboards, the metrics you’ll look at most often are pool idle size (should hover near min_idle) and the request counter split by outcome. A rising error rate is the leading indicator of peer trouble.
Upgrading
- Homebrew:
brew upgrade --cask BitpingApp/tap/p2proxy - Debian/Ubuntu/RHEL/Alpine: download the new package from the releases page and reinstall.
- Docker: pull the new tag and recreate the container —
docker compose pull && docker compose up -d. - From source:
git pull && cargo build --release -p p2proxy.
Restart the daemon to pick up the new binary. Config.yaml is read at startup, so config changes also require a restart.
Keypair / peer identity
p2proxy persists its libp2p keypair to ~/.config/p2proxy/node_keypair.bin (or $StateDirectory/node_keypair.bin under systemd). Deleting this file changes your peer ID; the daemon regenerates one on next start. You usually don’t need to touch this.
Next
- Troubleshooting — symptom-driven matrix.
- Support — bug reports, security, billing.