Portunus
Deployment

Deploy on Railway

Run the Portunus server template on Railway with persistent state and TCP Proxy for clients.

Railway can host portunus-server as a single service: the Web UI and operator HTTP API use Railway's public HTTP domain, while the gRPC control plane for portunus-client uses a Railway TCP Proxy pointed at application port 7443.

Template shape

The Railway build uses deploy/railway/Dockerfile. It builds the React Web UI first, embeds webui/dist/ into portunus-server, then runs the server from a small Debian runtime image.

At startup, deploy/railway/start-server.sh writes a minimal /var/lib/portunus/server.toml:

control_listen = "0.0.0.0:7443"
operator_http_listen = "0.0.0.0:${PORT}"
metrics_listen = "127.0.0.1:7081"
# Appended only when a public origin is resolved (see below):
operator_http_public_origin = "https://${RAILWAY_PUBLIC_DOMAIN}"

$PORT is the HTTP port assigned by Railway. The control-plane listener stays on 7443 so Railway's TCP Proxy can expose it separately. The first three lines are always written; the operator_http_public_origin line is only appended when a public origin is resolved — taken from PORTUNUS_OPERATOR_HTTP_PUBLIC_ORIGIN if set, otherwise from Railway's RAILWAY_PUBLIC_DOMAIN. If neither is set, the line is omitted entirely. Set PORTUNUS_OPERATOR_HTTP_PUBLIC_ORIGIN when you attach a custom domain.

Required Railway resources

Configure these in the Railway project:

ResourceValue
Service buildDockerfile, path deploy/railway/Dockerfile
Volume mount/var/lib/portunus
Public HTTP networkingenabled, normal Railway generated domain is fine
TCP Proxyapplication port 7443
Service variablePORTUNUS_ADVERTISED_ENDPOINT=<tcp-proxy-host>:<tcp-proxy-port>

The volume is not optional. It stores state.db, generated TLS material, and the server config. Without it, redeploys create a fresh server identity and lose operator state.

Railway mounts volumes as root; the Railway image intentionally runs as root so the mounted /var/lib/portunus path is writable without an extra service variable.

Set PORTUNUS_ADVERTISED_ENDPOINT after creating the TCP Proxy. Railway shows the generated proxy domain and proxy port in the TCP Proxy settings. Use that exact host:port value; enrollment commands created before this variable is set will advertise the wrong endpoint and should be recreated.

First-run onboarding

Deploy the service, then open the Railway HTTP domain. The Web UI is served at /.

If the store is empty, the server logs a setup token:

Portunus onboarding setup token: <token>

Paste that token into the Web UI and create the first superadmin account. The token expires after 30 minutes and rotates on restart while onboarding is still incomplete.

Client endpoint

After creating a TCP Proxy for application port 7443, set PORTUNUS_ADVERTISED_ENDPOINT to the proxy host:port and redeploy. Then enroll clients from the Web UI Clients page; the enrollment command makes portunus-client dial the TCP Proxy endpoint. Install and run the edge client with Docker (see Client Configuration) or the host installer (install.sh).

Notes

  • Railway's HTTP domain serves only the Web UI and /v1/* operator API.
  • portunus-client must dial the TCP Proxy endpoint, not the HTTP domain.
  • The Prometheus metrics listener stays on 127.0.0.1:7081; Railway does not expose it.
  • Set PORTUNUS_CONTROL_LISTEN only if you change the TCP Proxy application port; the default is 0.0.0.0:7443.
  • For a custom domain, set PORTUNUS_OPERATOR_HTTP_PUBLIC_ORIGIN=https://your.custom.domain.

On Railway the gRPC host differs from the local bind; set the advertised endpoint during install (the interactive wizard prompts for it) or later via the installer's Config action — see Installation → Interactive manager.

On this page