# Pomerium: an identity-aware access proxy for internal apps, without a VPN

> Pomerium is an Apache-2.0 Go reverse proxy that puts an identity provider in front of every request instead of a network tunnel. It fits teams replacing a corporate VPN for web apps, and it is a poor fit if you need raw TCP access or want a proxy you configure once and forget.

**pomerium/pomerium** — Pomerium is an identity and context-aware access proxy.

- Repository: https://github.com/pomerium/pomerium
- Website: https://www.pomerium.com
- Stars: 5,023 · Forks: 358
- Language: Go
- License: Apache-2.0
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/pomerium-pomerium

## The problem Pomerium solves: network access where identity access belongs

A VPN answers one question: is this device on the network? Once the answer is yes, the device can reach everything the network exposes, and every internal service has to defend itself. Pomerium inverts that. The README describes it as an identity and context-aware reverse proxy that builds "secure, clientless connections to internal web apps and other services without a corporate VPN." The unit of access becomes the individual HTTP request, and the decision is made per request by a policy that names a user or a group rather than an IP range.

The audience is specific. Platform and infrastructure teams at companies with more internal web apps than they can individually secure, who already run an OIDC provider, and who are tired of issuing device certificates and VPN clients to contractors. If your internal estate is mostly SSH and database ports, Pomerium is the wrong shape of tool: it is an HTTP reverse proxy, and the repository's own examples directory reflects that, with subdirectories for docker, helm, kubernetes, cloudrun and mutual-tls rather than for generic TCP forwarding.

## How the request flow actually works: authenticate, authorize, proxy

The top-level repository layout tells you most of the architecture before you read a line of code. Three directories carry the work: authenticate/, authorize/, and proxy/, with databroker/ holding shared state and config/ holding the configuration model. The cmd/ directory holds the binary entry points, and pomerium.go sits at the root as the composition point that wires the services together.

The flow is a standard three-step identity proxy. A browser hits a protected route. The proxy service sees no valid session and redirects the user to the authenticate service, which performs the OIDC dance against your identity provider. Once a session exists, the authorize service evaluates the route's policy against the user's identity and whatever context the policy references, and the proxy either forwards the request upstream or refuses it. The databroker keeps session and policy state so that multiple Pomerium instances agree on what they know, which is why it is a separate top-level package rather than a library inside the proxy.

The dependency list in go.mod confirms the shape rather than contradicting it. github.com/coreos/go-oidc/v3 is the OIDC client, github.com/caddyserver/certmagic handles certificate issuance, and the storage backends are real: AWS S3 via github.com/aws/aws-sdk-go-v2/service/s3, Azure Blob via the azure-sdk-for-go storage module, and a local embedded store via github.com/cockroachdb/pebble/v2. There is also an Envoy reference in the Makefile, ENVOY_OCI_REPO defaulting to ghcr.io/pomerium/envoy-custom, which tells you the data plane is not hand-rolled Go HTTP forwarding.

One design consequence is worth stating plainly. Because the decision is per request and the databroker is shared state, Pomerium's availability is coupled to its storage backend. A single-node deployment with the embedded Pebble store is simple, but it is also a single point of failure for session and policy data. The repository offers the object-store backends precisely because that coupling is real.

## Installing Pomerium and getting one route behind login

The README does not carry install steps; it points at the documentation site at pomerium.com/docs and at a hosted control plane called Pomerium Zero. What the repository itself gives you is a Dockerfile and an examples tree, and those are enough to reason about a first run.

The image is built in three stages. A Node stage runs make npm-install and make build-ui to produce ui/dist. A Go stage on golang:1.27.1-bookworm runs make build-go NAME=pomerium. The runtime stage is gcr.io/distroless/base-nossl-debian12:debug, which copies the built binaries into /bin/, places an empty /pomerium/config.yaml, and starts the binary with a config flag.

The entrypoint and default command are the important part for a first run, because they tell you exactly what the container expects:

```dockerfile
ENV AUTOCERT_DIR=/data/autocert
WORKDIR /pomerium
COPY --from=build /go/src/github.com/pomerium/pomerium/bin/* /bin/
COPY --from=build /config.yaml /pomerium/config.yaml
ENTRYPOINT [ "/bin/pomerium" ]
CMD ["--config","/pomerium/config.yaml"]
```

So a container starts with --config pointing at /pomerium/config.yaml, and the image ships that file empty. Mounting your own config over it is the intended path. The AUTOCERT_DIR environment variable points at /data/autocert, which is where certificate material is written, so that path needs a volume if you want certificates to survive a container restart.

To build the binary yourself rather than pull an image, the Makefile defines the target the Dockerfile uses:

```bash
make build-go NAME=pomerium
```

The Makefile sets VERSION from git describe --tags and stamps GitCommit, Version, BuildMeta and ProjectName into internal/version through -ldflags, so a locally built binary reports the commit it came from. If your working tree has modified tracked files, the build metadata is marked dirty. Note also that the Makefile disables the race detector when the host GOOS is darwin and exports POMERIUM_SOCKET_DIRECTORY to /tmp in that case, which is a hint that local macOS development uses a socket path that the default does not accommodate.

For the config file itself, the examples directory is the honest starting point: examples/config/, examples/docker/, examples/kubernetes/ and examples/helm/ are all present in the repository. Copy the closest one and change the identity provider settings and the route you want to protect. The README does not document rollback of a bad config, and the repository does not ship a config validation subcommand in what is visible here, so treat a config change as something to stage in a non-production deployment first.

## Where Pomerium is the wrong tool

The most common mismatch is protocol. Pomerium is a reverse proxy for HTTP services. If the thing behind the wall is a Postgres port, an SSH daemon, or a custom binary protocol, an HTTP-aware identity proxy is not the layer that will help you, and bolting an HTTP wrapper in front of it usually breaks the client. The topics list on the repository includes vpn, but the README's own framing is that Pomerium is not a VPN alternative in the sense of tunnelling arbitrary traffic; it is a replacement for the access pattern, not the transport.

The second mismatch is operational appetite. Pomerium is not a set-and-forget nginx config. It has a configuration model in config/, a shared-state service in databroker/, separate authenticate, authorize and proxy services, and a UI built from a separate npm toolchain. Releases are frequent: v0.33.0, v0.33.1 and v0.33.3 all landed within roughly two months. A team without anyone who owns upgrades will fall behind and then face a jump.

The third is the hosted option. The README advertises Pomerium Zero for a hosted control plane and management GUI. That is a legitimate path, but it means the self-hosted story and the managed story are different products with different operational burdens, and choosing the managed one moves your access control plane to someone else's infrastructure. That is a decision about trust, not about features.

## Pomerium compared with oauth2-proxy and Traefik

The nearest comparison in the search data is oauth2-proxy, and the difference is architectural rather than cosmetic. oauth2-proxy is a single Go binary that sits in front of a backend and validates an OIDC session, typically as an auth_request target behind nginx or as a forward-auth provider. It has no separate authorize service and no shared databroker; policy lives in the proxy configuration. Pomerium splits authentication, authorization and proxying into distinct services with shared state, which is heavier to run but lets authorization policy reference context beyond the token, and lets multiple instances share session state through an external store.

Against Traefik the split is different again. Traefik is a general-purpose edge router whose job is routing and service discovery, with middleware for authentication. Pomerium's job is the authentication and authorization decision, with routing as the delivery mechanism. If you already run Traefik and only need to gate a handful of routes on an OIDC login, adding an auth middleware is a smaller change than standing up Pomerium's service set. If you need per-route policy tied to identity and shared across replicas, Pomerium is doing work that Traefik middleware is not designed to do.

The honest summary: Pomerium is more machinery than oauth2-proxy and more opinionated than Traefik. That machinery is the product.

## Maintenance, licence and upgrade cost

The repository is not archived, and the last push was on 2026-09-22, one day before this writing, with v0.33.3 released on 2026-09-09. That is a fast-moving project by any measure, and it sets the maintenance expectation: you are tracking a moving target, not a frozen artifact.

Upgrade cost has a specific shape here. The Makefile carries a setup-merge-driver target that configures a git merge driver for internal/version/components.json, described in the target as picking the highest semver, and a check-component-versions target alongside it. That exists because the project tracks component versions in a JSON file that changes often, and the maintainers found plain merges painful enough to automate. If you vendor or fork Pomerium, that merge driver is not optional infrastructure; without it, component version conflicts land in your lap.

On licensing, the LICENSE file is Apache-2.0, which is a permissive licence with an explicit patent grant and a requirement to preserve notices. That is a summary of the identifier in the repository, not legal advice, and the terms that matter to you depend on how you redistribute the binary. Note that the README points to a hosted offering, Pomerium Zero, which is a separate commercial product; the Apache-2.0 licence covers the code in this repository, not the hosted service.

## Conclusion

Adopt Pomerium if your internal access problem is web-shaped and you already run an OIDC identity provider you trust, because the proxy model lets you delete per-app auth code instead of adding more of it. Do not adopt it if your users need raw TCP, SSH or database sessions, or if you want a proxy that a single engineer can configure once and leave alone, since Pomerium's config surface is large and its releases are frequent. Before committing, verify three things against your own environment: that your IdP issues the claims you plan to write policy against, that the databroker storage backend you choose survives a restart, and that every route you intend to protect terminates HTTP rather than a bare TCP stream.

## FAQ

### How do I install Pomerium?

The README does not list install steps and points to the documentation site at pomerium.com/docs. The repository itself provides a Dockerfile whose runtime stage starts /bin/pomerium with --config /pomerium/config.yaml, plus a Makefile target, make build-go NAME=pomerium, for building the binary from source.

### What is Pomerium?

Pomerium is an identity and context-aware reverse proxy, written in Go, that builds clientless connections to internal web apps and other services without a corporate VPN. It authenticates users against an identity provider and authorizes each request against a policy before forwarding it.

### Is Pomerium open source?

Yes. The repository is licensed under Apache-2.0 and the source is public on GitHub. The README separately advertises Pomerium Zero, a hosted control plane and management GUI, which is a distinct offering from the Apache-2.0 code in this repository.

### Is Pomerium free?

The code in this repository is Apache-2.0 licensed, so self-hosting it carries no licence fee. The README promotes Pomerium Zero for a hosted control plane and management GUI, and the repository does not state pricing for that service.

### How does Pomerium differ from oauth2-proxy?

Pomerium splits authentication, authorization and proxying into separate services in the authenticate/, authorize/ and proxy/ directories, with shared state in databroker/. oauth2-proxy is a single binary that validates an OIDC session in front of a backend, so Pomerium carries more moving parts in exchange for policy that can draw on shared context.

### How does Pomerium compare with Traefik?

Traefik is a general-purpose edge router for routing and service discovery, with middleware for authentication. Pomerium's job is the authentication and authorization decision, with routing as the delivery mechanism, so gating a few routes on an OIDC login is a smaller change with Traefik middleware than standing up Pomerium's service set.

## Sources

- [License: Apache-2.0](https://github.com/pomerium/pomerium/blob/main/LICENSE)
- [pomerium/pomerium on GitHub](https://github.com/pomerium/pomerium)
- [Project website](https://www.pomerium.com)
- [README](https://github.com/pomerium/pomerium/blob/main/README.md)
- [Releases](https://github.com/pomerium/pomerium/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pomerium-pomerium
