Pomerium
Overview
Pomerium is the open source identity-aware access proxy. It is a single Go binary that runs 24/7 under systemd, terminates its own TLS, and gates access to your internal applications. It authenticates every request against your own identity provider (any OIDC provider) and enforces per-route authorization policy, so you grant access by identity and context instead of by network location, the foundation of a zero trust posture.
This SecureAnyCloud Azure Marketplace image runs Pomerium on Ubuntu 24.04. It installs the latest stable Pomerium Core release, resolved at build time from the official Pomerium apt repository and baked reproducibly into the image. The exact version is recorded in /opt/pomerium/VERSION. Pomerium runs under systemd as the unprivileged pomerium system user.
What you get:
- The latest stable Pomerium Core release (single Go binary), run under systemd as the unprivileged
pomeriumsystem user, which is granted theCAP_NET_BIND_SERVICEcapability so that it can bind port 443 without running as root - Pomerium terminating TLS on :443 itself, with a per-instance self-signed certificate regenerated on first boot
- A tiny nginx on :80 that serves an unauthenticated
/healthzload balancer probe and a301redirect to HTTPS, plus a local demo upstream on127.0.0.1:8000 - A working public demo route (proxied with no authentication) and a gated demo route (an unauthenticated request is redirected to sign in), so you can see the proxy working the moment it boots
- Per-instance shared secret and cookie secret generated on first boot into
/etc/pomerium/config.yaml(owned by thepomeriumuser, not world-readable), rendered from a template so that the shipped image carries no secret - An example OIDC identity provider block that you replace with your own provider, and a demo authorization policy that you replace with your own rules
- An Ubuntu 24.04 LTS base with the latest security patches applied at build time
- The Azure Linux Agent for seamless cloud integration and SSH key injection
- 24/7 SecureAnyCloud support with a guaranteed 24 hour response SLA
HTTPS is built in. Pomerium is a TLS proxy and terminates its own TLS on port 443. On the very first boot, this image generates a fresh per-instance self-signed certificate that covers the proxy domains plus the VM public IP. It also generates the two secrets Pomerium needs, the shared secret and the cookie secret (each a 32-byte key), directly into the configuration.
Those secrets are unique to this VM and come from the kernel random source on first boot. No shared secret and no default login ever ships inside the image.
You bring your own identity provider. Pomerium authenticates users against your OIDC provider (Google, Microsoft Entra ID, Okta, Auth0, Authelia, Keycloak and others). The image ships with an example identity provider block that you replace with your own values after launch. Pomerium discovers your provider lazily, so the service starts and serves public routes immediately. It contacts your provider only when a user signs in.
Prerequisites
- An active Azure subscription, an SSH public key, and a VNet with a subnet in the target region
- A subscription to the Pomerium listing on Azure Marketplace
- An OIDC identity provider you control (for production sign-in), and a DNS domain you can point at the VM
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is ample for a proxy that fronts a handful of applications. For heavier traffic, use Standard_D2s_v5 or larger.
Pomerium is stateless in this all-in-one configuration, because its data broker is in memory. It scales with CPU and memory.
Deploy from the Azure Portal
Search the Azure Marketplace for Pomerium, select the SecureAnyCloud publisher, and click Create.
Configure the network security group (NSG) rules to allow these ports from the networks your users connect from: TCP 22 (admin), TCP 443 (the proxy over HTTPS) and TCP 80 (HTTP to HTTPS redirect and health probe).
Deploy from the Azure CLI
RG="pomerium-prod"; LOCATION="eastus"; VM_NAME="pomerium-01" IMAGE_URN="sac1647283583153:pomerium-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create -g "$RG" --name pomerium-vnet --address-prefix 10.100.0.0/16 --subnet-name pomerium-subnet --subnet-prefix 10.100.1.0/24 az network nsg create -g "$RG" --name pomerium-nsg az network nsg rule create -g "$RG" --nsg-name pomerium-nsg --name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name pomerium-nsg --name allow-https --priority 110 \
--destination-port-ranges 443 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name pomerium-nsg --name allow-http --priority 120 \
--destination-port-ranges 80 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:pomerium-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username azureuser --ssh-key-values "$SSH_KEY" \
--vnet-name pomerium-vnet --subnet pomerium-subnet --nsg pomerium-nsg --public-ip-sku StandardVerify the Pomerium and nginx services
Connect over SSH, then confirm that both services are active and check which ports are bound. Pomerium listens on :443 (its own TLS). nginx listens on :80 (redirect and health probe) and on 127.0.0.1:8000 (the loopback demo upstream):
sudo systemctl status pomerium nginx --no-pager | head -14 sudo ss -tlnp | grep -E ':443|:80 |:8000' cat /opt/pomerium/VERSIONUnderstand the per-instance secrets and configuration
Pomerium needs two secrets, a shared secret and a cookie secret, each a 32-byte key. This image generates both on first boot into /etc/pomerium/config.yaml, which is owned by the pomerium user and is not world-readable. The shipped image carries only a template, so no two VMs ever share a secret.
The endpoints for this VM are summarised in a root-only file. Inspect it, the configuration and the certificate:
sudo cat /root/pomerium-credentials.txt
sudo grep -E '^address|^authenticate_service_url|^autocert|shared_secret|cookie_secret|^ - from|^ to' /etc/pomerium/config.yaml
sudo openssl x509 -in /etc/pomerium/certs/cert.pem -noout -subject -ext subjectAltNameThe credentials file lists the proxy URL, the authenticate URL and the example base domain.
In the configuration you can see address: ":443", autocert: false (Pomerium serves the self-signed certificate rather than requesting one from Let's Encrypt), the public and secure demo routes, and the two per-instance secrets. The certificate Subject Alternative Names cover the proxy domains and this VM.
See the proxy and the access gate over TLS
Pomerium is domain-based and routes by hostname. The image ships example domains under pomerium.example.com. To reach them before you set up DNS, map the three hostnames to the VM public IP in your client's hosts file, then trust the self-signed certificate.
On the VM itself, you can demonstrate the same behavior with curl using --resolve:
# /ping is served without a host match (Envoy level health), so it works on the raw IP:
curl -sk -o /dev/null -w 'ping: HTTP %{http_code}\n' https://127.0.0.1/ping
# The PUBLIC demo route is proxied with no authentication -> HTTP 200:
curl -sk -o /dev/null -w 'public route: HTTP %{http_code}\n' \
--resolve public.pomerium.example.com:443:127.0.0.1 https://public.pomerium.example.com/
# The SECURE demo route is identity gated -> unauthenticated requests are redirected to sign in:
curl -sk -o /dev/null -w 'secure route: HTTP %{http_code} -> %{redirect_url}\n' \
--resolve secure.pomerium.example.com:443:127.0.0.1 https://secure.pomerium.example.com/
# Pomerium publishes an unauthenticated JWKS document upstreams use to verify signed identity:
curl -sk -o /dev/null -w 'jwks: HTTP %{http_code}\n' \
--resolve authenticate.pomerium.example.com:443:127.0.0.1 \
https://authenticate.pomerium.example.com/.well-known/pomerium/jwks.jsonThe health check returns HTTP 200. The public route returns HTTP 200, because Pomerium proxied the demo upstream with no sign-in. The secure route returns HTTP 302 with a redirect to the authenticate service sign-in page, which is the identity-aware gate in action. There is no anonymous access to a gated route.
Reach the proxy from your workstation
To browse the demo routes from your own machine before you set up DNS, map the example hostnames to the VM public IP and trust the certificate. Copy the certificate from the VM, then add a hosts entry. Replace <public-ip> with your VM public IP:
scp azureuser@<public-ip>:/etc/pomerium/certs/cert.pem pomerium.crt
# Linux/macOS: append to /etc/hosts (needs sudo):
echo "<public-ip> authenticate.pomerium.example.com public.pomerium.example.com secure.pomerium.example.com" | sudo tee -a /etc/hosts curl --cacert pomerium.crt https://public.pomerium.example.com/
# 200, the demo upstream curl --cacert pomerium.crt -I https://secure.pomerium.example.com/
# 302 to sign inWire up your identity provider
To sign users in, replace the example OIDC block with your own provider. Create an OIDC application at your provider and set the redirect URL to https://authenticate.<your-domain>/oauth2/callback. Then look at the current provider settings:
sudo grep -nE 'idp_provider|idp_provider_url|idp_client_id|idp_client_secret' /etc/pomerium/config.yamlEdit the configuration (for example with sudo nano /etc/pomerium/config.yaml) and set the four idp_ values to your provider's. Then apply the change:
sudo systemctl restart pomeriumSet idp_provider to your provider type (for example google, azure, okta, auth0, or oidc for a generic provider). Set idp_provider_url to your provider's issuer URL, and idp_client_id / idp_client_secret to the values from the OIDC application you created.
After the restart, a request to the secure route redirects the user to your provider. After sign-in, Pomerium enforces the route policy.
Add your own applications and policy
The two demo routes show the pattern. To protect your own application, add a route that points at your upstream, together with an authorization policy:
routes: - from: https://app.your-domain.com to: http://127.0.0.1:9000
# your upstream application policy: - allow: and: - domain: is: your-company.com
# any signed in user from your domainPomerium passes a signed identity header to your upstream, so the application knows who the user is. Policies can match on email, group, domain, device and more. After editing, apply the change with sudo systemctl restart pomerium.
Use your own domain and a trusted certificate (production)
The self-signed certificate and the pomerium.example.com example domains are a convenience for getting started. For production, make two changes.
Use your own domain. Replace pomerium.example.com throughout /etc/pomerium/config.yaml with a domain you control. Point its DNS (a wildcard record is convenient) at the VM public IP.
Provide a trusted certificate. Either drop a CA-signed certificate and key into /etc/pomerium/certs/ and keep the certificates: block, or enable Pomerium's built-in autocert to obtain a certificate from Let's Encrypt automatically. To enable autocert, set autocert: true and autocert_dir: /etc/pomerium/. The VM must be reachable on :443 from the internet for the ACME challenge. Then run sudo systemctl restart pomerium.
Users then reach your routes at your own domain with no certificate trust step.
Server components
- Proxy: Pomerium Core (latest stable release, baked reproducibly, see /opt/pomerium/VERSION)
- TLS: terminated by Pomerium on :443 (self-signed per VM, autocert off), certificate at /etc/pomerium/certs/
- Front helper: nginx (:80 -> 301 https, unauthenticated /healthz, loopback 127.0.0.1:8000 demo upstream)
- Secrets: per-instance shared_secret and cookie_secret (32-byte), generated on first boot into /etc/pomerium/config.yaml
- Identity: your OIDC provider (an example block is shipped, and you replace it)
- Data broker: in memory (stateless, all-in-one)
- Service user: pomerium (unprivileged, granted CAP_NET_BIND_SERVICE to bind :443)
- Operating system: Ubuntu 24.04 LTS (patched at build)
- License: Apache 2.0 (Pomerium Core)
Managing the Pomerium service
sudo systemctl status pomerium --no-pager | head -12 sudo systemctl is-active pomerium nginxApply a configuration change with sudo systemctl restart pomerium. Reload the nginx helper with sudo systemctl reload nginx. Follow the logs live with sudo journalctl -u pomerium -f.
The configuration lives at /etc/pomerium/config.yaml. After any change, restart Pomerium. The service uses a systemd watchdog, so a hung process is restarted automatically.
Security recommendations
- Restrict the NSG. Allow TCP 443 (and 22 for admin) only from the networks your users connect from.
- Use a trusted certificate and your own domain (Step 9), so that users validate the proxy without a manual trust step and OIDC redirects work cleanly.
- Keep the demo routes out of production. Remove the
public.pomerium.example.comandsecure.pomerium.example.comdemo routes once you have added your own, so that nothing is exposed unintentionally. - Scope your policies tightly. Prefer group and email-based rules over broad domain rules, and layer device and context conditions where your provider supports them.
- Protect the secrets.
/etc/pomerium/config.yamlholds the shared and cookie secrets and is not world-readable. Keep it that way, and rotate the secrets if a VM is ever exposed. - Keep the OS patched. Unattended security upgrades remain enabled on the running VM.
Support and Licensing
Pomerium Core is distributed under the Apache License 2.0. This SecureAnyCloud image bundles the unmodified official open source Core release. The separate commercial Pomerium Enterprise Console is not included.
SecureAnyCloud provides the packaging, hardening, per-instance secret and TLS automation, the working demo routes, and 24/7 support with a guaranteed 24 hour response SLA. Pomerium is an independent open source project, and this image is not affiliated with or endorsed by Pomerium, Inc.
Deploy on Azure
Find Pomerium on Ubuntu 24.04 on the Azure Marketplace, published by SecureAnyCloud. Deploy from the Portal or the Azure CLI as shown above.
Need Help?
Email sales@secureanycloud.com for deployment help, configuration questions, or licensing enquiries.
Deploy on Azure
Launch Pomerium on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com