Configuration
All sensitive or provider-specific material lives under your config directory (~/.config/pr0xteus/ per-user, /etc/pr0xteus/ system-wide), stays out of Git, and stays out of Docker build contexts:
secrets/wireguard/*.conf: real WireGuard files.secrets/pools.yaml: named pools and their approved config basenames.config/egress-routing.yaml: country-to-pool policy..env: the private bearer token, absolute host configuration path, runtime user, ports and controller image selection.
The installer writes .env; it is not something you need to create by hand. pr0xteus setup and pr0xteus upgrade refresh .env.example next to it, so new settings show up there first. The controller reads the pool and routing files once at startup, so restart the stack after changing them.
pools.yaml
pools:
primary:
region: operator-defined
purpose: general-outbound
configs: [my-provider-node, my-provider-node-2]
exit_countries:
my-provider-node: US
my-provider-node-2: US
fallback_pool: backup
backup:
region: operator-defined
purpose: general-outbound
configs: [backup-node]
exit_countries:
backup-node: US
| Field | Required | Meaning |
|---|---|---|
pools.<name> | yes | Logical pool name. Callers can request it with {"pool":"<name>"}. At least one pool must exist. |
configs | yes | Basenames of files in secrets/wireguard/ without .conf. Every name must exist when the controller starts. |
exit_countries | no | Maps a basename to its ISO 3166-1 alpha-2 exit country. Every key must be one of the pool’s configs. Without an entry, the controller guesses the country from a legacy <country>-<location> file name. |
fallback_pool | no | Pool to try when this one is exhausted or unavailable and the caller sent "fallbackOk": true. It must name a defined pool. |
region, purpose | no | Free text shown in GET /v1/pools. |
A pool runs at most one cell at a time. Configurations that just failed are skipped for TUNNEL_POOL_FAILURE_CACHE_TTL before the controller tries them again.
egress-routing.yaml
country_to_pool:
US: primary
default_pool: primary
country_to_pool maps a requested country code (case does not matter) to a pool. default_pool handles countries with no entry. Every pool named here must exist in pools.yaml.
Keep default_pool set. When it is missing, the controller falls back to TUNNEL_POOL_DEFAULT_POOL, whose default is neutral_clean, and refuses to start unless a pool with that name exists. config check does not apply that fallback, so it can pass while startup fails. See troubleshooting.
.env settings
These are the values the installer writes and the wrapper and Compose read. .env.example holds the current list.
| Variable | Default | Meaning |
|---|---|---|
PR0XTEUS_CONFIG_DIR | set by the installer | Absolute host path of the config directory. Docker mounts it into the controller at the same path, so the controller can ask Docker to bind one WireGuard file into a cell. |
PR0XTEUS_RUNTIME_USER | installing user’s UID:GID | User the controller runs as, so it can read owner-only config files. setup and upgrade keep it current. |
PR0XTEUS_API_TOKEN | random, generated | Bearer token for the control API. Required, at most 4096 bytes. |
PR0XTEUS_CONTROLLER_IMAGE | the latest release | Controller image. setup and upgrade pin it to a release tag. |
PR0XTEUS_HTTP_HOST_PORT | 127.0.0.1:8000 | Host binding for the control API, as HOST:PORT. |
PR0XTEUS_METRICS_HOST_PORT | 127.0.0.1:9091 | Host binding for /healthz and /metrics. |
PR0XTEUS_SOCKS_HOST_PORT | 127.0.0.1:1080 | Host binding for the SOCKS5 gateway. |
PR0XTEUS_HTTP_PROXY_HOST_PORT | 127.0.0.1:8080 | Host binding for the HTTP proxy gateway. |
PR0XTEUS_SOCKS_PUBLIC_ADDRESS | 127.0.0.1:1080 | host:port written into returned SOCKS5 URLs. Never 0.0.0.0. |
PR0XTEUS_HTTP_PROXY_PUBLIC_ADDRESS | 127.0.0.1:8080 | host:port written into returned HTTP proxy URLs. Never 0.0.0.0. |
PR0XTEUS_DISABLE_HOST_PORTS | false | true removes every controller host port. Use it with the Tailscale sidecar or another private gateway. |
PR0XTEUS_TAILSCALE_ENABLED | false | true starts the Tailscale sidecar profile. See Tailscale sidecar. |
TS_AUTHKEY | placeholder | Auth key from your tailnet, used on the sidecar’s first login. |
TS_HOSTNAME | pr0xteus | Tailnet host name of the sidecar. |
TS_EXTRA_ARGS | --accept-dns=false | Extra tailscale up arguments, such as --login-server for Headscale or --advertise-tags. |
LOG_LEVEL | info | debug, info, warn or error. |
LOG_FORMAT | json | json or text. |
LOG_ADD_SOURCE | true | Include the source location in log records. |
The wrapper also reads PR0XTEUS_HOME from your shell to find a config directory in a non-default place.
Controller environment
The controller binary reads these variables (internal/pkg/services/pr0xteus/config.go). The supplied docker-compose.yml sets the ones it needs from .env, so a normal install never sets them by hand. They matter when you run the image some other way.
| Variable | Default | Meaning |
|---|---|---|
PR0XTEUS_API_TOKEN | none, required | Bearer token. Empty or longer than 4096 bytes fails startup. |
TUNNEL_POOL_LISTEN_ADDR | :8000 | Control API listener. |
TUNNEL_POOL_METRICS_ADDR | :9091 | /healthz and /metrics listener. |
TUNNEL_POOL_SOCKS_ADDR | :1080 | SOCKS5 gateway listener. |
TUNNEL_POOL_SOCKS_PUBLIC_ADDR | 127.0.0.1:1080 | Address in returned SOCKS5 URLs. A host is required. |
TUNNEL_POOL_HTTP_PROXY_ADDR | :8080 | HTTP proxy gateway listener. |
TUNNEL_POOL_HTTP_PROXY_PUBLIC_ADDR | 127.0.0.1:8080 | Address in returned HTTP proxy URLs. A host is required. |
TUNNEL_POOL_PROXY_LEASE_TTL | 15m | How long returned proxy URLs stay valid. Must be positive. |
TUNNEL_POOL_POOLS_FILE | secrets/pools.yaml | Pool policy file. |
TUNNEL_POOL_BUNDLE_DIR | secrets/wireguard | Directory of WireGuard .conf files. |
TUNNEL_POOL_ROUTING_FILE | config/egress-routing.yaml | Country routing file. |
TUNNEL_POOL_DEFAULT_POOL | neutral_clean | Default pool used only when the routing file has no default_pool. |
TUNNEL_POOL_IDLE_TIMEOUT | 10m | Unused time before a cell with no active connections is removed. |
TUNNEL_POOL_SPAWN_TIMEOUT | 20s | How long a new cell has to become healthy. |
TUNNEL_POOL_HEALTH_CHECK_INTERVAL | 30s | How often the controller calls each hot cell’s /healthz. |
TUNNEL_POOL_HEALTH_HANDSHAKE_MAX_AGE | 180s | Host-loopback test mode only: age of the last healthy check after which a cell counts as unhealthy. |
TUNNEL_POOL_FAILURE_CACHE_TTL | 5m | How long a failed configuration is skipped. |
PR0XTEUS_CELL_NETWORK | empty | Docker network cells join for egress. Compose sets pr0xteus-egress. Empty selects a host-loopback test mode. |
PR0XTEUS_CELL_CONTROL_NETWORK | empty | Internal network between the controller and cells. Compose sets pr0xteus-cell-control. |
PR0XTEUS_CELL_SOCKS_PORT | 1080 | SOCKS5 port inside each cell. |
PR0XTEUS_CELL_CONTROL_PORT | 9090 | Control HTTP port inside each cell. Must differ from the SOCKS5 port. |
PR0XTEUS_MANAGED_SCOPE | default | Scope label that separates this controller’s cells from another controller on the same Docker daemon. Compose sets pr0xteus-compose. Must not be empty. |
PR0XTEUS_PARENT_ID | the container host name | Controller identity stamped on each cell, used to find its children again. |
PR0XTEUS_DOCKER_HOST | empty (Docker default) | Docker API endpoint. Compose points it at tcp://docker-socket-proxy:2375. |
Cell image pairing
The controller chooses its cell image itself, from the build version baked into its binary: psyb0t/pr0xteus:cell-<version>. CI bakes sha-<commit>, so the published :latest controller and the release tag promoted from it both pull the cell built from the same commit (cell-sha-<commit>). Releases up to v0.11.6 were rebuilt on their tag and pull cell-vX.Y.Z instead. A local development build uses dev and pulls psyb0t/pr0xteus:cell-dev. There is no environment setting for the cell image.
To stay on a particular release, set PR0XTEUS_CONTROLLER_IMAGE=psyb0t/pr0xteus:vX.Y.Z in ~/.config/pr0xteus/.env, run pr0xteus setup, then run pr0xteus start.
See installation and upgrades for installer paths, upgrades, ports and tailnet access.