Deploy Pomerium
Identity-aware proxy that puts SSO in front of any web app
pomerium
Just deployed
Just deployed
/var/lib/postgresql/data
Just deployed
authenticate
Just deployed
verify
Just deployed
Deploy and Host Pomerium on Railway
Pomerium is an open-source identity-aware reverse proxy. Rather than putting applications behind a VPN or bolting a login screen onto each, you put Pomerium in front: every request is authenticated against an identity provider, then authorised against a policy you write, before it reaches the application. Teams use it to publish internal dashboards, admin panels and legacy apps to the internet without giving any of them a user database. Self-host Pomerium and the sessions, the policy engine and TLS termination stay on infrastructure you control.
This template deploys Pomerium as five services already wired together. pomerium is the gateway: it runs the proxy, authorize and databroker roles and owns the URL your users visit. authenticate runs Pomerium's login hop on its own domain, because the browser is redirected there for the OIDC exchange. dex is a bundled CNCF identity provider, so the template works the moment it deploys instead of asking you to register an OAuth client first. verify is a demo app that renders the identity Pomerium forwarded to it, and Postgres holds session state and Dex's storage. You supply an admin email and password; every shared secret is generated at boot.

Getting Started with Pomerium on Railway
Set ADMIN_EMAIL and ADMIN_PASSWORD when you deploy — together they become the one account in the bundled identity provider, and that email is the default allow-list for every route. When the deploy finishes, open the pomerium service's public URL. You are redirected to authenticate and then the Dex sign-in form; enter the admin email and password. Pomerium sends you back to the gateway and you land on the demo app, which reports Identity found and verified with the claims from your signed identity token. That page proves the deploy is healthy: the OIDC exchange completed, the session reached Postgres, and the upstream got Pomerium's signed assertion.
Visit /.pomerium/ to inspect the session, user id, issuer and expiry, and /.pomerium/sign_out to end it — the next request returns to the login form. To put your own application behind the proxy, point UPSTREAM_URL on the pomerium service at its private address, such as http://my-app.railway.internal:3000, and add more with UPSTREAM_2_URL plus UPSTREAM_2_PREFIX, up to five. Widen access with ALLOWED_EMAILS or ALLOWED_DOMAINS.



About Hosting Pomerium
Pomerium replaces perimeter access control with per-request authorisation. A VPN decides once, at connection time, that a device is inside the network; Pomerium decides on every HTTP request, from the user's identity, their groups and the route's policy. The application behind it stays private and needs no auth code.
- Per-route policy in Pomerium Policy Language — by email, domain, group or claim
- Any OIDC provider — Google Workspace, Okta, Entra ID, Auth0, Keycloak, or the bundled Dex
- Signed identity forwarding — a verifiable JWT plus
X-Pomerium-Claim-*headers for apps that do header SSO - A self-service dashboard at
/.pomerium/showing session, devices and routes - WebSocket and streaming support, so proxied dev tools and dashboards work
Pomerium's four internal roles can run in one process or split across several. This template splits them because the login hop and the gateway each need their own hostname: authenticate handles the redirect to and from the identity provider, while pomerium runs the proxy, the authorize service and the databroker. The two talk over private gRPC.
Why Deploy Pomerium on Railway
Railway removes the parts of a Pomerium deployment that cost the most time.
- TLS is terminated at the edge, so there are no certificates to issue or renew
- Every service gets a private address, so upstreams never need a public URL
- Postgres is provisioned and connected
- The bundled provider means no external OAuth registration before first login
- Session state lives in Postgres, so the gateway scales horizontally
Common Use Cases
- Publishing internal tools safely — a Grafana, Airflow or Prometheus instance reachable from a browser, with no VPN and no second login screen
- Adding SSO to software that has none — legacy apps gain Google or Okta login with no code change, through forwarded headers
- Contractor and vendor access — grant one address exactly one route, then revoke it by editing a list
- Replacing a VPN for HTTP services — per-request authorisation for the web apps that are most VPN traffic
Dependencies for Pomerium
- pomerium —
pomerium/pomerium:latest, with the Envoy data plane embedded in the binary. Runs the proxy, authorize and databroker roles. - authenticate — the same image, running only that role on its own domain.
- dex —
ghcr.io/dexidp/dex:v2.45.1-alpine, a CNCF OpenID Connect provider, pinned because Dex'slatesttracks its main branch. - verify —
pomerium/verify:latest, the demo upstream displaying forwarded identity. - Postgres — Railway's managed PostgreSQL. Pomerium creates a
pomeriumschema; Dex uses the default.
Environment Variables Reference
| Variable | Service | Purpose |
|---|---|---|
ADMIN_EMAIL | pomerium | The initial account and default allow-list |
ADMIN_PASSWORD | dex | Password for that account, hashed at boot |
UPSTREAM_URL | pomerium | The application the gateway proxies to |
UPSTREAM_2_URL … _5_URL | pomerium | Extra upstreams, each with a matching _PREFIX |
ALLOWED_EMAILS, ALLOWED_DOMAINS | pomerium | Comma-separated addresses or domains allowed through |
IDP_PROVIDER, IDP_PROVIDER_URL, IDP_CLIENT_ID | pomerium, authenticate | Point at your own OIDC provider to replace Dex |
Deployment Dependencies
- Source repository: https://github.com/gridalpha/pomerium-railway
- Upstream: https://github.com/pomerium/pomerium and https://github.com/dexidp/dex
- Documentation: https://www.pomerium.com/docs
Hardware Requirements for Self-Hosting Pomerium
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU per service | 2 vCPU for the gateway |
| RAM | 512 MB gateway, 256 MB others | 1 GB gateway |
| Storage | None outside Postgres | 1 GB Postgres volume |
| Runtime | Linux container | Linux container |
Self-Hosting Pomerium with Docker
A minimal local Pomerium needs one config file, config.yaml:
authenticate_service_url: https://authenticate.example.com
idp_provider: oidc
idp_provider_url: https://dex.example.com
idp_client_id: pomerium
idp_client_secret: replace-me
routes:
- from: https://app.example.com
to: http://app:8080
pass_identity_headers: true
policy:
- allow:
or:
- email: {is: you@example.com}
Run it with the published image, mounting that file:
docker run -d --name pomerium \
-v "$PWD/config.yaml:/pomerium/config.yaml:ro" \
-p 443:443 \
pomerium/pomerium:latest
Behind a TLS-terminating proxy add INSECURE_SERVER=true and ADDRESS=:8080, so Pomerium serves plain HTTP on a port the proxy reaches. The Railway template does that, and renders the route table from environment variables so you never edit YAML by hand.
How Much Does Pomerium Cost to Self-Host?
Pomerium Core is free and open source under Apache 2.0, with no user cap, no route cap and no feature gate on policy; Dex is Apache 2.0 too. The company sells an enterprise console with audit logs and directory sync, but nothing here depends on it. On Railway you pay only for compute and Postgres.
FAQ
What is Pomerium? Pomerium is an open-source identity-aware reverse proxy. It authenticates each request against an identity provider and authorises it against a policy, so apps behind it need no login of their own.
What does this Railway template deploy? Five services: the Pomerium gateway, Pomerium's authenticate role on its own domain, a Dex identity provider, a demo upstream that displays your forwarded identity, and PostgreSQL for sessions.
Why does the template include a database? Pomerium's databroker stores sessions, users and device records. Keeping them in PostgreSQL rather than memory means a redeploy does not sign everyone out, and it is what lets the gateway run more than one replica.
Why is there a separate authenticate service? Pomerium redirects the browser to its authenticate role for the OIDC exchange, and that role needs its own hostname. A second service gives it one while the gateway keeps the URL your users bookmark.
How do I use Google, Okta or Entra ID instead of the bundled provider?
Set IDP_PROVIDER, IDP_PROVIDER_URL, IDP_CLIENT_ID and IDP_CLIENT_SECRET on both pomerium and authenticate, register https:///oauth2/callback as the redirect URI, then delete the dex service.
How does my application know who the user is?
Every proxied request carries X-Pomerium-Jwt-Assertion, a signed JWT, plus X-Pomerium-Claim-Email, -User and -Groups. Verify the JWT against the gateway's JWKS endpoint, or read the claim headers directly if the app only supports header SSO.
Template Content
pomerium
gridalpha/pomerium-railwayauthenticate
gridalpha/pomerium-railwayverify
pomerium/verify:latest
