---
title: "Deploy Pomerium"
description: "Identity-aware proxy that puts SSO in front of any web app"
category: "Authentication"
url: https://railway.com/deploy/pomerium
---

# Deploy Pomerium

Identity-aware proxy that puts SSO in front of any web app

**[Deploy Pomerium on Railway](https://railway.com/template/pomerium)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/pomerium/manifest.json

- **Creator:** A3A
- **Category:** Authentication
- **Total deploys:** 1

## Template content

### pomerium https://cdn.jsdelivr.net/gh/pomerium/pomerium@v0.33.3/ui/src/static/logo_white.svg

- **Source:** https://github.com/gridalpha/pomerium-railway
- **Health check:** /healthz
- **Public domain:** Yes

### Postgres https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/postgresql.svg

- **Image:** ghcr.io/railwayapp-templates/postgres-ssl:18

### dex https://cdn.jsdelivr.net/gh/dexidp/website@main/static/img/logos/dex-glyph-white.svg

- **Source:** https://github.com/gridalpha/pomerium-railway
- **Health check:** /healthz
- **Public domain:** Yes

### authenticate https://cdn.jsdelivr.net/gh/pomerium/pomerium@v0.33.3/ui/src/static/logo_white.svg

- **Source:** https://github.com/gridalpha/pomerium-railway
- **Health check:** /healthz
- **Public domain:** Yes

### verify https://cdn.jsdelivr.net/gh/pomerium/pomerium@v0.33.3/ui/src/static/logo_white.svg

- **Image:** pomerium/verify:latest
- **Health check:** /healthz

## Documentation

# 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.

![Pomerium gateway, authenticate, Dex, verify and Postgres services on Railway](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789510877/pomerium-architecture.webp)

## 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`.

![Pomerium confirming a verified identity for the protected upstream app](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789510879/pomerium-identity-verified.webp)

![Signed identity token and X-Pomerium-Claim headers sent to the upstream](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789510881/pomerium-claim-headers.webp)

![Pomerium user dashboard listing the signed-in session details](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789510882/pomerium-session-dashboard.webp)

## 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's `latest` tracks its main branch.
- **verify** — `pomerium/verify:latest`, the demo upstream displaying forwarded identity.
- **Postgres** — Railway's managed PostgreSQL. Pomerium creates a `pomerium` schema; 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.


## Similar templates

- [Keycloak](https://railway.com/deploy/mSwigX) — Keycloak template with keywind theme + apple and discord providers
- [lua-protector](https://railway.com/deploy/lua-protector) — Test deployed my project first
- [bknd](https://railway.com/deploy/p4nTYL) — Feature-rich yet lightweight backend

Open this page in a browser: https://railway.com/deploy/pomerium
