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:
| Resource | Value |
|---|---|
| Service build | Dockerfile, path deploy/railway/Dockerfile |
| Volume mount | /var/lib/portunus |
| Public HTTP networking | enabled, normal Railway generated domain is fine |
| TCP Proxy | application port 7443 |
| Service variable | PORTUNUS_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-clientmust 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_LISTENonly if you change the TCP Proxy application port; the default is0.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
Configaction — see Installation → Interactive manager.