Course overview
Pomerium is an open-source, identity-aware proxy that helps organizations securely provide access to internal applications and services. It combines identity verification, access policies, and reverse-proxy capabilities to control who can reach protected resources.
This tutorial teaches the underlying technology first, then Pomerium, then hands-on implementation.
Beginner → Advanced
28 Chapters
Practical labs
Primary technology
Zero Trust Network Access (ZTNA), Identity-Aware Proxy (IAP), Access Management
Learning environment
Windows, Ubuntu, macOS, Docker, and applicable Linux distributions
Learning approach
Concepts, diagrams, experiments, configurations, labs, troubleshooting, and assessments
Final outcome
Design, deploy, secure, operate, and troubleshoot Pomerium independently
Research and version baseline
Official documentation and release information checked: October 28, 2026.
The latest stable Pomerium Core release found in the official GitHub releases was v0.33.4, published October 2, 2026. Its security updates include an Envoy upgrade and a fix addressing potential denial-of-service or performance issues with many distinct clients. Pomerium Core v0.33.0 also removed native macOS Intel (darwin/amd64) binary support. GitHub+1
The course primarily uses:
- Pomerium Core for self-managed, open-source experiments.
- Docker Compose for a consistent local environment.
- Pomerium’s hosted authentication service for the first experiments, avoiding the need to configure your own identity provider immediately.
- Realistic access policies and private demo applications.
- Enterprise and cloud-managed deployment models when discussing larger environments.
The local examples are educational rather than production-ready defaults. Production changes are introduced separately and explicitly.
Part I — Understand the World Before the Tool
Chapter 1. The Big Picture
1.1 Imagine a company with many internal applications
Suppose a company has 500 employees using several internal systems:
| Application | Purpose | Who should access it? |
|---|---|---|
| Grafana | Monitoring dashboards | DevOps and SRE teams |
| Jenkins | Continuous Integration/Continuous Delivery | Authorized developers |
| Internal Admin Portal | Company administration | Administrators |
| Kubernetes Dashboard | Cluster management | Platform engineers |
| Internal Wiki | Documentation | Employees |
| Database Management UI | Database administration | Database administrators |
These applications might run on company servers, Amazon Web Services (AWS), Microsoft Azure, Google Cloud, or Kubernetes clusters.
The company must answer some important questions:
- How can employees access these applications securely from home, an office, or while traveling?
- How does the company know who is connecting?
- How can it allow developers to access Jenkins but not the finance administration portal?
- How can it remove access when an employee leaves?
- How can it establish a central record of application access?
- Can it avoid giving employees access to an entire private network?
These are identity, access management, and application security problems.
1.2 The traditional approach: Virtual Private Networks
Historically, many companies used a Virtual Private Network (VPN).
A VPN creates an encrypted connection between a user’s device and a private network.
Imagine the company’s private network as an office building.
A VPN is somewhat like giving an employee a special tunnel into the building. Once inside, they may be able to reach multiple rooms, depending on how the network and access controls are configured.
Traditional remote-access concept
EmployeeRemote laptop
VPNNetwork tunnel
Private networkInternal resources
A VPN provides network connectivity. Additional controls are still necessary to restrict which resources the user can reach.
VPNs are useful, but several operational challenges can arise:
- Users typically need VPN client software.
- Administrators must manage VPN connectivity and client configurations.
- Network access may be broader than necessary.
- Applications may each have separate login systems.
- Security teams need additional mechanisms to manage fine-grained application permissions.
- A compromised device may gain access to network services that were not intended for that user.
A properly configured VPN can implement strong authentication, segmentation, and fine-grained controls. The limitation is not that VPNs are inherently insecure; it is that network connectivity and application authorization are different problems.
1.3 The modern approach: Zero Trust
Zero Trust is a security approach in which access is not automatically trusted just because a user or device is connected to a particular network.
Instead, access decisions consider identity, permissions, and other relevant conditions.
For example, a user wants to open an internal dashboard.
The system can ask:
- Who is the user?
- Has the user authenticated?
- Is the user allowed to access this application?
- Is the session still valid?
- Do any additional access restrictions apply?
The National Institute of Standards and Technology (NIST) describes these principles in Special Publication 800-207, Zero Trust Architecture. CSRC
1.4 The office security analogy
Compare two ways of securing an office building.
Traditional perimeter security: A guard checks someone at the front entrance. Inside, additional permissions depend on locks and other controls.
Zero Trust-oriented application security: Every sensitive room has its own access check. A person allowed into the engineering room is not automatically allowed into the finance room.
Pomerium acts like a central access checkpoint for the applications it protects.
The actual technology is more precise than the analogy: Pomerium validates user sessions, evaluates configured policies, and forwards authorized requests to the correct service.
1.5 Where Pomerium fits
#chatgpt-mermaid-_r_1e3_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_1e3_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1e3_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1e3_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_1e3_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1e3_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_1e3_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_1e3_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_1e3_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_1e3_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_1e3_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_1e3_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1e3_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1e3_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_1e3_ p{margin:0;}#chatgpt-mermaid-_r_1e3_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1e3_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1e3_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1e3_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_1e3_ .label text,#chatgpt-mermaid-_r_1e3_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1e3_ .node rect,#chatgpt-mermaid-_r_1e3_ .node circle,#chatgpt-mermaid-_r_1e3_ .node ellipse,#chatgpt-mermaid-_r_1e3_ .node polygon,#chatgpt-mermaid-_r_1e3_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_1e3_ .rough-node .label text,#chatgpt-mermaid-_r_1e3_ .node .label text,#chatgpt-mermaid-_r_1e3_ .image-shape .label,#chatgpt-mermaid-_r_1e3_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_1e3_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_1e3_ .rough-node .label,#chatgpt-mermaid-_r_1e3_ .node .label,#chatgpt-mermaid-_r_1e3_ .image-shape .label,#chatgpt-mermaid-_r_1e3_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_1e3_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_1e3_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1e3_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1e3_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_1e3_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_1e3_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1e3_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1e3_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1e3_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_1e3_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1e3_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1e3_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1e3_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_1e3_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1e3_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_1e3_ .icon-shape,#chatgpt-mermaid-_r_1e3_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1e3_ .icon-shape p,#chatgpt-mermaid-_r_1e3_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_1e3_ .icon-shape .label rect,#chatgpt-mermaid-_r_1e3_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1e3_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_1e3_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_1e3_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_1e3_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_1e3_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_1e3_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_1e3_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1e3_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_1e3_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_1e3_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_1e3_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1e3_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_1e3_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_1e3_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1e3_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_1e3_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_1e3_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1e3_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_1e3_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1e3_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1e3_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1e3_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_1e3_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_1e3_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_1e3_ .node rect,#chatgpt-mermaid-_r_1e3_ .node circle,#chatgpt-mermaid-_r_1e3_ .node ellipse,#chatgpt-mermaid-_r_1e3_ .node polygon,#chatgpt-mermaid-_r_1e3_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1e3_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_1e3_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_1e3_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_1e3_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1e3_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Employee opens an internalweb applicationPomerium receives therequestIdentity is verifiedIs access permitted?Application receives therequestAccess deniedYesNo
What happens here?
The employee requests an application through a Pomerium-protected address. Pomerium establishes or validates the user’s identity and evaluates an authorization policy.
Only an authorized request is forwarded to the protected application.
This allows access decisions to focus on the individual application rather than granting general network connectivity.
1.6 Who uses Pomerium?
Pomerium is particularly relevant to platform engineering, DevOps, Site Reliability Engineering (SRE), cybersecurity, identity and access management, and internal application development teams.
It is useful for protecting web-based tools, internal administrative interfaces, developer services, monitoring tools, and other supported services.
What You Should Understand Before Moving On
Pomerium addresses application access rather than simply providing network connectivity.
The central idea is:
Verify identity, evaluate permission, and grant access only to the requested resource.
Chapter 2. Prerequisite Concepts
Before learning Pomerium itself, understand the concepts that make its behavior possible.
2.1 Authentication versus authorization
These two concepts are fundamental.
Authentication answers:
Who are you?
Examples include signing in with a password, using a security key, or completing multifactor authentication.
Authorization answers:
What are you allowed to access or do?
An authenticated employee might be allowed to read internal documentation but forbidden from accessing production administration tools.
| Concept | Simple definition | Analogy | Practical example | Importance |
|---|---|---|---|---|
| Authentication | Proving identity | Showing an ID card | Signing in with Google | Essential |
| Authorization | Checking permission | Checking room access | Allowing only administrators into Grafana | Essential |
| Identity Provider | System that authenticates users | ID issuing office | Microsoft Entra ID, Okta | Essential |
| Single Sign-On | Reusing one login across applications | One identity badge | Corporate login for several tools | Essential |
| Reverse Proxy | Server that receives requests and forwards them to other servers | Reception desk | Pomerium forwarding traffic to Grafana | Essential |
| Zero Trust | Avoiding implicit trust based only on network position | Check each protected doorway | Verify access to every protected application | Essential |
| Access policy | Rules determining access | Entry requirements | Allow selected email addresses | Essential |
| TLS | Encryption and server authentication for network connections | Protected communication channel | HTTPS | Essential |
| DNS | Resolves names to network addresses | Address directory | Finding an application’s server IP | Essential |
| Container | Packaged application running in an isolated environment | Separate application workspace | Docker container | Helpful |
| Kubernetes | Platform for running and managing containerized applications | Automated operations coordinator | Managing Pomerium replicas | Advanced |
2.2 How a website request works
When someone types an address such as https://dashboard.example.com, several things happen.
First, the browser resolves the hostname to an Internet Protocol (IP) address, normally through the Domain Name System (DNS).
Next, the browser establishes a network connection.
For HTTPS, the browser also establishes a Transport Layer Security (TLS) connection, verifies the server certificate, and sends an encrypted Hypertext Transfer Protocol (HTTP) request.
Finally, the server processes the request and returns an HTTP response.
Pomerium can sit between the browser and the actual application server.
2.3 Forward proxy versus reverse proxy
A forward proxy usually acts on behalf of clients that want to connect to other systems.
A reverse proxy acts as the entry point to one or more backend services.
Pomerium is primarily a reverse proxy, with additional identity and policy enforcement functionality.
For example:
Browser
|
| HTTPS request
v
Pomerium
|
| Authorized upstream request
v
Grafana
The word upstream refers to the service behind Pomerium. Downstream refers to the client side of the connection.
2.4 Identity providers and Single Sign-On
An Identity Provider (IdP) is a trusted system that authenticates users.
Examples include:
- Microsoft Entra ID
- Okta
- Google identity services
- Authentik
- Keycloak
A user can authenticate through the identity provider rather than managing an independent password inside every protected application.
Pomerium then uses identity information to evaluate access policies.
There is an important distinction: the identity provider establishes user identity, while Pomerium evaluates whether the identity satisfies the access policy for the requested service.
2.5 OAuth 2.0 and OpenID Connect
Two standards frequently appear in Pomerium documentation.
OAuth 2.0 is an authorization framework for delegated access. It enables applications to obtain access tokens with defined permissions.
OpenID Connect (OIDC) is an identity layer built on OAuth 2.0. It provides a standardized way for applications to authenticate users and obtain identity information.
For example:
- The browser attempts to access a protected service.
- The user is redirected to an identity provider.
- The user signs in.
- The identity provider returns the appropriate authentication result through the protocol.
- Pomerium establishes a session and evaluates access policy.
The learner does not need to implement OAuth 2.0 manually to complete the first Pomerium laboratory.
2.6 Cookies, sessions, and tokens
These concepts are related but different.
A cookie is a small piece of information stored by a browser and sent to a website according to cookie rules.
A session represents the ongoing authenticated relationship between a user and a system.
A token is information used to represent identity, authorization, or other claims.
One common token format is a JavaScript Object Notation Web Token, normally abbreviated as JSON Web Token (JWT).
JWTs can contain digitally signed claims, such as the issuer, intended audience, identity, or expiration.
A signed JWT is not automatically encrypted. Systems must validate its signature and important claims before trusting it.
Pomerium uses sessions and identity information as part of its access decision workflow.
2.7 Docker and Docker Compose
A Docker image is a packaged definition of application software and its environment.
A container is a running instance of an image.
Docker Compose lets you describe multiple cooperating containers in one configuration file.
For example, our laboratory will use separate containers for Pomerium and a demo application.
Docker Compose allows us to start both without manually installing a web server into the host operating system.
2.8 Essential versus optional prerequisites
For the first laboratory, you only need basic computer skills, a browser, a terminal, and the ability to install Docker.
Understanding DNS, HTTPS, authentication, and YAML configuration is essential conceptually, but the examples below will teach them as they are used.
Kubernetes, PostgreSQL, custom identity-provider administration, public certificates, and distributed deployment are advanced topics. They are not prerequisites for first success.
Chapter 3. Essential Terminology and Glossary
3.1 Beginner terminology
| Term | Full form | Simple meaning | Example | Why it matters |
|---|---|---|---|---|
| IAP | Identity-Aware Proxy | Proxy that uses identity to control access | Pomerium | Main tool category |
| ZTNA | Zero Trust Network Access | Identity- and policy-based access to resources | Restricted internal apps | Security design |
| IdP | Identity Provider | Service responsible for authenticating users | Okta | Establishes identity |
| SSO | Single Sign-On | Login reused across services | Corporate login | Simplifies access |
| MFA | Multifactor Authentication | Multiple ways of proving identity | Password plus passkey | Stronger authentication |
| HTTP | Hypertext Transfer Protocol | Web request protocol | GET request | Application communication |
| HTTPS | HTTP Secure | HTTP over TLS | Secure web application | Encrypted communication |
| TLS | Transport Layer Security | Secure network communication protocol | HTTPS connection | Protects data in transit |
| DNS | Domain Name System | Maps hostnames to addresses | app.example.com | Routing |
| URL | Uniform Resource Locator | Address identifying a resource | https://app.example.com | Route identification |
| Proxy | Intermediary server | Receives and forwards requests | Pomerium | Main traffic component |
| Upstream | Backend receiving proxied traffic | Protected application | Grafana | Routing destination |
| Route | Mapping from an external address to a backend | Application mapping | App URL → service | Essential configuration |
| Policy | Rule controlling permitted access | Allow selected users | Email restriction | Core security function |
| Session | Authenticated interaction state | Logged-in browser session | Login session | Tracks authentication |
| YAML | YAML Ain’t Markup Language | Human-readable configuration format | config.yaml | Configuration |
| CLI | Command-Line Interface | Text-based interaction with software | docker compose | Operations |
| Container | Isolated application environment | Running Docker image | Pomerium container | Local deployment |
3.2 Intermediate and advanced terminology
| Term | Full form | Simple meaning | Example | Why it matters |
|—|—|—|—|—|
| OIDC | OpenID Connect | Standard identity protocol | Corporate SSO | Authentication |
| OAuth 2.0 | OAuth authorization framework | Delegated authorization protocol | Token-based integration | Identity integrations |
| JWT | JSON Web Token | Structured claims in a token | Signed identity assertion | Identity validation |
| JWKS | JSON Web Key Set | Published cryptographic verification keys | JWT signature verification | Application trust |
| PPL | Pomerium Policy Language | Language for access policies | Email or group rule | Authorization |
| RBAC | Role-Based Access Control | Permissions based on roles | Admin versus viewer | Governance |
| ABAC | Attribute-Based Access Control | Permissions based on attributes | Team and device conditions | Fine-grained policies |
| gRPC | Google-originated Remote Procedure Call framework | Protocol for service communication | Proxy to Authorize | Internal architecture |
| Databroker | Pomerium state-management component | Stores sessions and related data | User session records | State management |
| Envoy | High-performance proxy software | Handles network proxy traffic | Pomerium’s proxy engine | Underlying architecture |
| mTLS | Mutual Transport Layer Security | Both peers authenticate with certificates | Secure service connections | Stronger transport trust |
| CA | Certificate Authority | Entity that issues trusted certificates | Internal CA | TLS certificate trust |
| HA | High Availability | Design that tolerates component failure | Multiple replicas | Production reliability |
| Observability | Understanding system behavior through telemetry | Logs and metrics | Debugging failed logins | Operations |
| Data plane | Components processing application traffic | Pomerium Core proxy services | Request processing |
| Control plane | System managing configuration and policy | Pomerium Zero | Configuration management |
3.3 Commonly confused terms
| These are different | Main distinction |
|---|---|
| Authentication vs authorization | Identity verification vs permission checking |
| VPN vs IAP | Network connectivity vs application-aware access gateway |
| Identity Provider vs Pomerium | Authenticates identities vs enforces application access |
| HTTP vs HTTPS | Web protocol vs its TLS-protected form |
| Session vs JWT | Authenticated state vs a token format |
| Proxy vs load balancer | Request interception and processing vs traffic distribution; functions can overlap |
| Pomerium Core vs Pomerium Zero | Self-managed data plane and configuration vs managed control plane |
| Container vs virtual machine | Application-level isolation vs virtualized operating-system environments |
A useful habit is to distinguish identity, permission, network connectivity, and application behavior when diagnosing an access problem. They are different layers.
Chapter 4. How the Technology Domain Works Without Pomerium
4.1 A company builds its own access architecture
Imagine a small engineering team running an internal dashboard.
Without Pomerium, the team might implement access control using the dashboard’s own authentication system, a VPN, an application gateway, or several individually managed security components.
A basic system could look like this:
#chatgpt-mermaid-_r_1ef_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_1ef_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1ef_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1ef_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_1ef_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ef_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_1ef_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_1ef_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_1ef_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_1ef_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_1ef_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_1ef_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1ef_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1ef_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_1ef_ p{margin:0;}#chatgpt-mermaid-_r_1ef_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ef_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ef_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ef_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_1ef_ .label text,#chatgpt-mermaid-_r_1ef_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ef_ .node rect,#chatgpt-mermaid-_r_1ef_ .node circle,#chatgpt-mermaid-_r_1ef_ .node ellipse,#chatgpt-mermaid-_r_1ef_ .node polygon,#chatgpt-mermaid-_r_1ef_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_1ef_ .rough-node .label text,#chatgpt-mermaid-_r_1ef_ .node .label text,#chatgpt-mermaid-_r_1ef_ .image-shape .label,#chatgpt-mermaid-_r_1ef_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_1ef_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_1ef_ .rough-node .label,#chatgpt-mermaid-_r_1ef_ .node .label,#chatgpt-mermaid-_r_1ef_ .image-shape .label,#chatgpt-mermaid-_r_1ef_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_1ef_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_1ef_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1ef_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1ef_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_1ef_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_1ef_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1ef_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1ef_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1ef_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_1ef_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1ef_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ef_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ef_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_1ef_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ef_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_1ef_ .icon-shape,#chatgpt-mermaid-_r_1ef_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1ef_ .icon-shape p,#chatgpt-mermaid-_r_1ef_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_1ef_ .icon-shape .label rect,#chatgpt-mermaid-_r_1ef_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1ef_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_1ef_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_1ef_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_1ef_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_1ef_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_1ef_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_1ef_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1ef_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_1ef_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_1ef_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_1ef_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1ef_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_1ef_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_1ef_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1ef_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_1ef_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_1ef_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1ef_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_1ef_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1ef_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1ef_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1ef_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_1ef_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_1ef_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_1ef_ .node rect,#chatgpt-mermaid-_r_1ef_ .node circle,#chatgpt-mermaid-_r_1ef_ .node ellipse,#chatgpt-mermaid-_r_1ef_ .node polygon,#chatgpt-mermaid-_r_1ef_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1ef_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_1ef_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_1ef_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_1ef_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1ef_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}EmployeeVPN or network gatewayInternal applicationIdentity providerApplication permissionsApplication data
The diagram represents a possible architecture, not a claim that every VPN deployment works exactly this way.
The employee first obtains connectivity to the internal network. The application then authenticates the employee and decides whether to allow the requested operation.
If the organization has ten applications, each may need its own identity integration, session handling, and authorization model.
This can become operationally complicated, especially when some applications lack modern authentication features.
4.2 How an application determines access
Suppose an employee wants to view a monitoring dashboard.
The process may involve:
- Resolving the dashboard hostname.
- Establishing network connectivity.
- Connecting to the application’s HTTPS endpoint.
- Providing login credentials or using SSO.
- Establishing an authenticated session.
- Evaluating application-specific permissions.
- Returning the requested dashboard data.
If any stage fails, the application cannot complete the request successfully.
4.3 What a centralized application access gateway changes
Now introduce the idea of a centralized gateway without focusing on a particular product.
#chatgpt-mermaid-_r_1em_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_1em_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1em_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1em_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_1em_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1em_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_1em_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_1em_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_1em_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_1em_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_1em_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_1em_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1em_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1em_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_1em_ p{margin:0;}#chatgpt-mermaid-_r_1em_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1em_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1em_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1em_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_1em_ .label text,#chatgpt-mermaid-_r_1em_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1em_ .node rect,#chatgpt-mermaid-_r_1em_ .node circle,#chatgpt-mermaid-_r_1em_ .node ellipse,#chatgpt-mermaid-_r_1em_ .node polygon,#chatgpt-mermaid-_r_1em_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_1em_ .rough-node .label text,#chatgpt-mermaid-_r_1em_ .node .label text,#chatgpt-mermaid-_r_1em_ .image-shape .label,#chatgpt-mermaid-_r_1em_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_1em_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_1em_ .rough-node .label,#chatgpt-mermaid-_r_1em_ .node .label,#chatgpt-mermaid-_r_1em_ .image-shape .label,#chatgpt-mermaid-_r_1em_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_1em_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_1em_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1em_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1em_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_1em_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_1em_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1em_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1em_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1em_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_1em_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1em_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1em_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1em_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_1em_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1em_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_1em_ .icon-shape,#chatgpt-mermaid-_r_1em_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1em_ .icon-shape p,#chatgpt-mermaid-_r_1em_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_1em_ .icon-shape .label rect,#chatgpt-mermaid-_r_1em_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1em_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_1em_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_1em_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_1em_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_1em_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_1em_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_1em_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1em_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_1em_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_1em_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_1em_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1em_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_1em_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_1em_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1em_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_1em_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_1em_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1em_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_1em_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1em_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1em_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1em_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_1em_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_1em_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_1em_ .node rect,#chatgpt-mermaid-_r_1em_ .node circle,#chatgpt-mermaid-_r_1em_ .node ellipse,#chatgpt-mermaid-_r_1em_ .node polygon,#chatgpt-mermaid-_r_1em_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1em_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_1em_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_1em_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_1em_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1em_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Employee browserIdentity-aware accessgatewayCorporate identity providerCentral access policyPrivate applicationRequests applicationAuthenticate identityIdentity resultEvaluate permissionAllow or denyOnly authorized requests
This architecture moves an important part of the access decision out of each application and into a shared gateway.
The application can remain on a private network, provided users cannot bypass the gateway to reach it directly.
However, the application may still need its own authorization controls. A gateway granting access to Grafana does not automatically determine which Grafana dashboards or administrative operations that user may perform.
4.4 Universal concepts versus product-specific implementation
The following ideas are not unique to Pomerium:
- Identity-based authentication
- Access policies
- HTTPS and TLS
- Reverse proxies
- User sessions
- Zero Trust principles
- Application-level access enforcement
Pomerium implements these ideas through its own architecture, configuration model, policy language, and integrations.
That distinction matters because understanding the domain lets you evaluate Pomerium intelligently rather than learning a collection of commands without knowing why they exist.
Part II — Understand Pomerium
Chapter 5. What Is Pomerium?
5.1 One-sentence definition
Pomerium is an identity-aware reverse proxy that authenticates users, evaluates access policies, and securely connects authorized clients to protected applications and services.
5.2 Beginner-friendly definition
Pomerium is a security gateway you place in front of applications.
Instead of letting everyone connect directly to your internal Grafana, Jenkins, or administration portal, you make Pomerium the controlled entry point.
Pomerium determines whether a user has a valid identity and permission to access the requested application.
When access is allowed, Pomerium forwards the request to the application.
5.3 Technical definition
Pomerium is an identity- and context-aware reverse proxy built around Zero Trust principles.
Its core architecture includes a proxy, authentication service, authorization service, and state-management service called the Databroker. The proxy layer uses Envoy, while OAuth 2.0 and OpenID Connect support identity integration. Pomerium+1
Pomerium can be run as one integrated instance or as multiple cooperating service components.
5.4 What category does Pomerium belong to?
Pomerium belongs to several overlapping technology categories.
| Category | Relationship to Pomerium |
|---|---|
| Identity-Aware Proxy | Primary product category |
| Zero Trust Network Access | Provides resource-specific access capabilities |
| Reverse Proxy | Forwards authorized traffic to backend services |
| Identity and Access Management | Integrates with IAM systems to make access decisions |
| Application Access Gateway | Centralizes access to protected applications |
| Security Policy Enforcement | Evaluates permissions before forwarding traffic |
| Cloud-native infrastructure | Deployable with containers and Kubernetes |
Pomerium is not itself a complete identity provider or a general replacement for all network security controls.
5.5 Pomerium editions and deployment models
One important distinction is that Pomerium Core, Pomerium Zero, and Pomerium Enterprise are not identical products with identical management capabilities.
| Offering | What it is | Management model | Main audience |
|---|---|---|---|
| Pomerium Core | Open-source proxy and services | Self-managed | Developers, DevOps, platform teams |
| Pomerium Zero | Hosted control plane managing Core clusters | Cloud-managed configuration; self-hosted proxy | Individuals and teams wanting easier management |
| Pomerium Enterprise | Self-hosted control plane with additional governance features | Organization-managed | Larger organizations with advanced operational requirements |
Pomerium Core is available under the Apache 2.0 open-source license. Zero and Enterprise add management and operational capabilities, some subject to commercial plans and licensing. GitHub+2
5.6 Where does it run?
Pomerium Core supports several deployment methods.
[macOS] 개발환경 구축 – Docker, Colima 설치
Docker containers
The easiest starting environment for beginners. Pomerium and the applications it protects run in separate containers.
Se simplifier Kubernetes avec Helm et les Charts
Kubernetes
Suitable for orchestrated environments, ingress management, automated deployments, and scaling.
Linux servers and virtual machines
Pomerium can also run using official packages or prebuilt binaries.
The installation chapters explain operating-system differences in detail.
5.7 What Pomerium does not do
Understanding limitations prevents architecture mistakes.
Pomerium does not automatically provide:
- A complete corporate user directory.
- A replacement for application-specific business permissions.
- An antivirus or endpoint detection system.
- A Web Application Firewall (WAF) covering every attack category.
- Database backup, disaster recovery, or application data protection.
- General-purpose network connectivity equivalent to every VPN use case.
- Protection against an application being accessed through a separate, unprotected network path.
Its job is to enforce an access boundary for the resources routed through it.
Chapter 6. Why Does Pomerium Exist?
6.1 The problem of inconsistent access controls
Consider an organization with 30 internal applications.
Some applications use local passwords. Others support corporate SSO. Some are accessible only through VPN. Others have been exposed through reverse proxies.
Administrators may need to update several systems whenever someone joins, changes teams, or leaves.
Pomerium provides a way to standardize an important layer of application access.
6.2 Three real-world scenarios
| Scenario | Without Pomerium | With Pomerium | Main benefit |
|---|---|---|---|
| Internal Grafana | Configure a VPN or separate authentication for dashboard access | Protect Grafana with an identity-based route and policy | Centralized application access |
| Developer tools | Manage different access approaches for Jenkins, documentation, and utilities | Configure protected routes with distinct permissions | Consistent enforcement |
| Contractor access | Provision broader connectivity or application-specific credentials | Grant access to specifically approved protected applications | Reduced unnecessary access |
These benefits are conditional on correct deployment, identity integration, and network isolation.
6.3 Why not use only a VPN?
VPNs are useful for private networking, especially where applications require general IP connectivity.
However, a VPN does not inherently provide the same application-level authorization model as Pomerium.
Conversely, Pomerium is not always the right tool for access to arbitrary network protocols or complete private networks.
Organizations can use both technologies together.
6.4 What trade-offs does Pomerium introduce?
Pomerium adds a security enforcement component to the request path.
That introduces responsibilities:
- Deploying and maintaining a reliable gateway.
- Managing TLS certificates and access policies.
- Ensuring identity-provider availability.
- Controlling session and state storage.
- Preventing direct access that bypasses the proxy.
- Monitoring gateway health and response latency.
An access gateway becomes part of the application’s availability architecture. If it is unavailable, legitimate users may also be unable to access the protected application.
6.5 When is Pomerium unnecessary?
You may not need Pomerium when an application already has strong corporate SSO, appropriate authorization, reliable audit capabilities, and an acceptable network security architecture.
It may also be excessive for a small local-only tool used by one person.
The decision should depend on actual security and operational requirements, not on the desire to add another infrastructure component.
Chapter 7. How Pomerium Works
7.1 The four logical components
Pomerium contains four main logical services.
| Component | Responsibility | Everyday analogy |
|---|---|---|
| Proxy | Accepts and forwards traffic | Reception desk |
| Authenticate | Establishes user identity and login session | Identity verification counter |
| Authorize | Checks whether the request is permitted | Access-control officer |
| Databroker | Manages state, sessions, and identity-related records | Secure records office |
These are logical services. They do not necessarily require four separately deployed servers.
In the beginner deployment, the Core components run together, while the first login experience uses Pomerium’s external hosted authentication service.
7.2 End-to-end request architecture
#chatgpt-mermaid-_r_1fm_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_1fm_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1fm_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1fm_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_1fm_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1fm_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_1fm_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_1fm_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_1fm_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_1fm_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_1fm_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_1fm_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1fm_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1fm_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_1fm_ p{margin:0;}#chatgpt-mermaid-_r_1fm_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1fm_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1fm_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1fm_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_1fm_ .label text,#chatgpt-mermaid-_r_1fm_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1fm_ .node rect,#chatgpt-mermaid-_r_1fm_ .node circle,#chatgpt-mermaid-_r_1fm_ .node ellipse,#chatgpt-mermaid-_r_1fm_ .node polygon,#chatgpt-mermaid-_r_1fm_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_1fm_ .rough-node .label text,#chatgpt-mermaid-_r_1fm_ .node .label text,#chatgpt-mermaid-_r_1fm_ .image-shape .label,#chatgpt-mermaid-_r_1fm_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_1fm_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_1fm_ .rough-node .label,#chatgpt-mermaid-_r_1fm_ .node .label,#chatgpt-mermaid-_r_1fm_ .image-shape .label,#chatgpt-mermaid-_r_1fm_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_1fm_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_1fm_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1fm_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1fm_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_1fm_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_1fm_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1fm_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1fm_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1fm_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_1fm_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1fm_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1fm_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1fm_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_1fm_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1fm_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_1fm_ .icon-shape,#chatgpt-mermaid-_r_1fm_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1fm_ .icon-shape p,#chatgpt-mermaid-_r_1fm_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_1fm_ .icon-shape .label rect,#chatgpt-mermaid-_r_1fm_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1fm_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_1fm_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_1fm_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_1fm_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_1fm_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_1fm_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_1fm_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1fm_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_1fm_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_1fm_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_1fm_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1fm_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_1fm_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_1fm_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1fm_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_1fm_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_1fm_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1fm_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_1fm_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1fm_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1fm_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1fm_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_1fm_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_1fm_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_1fm_ .node rect,#chatgpt-mermaid-_r_1fm_ .node circle,#chatgpt-mermaid-_r_1fm_ .node ellipse,#chatgpt-mermaid-_r_1fm_ .node polygon,#chatgpt-mermaid-_r_1fm_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1fm_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_1fm_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_1fm_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_1fm_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1fm_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}User browserPomerium ProxyAuthorization serviceAuthentication serviceIdentity providerDatabrokerProtected applicationHTTPS requestCheck accessIf login requiredOIDC authenticationSession establishedSession informationAccess decisionAuthorized trafficResponseHTTPS response
This diagram summarizes the logical interactions; the browser also participates in authentication redirects.
7.3 Step-by-step execution
Step 1 — The browser requests an application.
An employee opens a Pomerium-protected URL.
Step 2 — The proxy receives the request.
Pomerium identifies the matching route and initiates an authorization check.
Step 3 — Authentication is checked.
If the request lacks a valid session, the user is directed into the authentication process.
Step 4 — The identity provider authenticates the user.
This may involve a password, passkey, or another factor according to the identity-provider configuration.
Step 5 — Session information is established.
Pomerium manages the session using its authentication and Databroker components.
Step 6 — Authorization evaluates the request.
The service evaluates the policy using relevant authenticated identity and request context.
Step 7 — Pomerium either allows or denies access.
If the request is permitted, Pomerium forwards it to the upstream application.
Step 8 — The application responds.
The response returns through Pomerium to the browser.
Pomerium’s documented architecture uses gRPC communication among relevant internal components and evaluates HTTP authorization on a per-request basis. Pomerium+1
7.4 Authentication does not equal authorization
Suppose two employees successfully authenticate.
The first employee is a platform engineer. The second is a marketing analyst.
Both may have valid corporate identities.
However, a production administration route might permit only the first employee.
Authentication succeeds for both, but authorization produces different decisions.
That is the central distinction behind Pomerium’s access-control model.
7.5 Understanding state
Pomerium maintains data about users and sessions.
Its Databroker supports different storage approaches, including memory, local file storage, and PostgreSQL.
The choice affects session persistence and high availability.
We will explore those differences when designing production architecture.
Chapter 8. Core Features Explained Simply
8.1 Essential features
| Feature | What it does | Problem solved | Example | Level |
|---|---|---|---|---|
| Identity-aware reverse proxy | Accepts and controls application requests | Uncontrolled application entry | Protect Grafana | Beginner |
| Authentication integration | Connects user login to an identity provider | Repeated login systems | Corporate SSO | Beginner |
| Access policies | Controls who can access a route | Excessive access | Allow specific users | Beginner |
| Application routing | Maps external URLs to backend services | Complex access paths | Dashboard hostname to Grafana | Beginner |
| Session handling | Maintains authenticated interactions | Repeated authentication | Browser login session | Beginner |
| TLS support | Secures transport connections | Unencrypted communication | HTTPS | Beginner |
8.2 Everyday operational features
| Feature | Simple explanation | Practical example | Level |
|---|---|---|---|
| Configuration files | Define how Pomerium behaves | YAML configuration | Beginner |
| Environment variables | Supply configuration outside the file | Secrets injected at runtime | Intermediate |
| Logs | Show significant operational events | Diagnose login errors | Beginner |
| Health checks | Report whether services are ready and healthy | Container health probe | Intermediate |
| Metrics | Expose operational measurements | Prometheus monitoring | Intermediate |
| Hot configuration reload | Apply supported configuration changes without restarting the process | Update a route policy | Intermediate |
8.3 Integration features
| Feature | Practical purpose | Typical environment | Level |
|---|---|---|---|
| OIDC integration | Authenticate through existing identity providers | Corporate IAM | Intermediate |
| Signed identity assertions | Allow applications to verify identity information sent by Pomerium | Custom applications | Intermediate |
| Kubernetes integration | Protect services in Kubernetes environments | EKS or other Kubernetes clusters | Advanced |
| TCP connectivity clients | Access supported non-HTTP services through Pomerium | Developer services | Advanced |
| Terraform integrations | Manage supported configuration declaratively | Infrastructure as Code | Advanced |
8.4 Security features
| Feature | Problem addressed | Example |
|---|---|---|
| Per-route authorization | Users getting access to applications outside their role | Only SREs can access monitoring |
| Deny policies | Explicitly blocked identities or conditions | Deny a named user |
| Identity assertions | Applications need trustworthy identity information | Verify signed JWT |
| Session restrictions | Long-lived or inappropriate sessions | Configure session expiration |
| Certificate validation | Untrusted network connections | Validate upstream TLS |
| Mutual TLS | Need for certificate-based authentication | Client-to-gateway certificate authentication |
| Contextual policies | Decisions require additional conditions | Policy using available device or request attributes |
Contextual policy support depends on the availability of the relevant attributes, provider integrations, and product configuration. Installing Core alone does not automatically supply every device or directory signal.
8.5 Advanced and production features
| Feature | Availability or dependency | Main value |
|---|---|---|
| Multiple Pomerium replicas | Core with appropriate shared state | Availability and capacity |
| Split-service deployment | Core | Independent component placement |
| PostgreSQL-backed Databroker | Core | Persistent shared state |
| Pomerium Policy Language | Core | Declarative authorization |
| Kubernetes Ingress Controller | Separate Pomerium Kubernetes integration | Kubernetes-native routes |
| Service accounts | Zero and Enterprise feature | Machine authentication |
| Centralized management console | Zero or Enterprise | Easier multi-application management |
| Directory synchronization | Enterprise feature | Organization-scale identity management |
| Namespace-based governance | Enterprise feature | Delegated administration |
| Enterprise API | Enterprise | Programmatic administration |
| Advanced Rego policies | Enterprise | More expressive policy logic |
The official Pomerium product documentation distinguishes these capabilities across Core, Zero, and Enterprise. Pomerium+2
Chapter 9. Similar Tools and Alternatives
Pomerium is one of several technologies that address application access.
The correct alternative depends on whether you primarily need application authentication, cloud-managed access, general private networking, or infrastructure access.
9.1 Comparison
| Tool | Primary purpose | Main strength | Main limitation or trade-off | Best use case | Relationship to Pomerium |
|---|---|---|---|---|---|
| Pomerium | Identity-aware application access | Open-source proxy with route-level policies | Requires gateway operations and configuration | Self-hosted protected web apps | Reference tool |
| OAuth2 Proxy | OAuth/OIDC authentication proxy | Focused authentication integration | Advanced authorization designs may require additional components | Add SSO to existing web apps | Direct alternative for some use cases |
| Cloudflare Access | Managed identity-aware application access | Managed service with device and identity policies | Dependence on Cloudflare service and architecture | Cloud-managed application access | Direct alternative |
| Authentik | Identity provider and proxy authentication | Integrated identity and application access features | Managing the broader identity platform adds responsibility | Self-hosted identity and app access | Alternative or complementary |
| Authelia | Authentication and authorization portal | Self-hosted authentication alongside reverse proxies | Requires compatible integration and operations | Protect self-hosted services | Alternative architecture |
| Teleport | Infrastructure and application access | Strong SSH, Kubernetes, database, and session-oriented capabilities | Broader infrastructure platform with different operational model | Infrastructure access governance | Overlapping alternative |
| Tailscale | Identity-based private networking | Simple private device and network connectivity | Different focus from per-request HTTP proxy enforcement | Devices, private networks, remote connectivity | Often complementary |
| NGINX with external authentication | Reverse proxy with external access checks | Flexible integration into existing proxy stacks | Requires separate identity and authorization design | Organizations already using NGINX | Component alternative |
Cloudflare Access, Authentik, Teleport, and Tailscale provide substantially different technical models despite their overlapping access-security goals. Cloudflare One docs+3
9.2 Pomerium versus OAuth2 Proxy
OAuth2 Proxy is particularly relevant when you have a web application that needs authentication and you want to reuse an existing OAuth 2.0 or OIDC provider.
Pomerium is attractive when you also want a unified identity-aware routing gateway with a dedicated authorization architecture.
This is a design distinction, not an absolute statement that one product is always more secure.
9.3 Pomerium versus Cloudflare Access
Cloudflare Access offers a managed, identity-aware access layer.
Pomerium Core allows organizations to operate the proxy and configuration within their own environment.
Pomerium Zero provides a middle ground by retaining the self-hosted data plane while using a hosted management service.
Choose based on control-plane location, trust boundaries, networking requirements, cost, operational skills, and the features needed.
9.4 Pomerium versus Authentik
Authentik can function as an identity provider and can also protect applications through proxy or forward-authentication integrations.
Pomerium focuses on the identity-aware access gateway.
These tools can sometimes work together, with Authentik supplying identity and Pomerium enforcing access to protected applications.
9.5 Pomerium versus Tailscale
Tailscale is more naturally suited to secure device and network connectivity.
Pomerium is more naturally suited to application access through an identity-aware proxy.
A team might use Tailscale for private connectivity between environments while Pomerium enforces access to specific web applications.
Which Tool Should You Choose and Why?
Interactive technology selection guideSelf-host a central identity-aware gateway for internal web applicationsUse a managed service for protecting web applicationsOperate a self-hosted identity provider and application access systemConnect remote devices and private networksControl SSH, Kubernetes, and database infrastructure access
Start by evaluating Pomerium Core.
Its identity-aware routing and policy model directly match this requirement.
Part III — Architecture and Workflows
Chapter 10. Architecture Explained from Simple to Advanced
10.1 Architecture level 1: The smallest working setup
The first architecture uses one Pomerium instance and one web application.
#chatgpt-mermaid-_r_1gs_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_1gs_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1gs_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1gs_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_1gs_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1gs_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_1gs_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_1gs_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_1gs_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_1gs_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_1gs_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_1gs_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1gs_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1gs_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_1gs_ p{margin:0;}#chatgpt-mermaid-_r_1gs_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1gs_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1gs_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1gs_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_1gs_ .label text,#chatgpt-mermaid-_r_1gs_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1gs_ .node rect,#chatgpt-mermaid-_r_1gs_ .node circle,#chatgpt-mermaid-_r_1gs_ .node ellipse,#chatgpt-mermaid-_r_1gs_ .node polygon,#chatgpt-mermaid-_r_1gs_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_1gs_ .rough-node .label text,#chatgpt-mermaid-_r_1gs_ .node .label text,#chatgpt-mermaid-_r_1gs_ .image-shape .label,#chatgpt-mermaid-_r_1gs_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_1gs_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_1gs_ .rough-node .label,#chatgpt-mermaid-_r_1gs_ .node .label,#chatgpt-mermaid-_r_1gs_ .image-shape .label,#chatgpt-mermaid-_r_1gs_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_1gs_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_1gs_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1gs_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1gs_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_1gs_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_1gs_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1gs_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1gs_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1gs_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_1gs_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1gs_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1gs_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1gs_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_1gs_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1gs_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_1gs_ .icon-shape,#chatgpt-mermaid-_r_1gs_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1gs_ .icon-shape p,#chatgpt-mermaid-_r_1gs_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_1gs_ .icon-shape .label rect,#chatgpt-mermaid-_r_1gs_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1gs_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_1gs_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_1gs_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_1gs_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_1gs_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_1gs_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_1gs_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1gs_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_1gs_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_1gs_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_1gs_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1gs_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_1gs_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_1gs_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1gs_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_1gs_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_1gs_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1gs_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_1gs_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1gs_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1gs_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1gs_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_1gs_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_1gs_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_1gs_ .node rect,#chatgpt-mermaid-_r_1gs_ .node circle,#chatgpt-mermaid-_r_1gs_ .node ellipse,#chatgpt-mermaid-_r_1gs_ .node polygon,#chatgpt-mermaid-_r_1gs_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1gs_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_1gs_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_1gs_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_1gs_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1gs_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}BrowserPomerium CorePrivate web applicationHosted authenticationHTTPSAuthorized HTTPAuthentication
Pomerium receives browser requests, handles the required authorization process, and forwards permitted traffic to the private application.
This is the setup we will build locally.
10.2 Architecture level 2: Inside Pomerium Core
#chatgpt-mermaid-_r_1h3_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_1h3_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1h3_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1h3_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_1h3_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1h3_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_1h3_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_1h3_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_1h3_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_1h3_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_1h3_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_1h3_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1h3_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1h3_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_1h3_ p{margin:0;}#chatgpt-mermaid-_r_1h3_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1h3_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1h3_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1h3_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_1h3_ .label text,#chatgpt-mermaid-_r_1h3_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1h3_ .node rect,#chatgpt-mermaid-_r_1h3_ .node circle,#chatgpt-mermaid-_r_1h3_ .node ellipse,#chatgpt-mermaid-_r_1h3_ .node polygon,#chatgpt-mermaid-_r_1h3_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_1h3_ .rough-node .label text,#chatgpt-mermaid-_r_1h3_ .node .label text,#chatgpt-mermaid-_r_1h3_ .image-shape .label,#chatgpt-mermaid-_r_1h3_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_1h3_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_1h3_ .rough-node .label,#chatgpt-mermaid-_r_1h3_ .node .label,#chatgpt-mermaid-_r_1h3_ .image-shape .label,#chatgpt-mermaid-_r_1h3_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_1h3_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_1h3_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1h3_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1h3_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_1h3_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_1h3_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1h3_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1h3_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1h3_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_1h3_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1h3_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1h3_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1h3_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_1h3_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1h3_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_1h3_ .icon-shape,#chatgpt-mermaid-_r_1h3_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1h3_ .icon-shape p,#chatgpt-mermaid-_r_1h3_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_1h3_ .icon-shape .label rect,#chatgpt-mermaid-_r_1h3_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1h3_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_1h3_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_1h3_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_1h3_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_1h3_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_1h3_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_1h3_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1h3_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_1h3_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_1h3_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_1h3_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1h3_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_1h3_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_1h3_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1h3_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_1h3_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_1h3_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1h3_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_1h3_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1h3_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1h3_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1h3_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_1h3_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_1h3_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_1h3_ .node rect,#chatgpt-mermaid-_r_1h3_ .node circle,#chatgpt-mermaid-_r_1h3_ .node ellipse,#chatgpt-mermaid-_r_1h3_ .node polygon,#chatgpt-mermaid-_r_1h3_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1h3_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_1h3_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_1h3_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_1h3_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1h3_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Pomerium Core — all-in-oneProxyAuthorizeAuthenticateDatabrokerBrowserIdentity providerUpstream applicationLogin redirects
An all-in-one deployment contains the logical Pomerium services in one running process.
Why this is useful: It reduces configuration and deployment complexity while preserving the main Pomerium architecture.
Pomerium officially recommends all-in-one mode for its simplicity, and notes that it can also be used in large production environments. Pomerium
10.3 Architecture level 3: Split-service deployment
In a split-service deployment, components are separated so that they can be deployed and operated independently.
#chatgpt-mermaid-_r_1he_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_1he_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1he_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1he_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_1he_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1he_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_1he_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_1he_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_1he_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_1he_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_1he_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_1he_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1he_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1he_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_1he_ p{margin:0;}#chatgpt-mermaid-_r_1he_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1he_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1he_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1he_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_1he_ .label text,#chatgpt-mermaid-_r_1he_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1he_ .node rect,#chatgpt-mermaid-_r_1he_ .node circle,#chatgpt-mermaid-_r_1he_ .node ellipse,#chatgpt-mermaid-_r_1he_ .node polygon,#chatgpt-mermaid-_r_1he_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_1he_ .rough-node .label text,#chatgpt-mermaid-_r_1he_ .node .label text,#chatgpt-mermaid-_r_1he_ .image-shape .label,#chatgpt-mermaid-_r_1he_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_1he_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_1he_ .rough-node .label,#chatgpt-mermaid-_r_1he_ .node .label,#chatgpt-mermaid-_r_1he_ .image-shape .label,#chatgpt-mermaid-_r_1he_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_1he_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_1he_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1he_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1he_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_1he_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_1he_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1he_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1he_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1he_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_1he_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1he_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1he_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1he_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_1he_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1he_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_1he_ .icon-shape,#chatgpt-mermaid-_r_1he_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1he_ .icon-shape p,#chatgpt-mermaid-_r_1he_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_1he_ .icon-shape .label rect,#chatgpt-mermaid-_r_1he_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1he_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_1he_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_1he_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_1he_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_1he_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_1he_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_1he_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1he_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_1he_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_1he_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_1he_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1he_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_1he_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_1he_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1he_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_1he_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_1he_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1he_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_1he_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1he_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1he_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1he_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_1he_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_1he_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_1he_ .node rect,#chatgpt-mermaid-_r_1he_ .node circle,#chatgpt-mermaid-_r_1he_ .node ellipse,#chatgpt-mermaid-_r_1he_ .node polygon,#chatgpt-mermaid-_r_1he_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1he_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_1he_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_1he_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_1he_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1he_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}BrowserLoad balancerPomerium Proxy servicePomerium Authorize servicePomerium AuthenticateservicePomerium DatabrokerserviceIdentity providerPersistent storagePrivate applications
Separating services can provide more deployment control, but it also introduces operational complexity.
Administrators must explicitly configure inter-service communication, addresses, credentials, and appropriate transport security.
More components do not automatically mean higher availability. Reliability depends on replica counts, state storage, dependencies, and failure handling.
10.4 Understanding the Databroker storage options
| Storage type | Persistence | Multi-replica suitability | Best use |
|---|---|---|---|
| Memory | No persistence across process restarts | Not suitable for independent memory-backed Databroker replicas | Initial experiments |
| File | Persistent local storage | Normally single-instance; clustered Databroker is a separate advanced capability | Small self-managed installations |
| PostgreSQL | Persistent database | Suitable for shared-state multi-replica deployments | Conventional production HA architecture |
Pomerium also documents an experimental clustered Databroker capability. Treat that as an architecture requiring independent evaluation rather than assuming that local file storage automatically provides cluster-wide redundancy. Pomerium+1
10.5 Architecture level 4: Production deployment
A production deployment might use multiple Pomerium instances, a load balancer, private upstream services, centralized monitoring, and persistent database storage.
#chatgpt-mermaid-_r_1hp_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_1hp_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1hp_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1hp_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_1hp_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1hp_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_1hp_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_1hp_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_1hp_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_1hp_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_1hp_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_1hp_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1hp_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1hp_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_1hp_ p{margin:0;}#chatgpt-mermaid-_r_1hp_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1hp_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1hp_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1hp_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_1hp_ .label text,#chatgpt-mermaid-_r_1hp_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1hp_ .node rect,#chatgpt-mermaid-_r_1hp_ .node circle,#chatgpt-mermaid-_r_1hp_ .node ellipse,#chatgpt-mermaid-_r_1hp_ .node polygon,#chatgpt-mermaid-_r_1hp_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_1hp_ .rough-node .label text,#chatgpt-mermaid-_r_1hp_ .node .label text,#chatgpt-mermaid-_r_1hp_ .image-shape .label,#chatgpt-mermaid-_r_1hp_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_1hp_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_1hp_ .rough-node .label,#chatgpt-mermaid-_r_1hp_ .node .label,#chatgpt-mermaid-_r_1hp_ .image-shape .label,#chatgpt-mermaid-_r_1hp_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_1hp_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_1hp_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1hp_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1hp_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_1hp_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_1hp_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1hp_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1hp_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1hp_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_1hp_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1hp_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1hp_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1hp_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_1hp_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1hp_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_1hp_ .icon-shape,#chatgpt-mermaid-_r_1hp_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1hp_ .icon-shape p,#chatgpt-mermaid-_r_1hp_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_1hp_ .icon-shape .label rect,#chatgpt-mermaid-_r_1hp_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1hp_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_1hp_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_1hp_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_1hp_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_1hp_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_1hp_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_1hp_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1hp_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_1hp_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_1hp_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_1hp_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1hp_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_1hp_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_1hp_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1hp_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_1hp_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_1hp_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1hp_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_1hp_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1hp_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1hp_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1hp_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_1hp_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_1hp_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_1hp_ .node rect,#chatgpt-mermaid-_r_1hp_ .node circle,#chatgpt-mermaid-_r_1hp_ .node ellipse,#chatgpt-mermaid-_r_1hp_ .node polygon,#chatgpt-mermaid-_r_1hp_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1hp_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_1hp_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_1hp_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_1hp_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1hp_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Application access layerPomerium Replica 1Pomerium Replica 2Employees and approvedclientsHTTPS load balancerCorporate Identity ProviderPostgreSQLPrivate applicationsLogging and monitoring
The diagram deliberately simplifies internal Pomerium communications to focus on deployment topology.
In a real architecture, the Databroker and its database dependencies need an explicit availability design. The identity provider and protected applications also remain separate failure domains.
10.6 Control plane versus data plane
This distinction is important when considering Pomerium Zero and Enterprise.
Data plane: Components processing the actual application requests.
Control plane: Components managing configuration, routes, policies, and operational settings.
With Pomerium Core, the organization manages both configuration and runtime infrastructure.
With Pomerium Zero, the management control plane is hosted, while the Pomerium proxy data plane runs in the organization’s environment.
With Pomerium Enterprise, the organization can operate the additional management control plane itself.
Chapter 11. The Complete Workflow
11.1 Authentication, authorization, and application access
A more detailed workflow includes both success and failure paths.
#chatgpt-mermaid-_r_1i0_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_1i0_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1i0_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1i0_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_1i0_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i0_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_1i0_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_1i0_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_1i0_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_1i0_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_1i0_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_1i0_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1i0_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1i0_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_1i0_ p{margin:0;}#chatgpt-mermaid-_r_1i0_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i0_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i0_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i0_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_1i0_ .label text,#chatgpt-mermaid-_r_1i0_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i0_ .node rect,#chatgpt-mermaid-_r_1i0_ .node circle,#chatgpt-mermaid-_r_1i0_ .node ellipse,#chatgpt-mermaid-_r_1i0_ .node polygon,#chatgpt-mermaid-_r_1i0_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_1i0_ .rough-node .label text,#chatgpt-mermaid-_r_1i0_ .node .label text,#chatgpt-mermaid-_r_1i0_ .image-shape .label,#chatgpt-mermaid-_r_1i0_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_1i0_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_1i0_ .rough-node .label,#chatgpt-mermaid-_r_1i0_ .node .label,#chatgpt-mermaid-_r_1i0_ .image-shape .label,#chatgpt-mermaid-_r_1i0_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_1i0_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_1i0_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1i0_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1i0_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_1i0_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_1i0_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1i0_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1i0_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1i0_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_1i0_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1i0_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i0_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i0_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_1i0_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i0_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_1i0_ .icon-shape,#chatgpt-mermaid-_r_1i0_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1i0_ .icon-shape p,#chatgpt-mermaid-_r_1i0_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_1i0_ .icon-shape .label rect,#chatgpt-mermaid-_r_1i0_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1i0_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_1i0_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_1i0_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_1i0_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_1i0_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_1i0_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_1i0_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1i0_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_1i0_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_1i0_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_1i0_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1i0_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_1i0_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_1i0_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1i0_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_1i0_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_1i0_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1i0_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_1i0_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1i0_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1i0_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1i0_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_1i0_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_1i0_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_1i0_ .node rect,#chatgpt-mermaid-_r_1i0_ .node circle,#chatgpt-mermaid-_r_1i0_ .node ellipse,#chatgpt-mermaid-_r_1i0_ .node polygon,#chatgpt-mermaid-_r_1i0_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1i0_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_1i0_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_1i0_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_1i0_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1i0_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Browser requests protectedURLPomerium matches routeValid session?Start authenticationLogin succeeds?Establish sessionEvaluate route policyAccess allowed?Forward request toapplicationReturn application responseAuthentication failureAccess deniedReturn upstream or gatewayerrorNoYesNoYesYesNoUpstream respondsUpstream failure
11.2 Example: Accessing an engineering dashboard
An employee opens the dashboard URL.
Pomerium determines whether the employee has an authenticated session. If not, the browser follows the login process.
After authentication, Pomerium evaluates the policy associated with the dashboard route.
If the employee satisfies the policy, Pomerium forwards the request to the dashboard.
If the employee does not satisfy the policy, the request is denied before reaching the dashboard.
11.3 How to recognize the results
| Outcome | What it means | Expected observation |
|---|---|---|
| Successful authorization | User satisfies the route policy | Application content loads |
| Authentication required | No valid session is available | Browser enters login process |
| Authentication failure | Login is not completed successfully | Authentication error or failed login |
| Authorization failure | Authenticated user lacks permission | Access denied |
| Upstream failure | Backend is unavailable or incorrectly configured | Proxy or upstream error |
| TLS failure | Secure connection cannot be validated or established | Browser or TLS diagnostic error |
An HTTP status code alone may not tell you exactly which condition occurred. Redirects, application responses, and gateway-generated errors must be interpreted together.
Chapter 12. Where Pomerium Fits into a Larger System
12.1 A realistic enterprise technology stack
Consider a software engineering organization using:
- An identity provider for employee identities.
- Kubernetes for application deployment.
- Grafana for monitoring.
- Jenkins for continuous integration.
- Terraform for infrastructure configuration.
- Prometheus for monitoring metrics.
Pomerium can be the access layer protecting the relevant internal applications.
#chatgpt-mermaid-_r_1i7_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_1i7_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1i7_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1i7_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_1i7_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i7_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_1i7_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_1i7_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_1i7_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_1i7_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_1i7_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_1i7_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1i7_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1i7_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_1i7_ p{margin:0;}#chatgpt-mermaid-_r_1i7_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i7_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i7_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i7_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_1i7_ .label text,#chatgpt-mermaid-_r_1i7_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i7_ .node rect,#chatgpt-mermaid-_r_1i7_ .node circle,#chatgpt-mermaid-_r_1i7_ .node ellipse,#chatgpt-mermaid-_r_1i7_ .node polygon,#chatgpt-mermaid-_r_1i7_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_1i7_ .rough-node .label text,#chatgpt-mermaid-_r_1i7_ .node .label text,#chatgpt-mermaid-_r_1i7_ .image-shape .label,#chatgpt-mermaid-_r_1i7_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_1i7_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_1i7_ .rough-node .label,#chatgpt-mermaid-_r_1i7_ .node .label,#chatgpt-mermaid-_r_1i7_ .image-shape .label,#chatgpt-mermaid-_r_1i7_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_1i7_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_1i7_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1i7_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1i7_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_1i7_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_1i7_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1i7_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1i7_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1i7_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_1i7_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1i7_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i7_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i7_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_1i7_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1i7_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_1i7_ .icon-shape,#chatgpt-mermaid-_r_1i7_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1i7_ .icon-shape p,#chatgpt-mermaid-_r_1i7_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_1i7_ .icon-shape .label rect,#chatgpt-mermaid-_r_1i7_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1i7_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_1i7_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_1i7_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_1i7_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_1i7_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_1i7_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_1i7_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1i7_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_1i7_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_1i7_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_1i7_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1i7_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_1i7_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_1i7_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1i7_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_1i7_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_1i7_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1i7_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_1i7_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1i7_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1i7_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1i7_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_1i7_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_1i7_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_1i7_ .node rect,#chatgpt-mermaid-_r_1i7_ .node circle,#chatgpt-mermaid-_r_1i7_ .node ellipse,#chatgpt-mermaid-_r_1i7_ .node polygon,#chatgpt-mermaid-_r_1i7_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1i7_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_1i7_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_1i7_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_1i7_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1i7_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Private infrastructureGrafanaJenkinsInternal Admin PortalEmployeesCorporate Identity ProviderPomerium Access GatewayObservability platformInfrastructure configurationDeployment and configuration
12.2 Who owns each component?
| Component | Typical responsible team | Responsibility |
|---|---|---|
| Identity provider | IAM or IT security | User identities, authentication, MFA |
| Pomerium | Platform engineering or security engineering | Gateway deployment and access enforcement |
| Application | Application development team | Application correctness and internal permissions |
| DNS and certificates | Infrastructure or platform team | Domain routing and TLS lifecycle |
| Databroker storage | Infrastructure or database operations | Availability and recovery |
| Monitoring | SRE or observability team | Metrics, alerts, incident diagnosis |
| Access policies | Application owners and security teams | Approval and lifecycle of permissions |
12.3 The most important architectural security rule
The protected application must not be independently accessible through an uncontrolled path that bypasses Pomerium.
For example, if Grafana is protected at one hostname but remains publicly available through an unprotected IP address, the access boundary is incomplete.
This is why production network isolation matters just as much as writing the right access policy.
Part IV — Installation and First Success
Chapter 13. Prerequisites and Environment Preparation
Our first working environment will run entirely on your local computer, except for the external hosted authentication service used to sign in.
You do not need to purchase a domain, create a cloud server, or operate an identity provider for this experiment.
13.1 What you need
| Requirement | Needed for first lab? | Notes |
|---|---|---|
| Windows, macOS, or Linux | Yes | Must support the selected Docker installation |
| Docker Engine or Docker Desktop | Yes | Runs the containers |
| Docker Compose | Yes | Starts Pomerium and the demo service together |
| Internet connection | Yes | Downloads images and supports hosted authentication |
| Web browser | Yes | Completes authentication |
| A usable email identity | Yes | Used in the access policy and login |
| Text editor | Yes | Creates YAML files |
mkcert | Recommended for this lab | Creates locally trusted TLS certificates |
| Domain registration | No | We use a special localhost domain |
| Kubernetes | No | Introduced later |
| PostgreSQL | No | Introduced for persistent and replicated deployments |
| Pomerium commercial licence | No | We use Pomerium Core |
13.2 Hardware recommendations
For a basic two-container demonstration, a modern laptop with several gigabytes of available memory is usually sufficient.
A comfortable learning setup would have approximately 8 GB or more of system memory and several gigabytes of free storage, particularly when using Docker Desktop.
These are practical classroom recommendations, not officially published minimum Pomerium server requirements.
Actual production capacity must be measured against requests per second, connection counts, authentication load, policy complexity, and dependencies.
13.3 Network requirements
Our first lab uses these connections:
| Connection | Port | Purpose |
|---|---|---|
| Browser → local Pomerium | TCP 443 | HTTPS access |
| Pomerium → demo Verify container | TCP 8000 | Private Docker-network traffic |
| Browser → hosted authentication | HTTPS/TCP 443 | User sign-in |
| Container environment → external dependencies | As required | Image download and authentication-related services |
Only Pomerium’s HTTPS endpoint needs to be published on the host.
The demo application should not expose a separate host port.
13.4 What is localhost.pomerium.io?
Pomerium documentation uses hostnames such as:
verify.localhost.pomerium.ioCode language: CSS (css)
This special local development domain resolves to the loopback address 127.0.0.1.
That means the browser reaches a service on your own computer.
It avoids the need to manually register a production domain for the local example.
Corporate DNS restrictions, proxy settings, or DNS-rebinding protection may interfere with this special domain. We will account for that during troubleshooting. Pomerium
13.5 Pre-installation checklist
Environment readiness
0 of 6Docker can be installed on my operating systemI can use a terminal and edit text filesTCP port 443 is available locallyI have a working Internet connectionI can authenticate with an email identityI understand this is a local learning environment
Chapter 14. Install Pomerium on Supported Operating Systems
The recommended beginner approach is to install Docker on the host and run the official Pomerium Linux container image.
This keeps the actual Pomerium deployment nearly identical across Windows, Ubuntu, and macOS.
14.1 Operating-system compatibility
| Operating system | Recommended beginner method | Native Pomerium Core server |
|---|---|---|
| Windows 10/11, subject to Docker prerequisites | Docker Desktop with a Linux-container backend | No official native Windows server binary documented |
| Ubuntu | Docker Engine and Compose | Linux binary or .deb package available |
| macOS Apple Silicon | Docker Desktop | Native ARM64 binary available |
| macOS Intel | Compatible Docker Linux-container environment | Native darwin/amd64 removed in Pomerium v0.33 |
| Debian | Docker Engine | Linux binary or .deb package |
| Fedora | Docker Engine | Linux binary or compatible .rpm package |
| RHEL and compatible distributions | Supported container runtime | Linux binary or applicable .rpm package |
Native binary support, package support, and container support are distinct. Do not infer one from another. Pomerium+1
A. Windows Installation
Step 1 — Prepare Windows
Use a supported Windows installation that meets Docker Desktop’s current system requirements.
For a typical Windows 11 setup, Docker Desktop can use Windows Subsystem for Linux 2 (WSL 2).
If WSL is not installed, open PowerShell as Administrator and run:
wsl --install
This installs or enables the necessary WSL components on supported systems. Restart the computer if prompted.
Verify:
wsl --status
If WSL installation fails because virtualization is unavailable, check firmware virtualization settings and the official Microsoft documentation.
Step 2 — Install Docker Desktop
Download Docker Desktop from the official installation page:Docker Desktop for Windows — Official installation instructions
Run the installer and select the WSL 2-based backend where appropriate.
Start Docker Desktop, complete its setup, and wait until the Docker engine is running.
Docker’s official instructions provide the current Windows requirements and supported backends. Docker Docs
Step 3 — Verify Docker
Open a new PowerShell window:
docker --version
docker compose version
docker run --rm hello-world
The hello-world image should print a message indicating that Docker successfully ran a container.
If the Docker engine is unavailable, start Docker Desktop and confirm that Linux containers are enabled.
Step 4 — Install mkcert
If Windows Package Manager is available:
winget install -e --id FiloSottile.mkcertCode language: CSS (css)
Alternatively, use the installation methods documented by the official mkcert project, including Chocolatey or Scoop.
Open a new PowerShell terminal and verify:
mkcert -version
The installation guidance and binaries are available in the official mkcert project.
B. Ubuntu Installation
The following procedure uses Docker’s official APT repository rather than an arbitrary installation script.
Step 1 — Check the Ubuntu version
cat /etc/os-release
uname -m
Verify your operating-system release and CPU architecture are supported by Docker.
For an existing installation, first review whether older Docker packages conflict with the official repository packages.
Step 2 — Install repository prerequisites
sudo apt update
sudo apt install -y ca-certificates curl
ca-certificates provides trusted certificate authorities. curl downloads resources over HTTP or HTTPS.
Step 3 — Add Docker’s official package signing key
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL \
https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.ascCode language: JavaScript (javascript)
The signing key enables APT to verify packages from the configured repository.
Step 4 — Add the Docker package repository
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOFCode language: JavaScript (javascript)
This creates the repository configuration corresponding to your Ubuntu release.
Step 5 — Install Docker and Compose
sudo apt update
sudo apt install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-pluginCode language: CSS (css)
These are the packages specified by Docker’s Ubuntu installation guide. Docker Docs
Step 6 — Start Docker
sudo systemctl enable --now docker
sudo systemctl status docker
The service should report an active state.
Verify container execution:
sudo docker run --rm hello-world
sudo docker compose version
On a standard Docker Engine installation, you may need to prefix Docker commands with sudo.
Adding users to the docker group grants powerful Docker daemon permissions, effectively root-equivalent access in a typical rootful Docker installation. Do not make this change casually.
Step 7 — Install mkcert
First install its certificate-store dependency:
sudo apt install -y libnss3-tools
For an x86_64 Ubuntu computer, the mkcert project’s documented prebuilt-binary method is:
curl -JLO \
"https://dl.filippo.io/mkcert/latest?for=linux/amd64"
chmod +x mkcert-v*-linux-amd64
sudo install -m 0755 \
mkcert-v*-linux-amd64 \
/usr/local/bin/mkcertCode language: JavaScript (javascript)
Verify:
mkcert -version
For ARM64 or other architectures, select the matching binary from the project’s official releases rather than using an AMD64 binary.
C. macOS Installation
Step 1 — Check the architecture
Open Terminal:
uname -m
sw_vers
Common architecture values include:
arm64: Apple Siliconx86_64: Intel Mac
Choose Docker Desktop for the correct architecture.
Step 2 — Install Docker Desktop
Download Docker Desktop from:Docker Desktop for macOS — Official instructions
Open the .dmg installer, move Docker into Applications, and start it.
Complete the initial configuration.
Docker Desktop may require specific macOS versions, system resources, privileges, and licensing depending on the environment and commercial use. Docker Docs
Step 3 — Verify Docker
docker --version
docker compose version
docker run --rm hello-world
If Docker commands cannot communicate with the engine, make sure Docker Desktop has started.
Step 4 — Install mkcert
If Homebrew is installed:
brew install mkcert
For Firefox integration, the official mkcert instructions also recommend:
brew install nss
Verify:
mkcert -version
Optional — Native macOS installation
For a supported Apple Silicon environment, Pomerium’s release documentation provides standalone macOS ARM64 binaries.
For the course’s pinned release, the documented release-artifact naming convention gives:
curl -fL -o pomerium.tar.gz \
https://github.com/pomerium/pomerium/releases/download/v0.33.4/pomerium-darwin-arm64.tar.gz
tar -xzf pomerium.tar.gzCode language: JavaScript (javascript)
After supplying a valid config.yaml, the native binary can be started with:
./pomerium -config config.yaml
Verify the archive’s release assets and integrity before relying on them. This is an alternative deployment method, not a requirement for the Docker lab.
D. Debian, Fedora, RHEL, Rocky Linux, and AlmaLinux
For these systems, first identify the distribution and architecture.
cat /etc/os-release
uname -m
Debian uses APT repositories, while Fedora, RHEL, and related distributions commonly use RPM packages with DNF.
For example, on a Docker-supported Fedora release, the official repository installation is:
sudo dnf config-manager addrepo \
--from-repofile \
https://download.docker.com/linux/fedora/docker-ce.repo
sudo dnf install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
sudo systemctl enable --now docker
sudo docker run --rm hello-worldCode language: JavaScript (javascript)
This assumes the prerequisite repository-management command is available.
Use the distribution-specific official guides for other systems. In particular, do not blindly use Fedora repository instructions on Rocky Linux, AlmaLinux, or RHEL.
Relevant resources:
Pomerium’s native Linux binaries and package repositories are documented separately in Pomerium Core deployment documentation.
Installation verification summary
| Environment | Key verification | Successful result |
|---|---|---|
| Windows | Docker Desktop running; Docker and Compose commands succeed | Docker engine responds |
| Ubuntu | Docker systemd service running; hello-world succeeds | Containers can run |
| macOS | Docker Desktop running; hello-world succeeds | Containers can run |
| Other Linux | Appropriate container runtime and Compose installed | Container execution works |
| All systems | mkcert version command works | Certificate utility available |
At this point, you have installed the tools needed to run Pomerium, but Pomerium itself has not yet been deployed.
Chapter 15. Your First Working Pomerium Example
We will now protect an application using Pomerium Core.
15.1 What we are going to build
We will use Pomerium’s demo Verify application.
The application is designed to help confirm identity information and test whether the gateway is working.
Our local deployment will contain:
- One Pomerium container.
- One Verify application container.
- An explicit email-based access policy.
- A locally trusted TLS certificate.
- A private Docker network.
This follows Pomerium’s official Core fundamentals approach while adding local TLS certificate management. Pomerium+1
15.2 Target architecture
#chatgpt-mermaid-_r_1ol_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_1ol_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1ol_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_1ol_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_1ol_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ol_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_1ol_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_1ol_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_1ol_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_1ol_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_1ol_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_1ol_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1ol_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1ol_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_1ol_ p{margin:0;}#chatgpt-mermaid-_r_1ol_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ol_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ol_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ol_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_1ol_ .label text,#chatgpt-mermaid-_r_1ol_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ol_ .node rect,#chatgpt-mermaid-_r_1ol_ .node circle,#chatgpt-mermaid-_r_1ol_ .node ellipse,#chatgpt-mermaid-_r_1ol_ .node polygon,#chatgpt-mermaid-_r_1ol_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_1ol_ .rough-node .label text,#chatgpt-mermaid-_r_1ol_ .node .label text,#chatgpt-mermaid-_r_1ol_ .image-shape .label,#chatgpt-mermaid-_r_1ol_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_1ol_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_1ol_ .rough-node .label,#chatgpt-mermaid-_r_1ol_ .node .label,#chatgpt-mermaid-_r_1ol_ .image-shape .label,#chatgpt-mermaid-_r_1ol_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_1ol_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_1ol_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1ol_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1ol_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_1ol_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_1ol_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1ol_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1ol_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1ol_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_1ol_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1ol_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ol_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ol_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_1ol_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_1ol_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_1ol_ .icon-shape,#chatgpt-mermaid-_r_1ol_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_1ol_ .icon-shape p,#chatgpt-mermaid-_r_1ol_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_1ol_ .icon-shape .label rect,#chatgpt-mermaid-_r_1ol_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_1ol_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_1ol_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_1ol_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_1ol_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_1ol_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_1ol_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_1ol_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1ol_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_1ol_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_1ol_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_1ol_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1ol_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_1ol_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_1ol_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1ol_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_1ol_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_1ol_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1ol_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_1ol_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_1ol_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1ol_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_1ol_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_1ol_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_1ol_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_1ol_ .node rect,#chatgpt-mermaid-_r_1ol_ .node circle,#chatgpt-mermaid-_r_1ol_ .node ellipse,#chatgpt-mermaid-_r_1ol_ .node polygon,#chatgpt-mermaid-_r_1ol_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_1ol_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_1ol_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_1ol_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_1ol_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_1ol_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Browser on your computerPomerium Core containerPomerium hostedauthenticationVerify demo containerhttps://verify.localhost.pomerium.ioSign-in flowAuthorized request
15.3 Create the project directory
Choose the commands matching your operating system.
macOS or Linux Terminal:
mkdir -p ~/pomerium-lab/certs
cd ~/pomerium-labCode language: JavaScript (javascript)
Windows PowerShell:
New-Item -ItemType Directory -Force `
-Path "$HOME\pomerium-lab\certs"
Set-Location "$HOME\pomerium-lab"Code language: PHP (php)
Our initial project structure will be:
pomerium-lab/
├── compose.yaml
├── config.yaml
└── certs/
15.4 Generate locally trusted TLS certificates
We need HTTPS because authentication sessions and browser security depend on secure connections.
mkcert makes local development certificates that your computer can trust.
macOS or Linux Terminal:
cd ~/pomerium-lab/certs
mkcert -install
mkcert "*.localhost.pomerium.io"
cd ..Code language: JavaScript (javascript)
Windows PowerShell:
Set-Location "$HOME\pomerium-lab\certs"
mkcert -install
mkcert "*.localhost.pomerium.io"
Set-Location ..Code language: JavaScript (javascript)
You should now have files resembling:
certs/
├── _wildcard.localhost.pomerium.io.pem
└── _wildcard.localhost.pomerium.io-key.pem
The first file contains the local server certificate. The second contains its private key.
The certificate is for local development only. Do not publish the private key, and do not share mkcert’s local root certificate authority private key.
15.5 Create the Pomerium configuration
Create config.yaml in the project root using your text editor.
# config.yaml
# Development authentication service.
authenticate_service_url: https://authenticate.pomerium.app
# Locally trusted TLS certificate.
certificate_file: /pomerium/certs/_wildcard.localhost.pomerium.io.pem
certificate_key_file: /pomerium/certs/_wildcard.localhost.pomerium.io-key.pem
# Protected application routes.
routes:
- from: https://verify.localhost.pomerium.io
to: http://verify:8000
pass_identity_headers: true
policy:
- allow:
or:
- email:
is: learner@example.comCode language: PHP (php)
Important: Replace learner@example.com with the email address you will actually use to authenticate.
Understanding the configuration
| Setting | Explanation |
|---|---|
authenticate_service_url | Specifies the hosted authentication service used in this demonstration |
certificate_file | Path to the server certificate inside the Pomerium container |
certificate_key_file | Path to the corresponding private key |
routes | Collection of protected application routes |
from | Address users open in their browsers |
to | Destination to which Pomerium forwards authorized requests |
pass_identity_headers | Enables forwarding of Pomerium identity assertion headers |
policy | Defines authorization rules |
allow | Specifies conditions that grant access |
email | Checks the authenticated user’s email identity |
is | Requires an exact value match |
The Verify app will be reachable through Pomerium, not through a separately published application port.
15.6 Create Docker Compose configuration
Create compose.yaml:
services:
pomerium:
image: pomerium/pomerium:v0.33.4
ports:
- "127.0.0.1:443:443"
volumes:
- ./config.yaml:/pomerium/config.yaml:ro
- ./certs:/pomerium/certs:ro
networks:
- private
verify:
image: pomerium/verify:latest
expose:
- "8000"
networks:
- private
networks:
private: {}Code language: PHP (php)
What the file does
pomerium service: Runs the official Pomerium Core image.
verify service: Runs the official demo application.
Port mapping: Binds Pomerium’s HTTPS listener to the local machine’s loopback interface on port 443.
Volumes: Mounts configuration and certificates from the host computer into the container.
Read-only mounts: The :ro suffix prevents the container from writing to those mounted host paths.
Private network: Allows the two containers to communicate.
expose: Documents the Verify service’s internal port without publishing that port to the host.
This uses a pinned Pomerium Core version to make the tutorial reproducible. The demo image follows the official example’s latest pattern; pin that dependency to a tested version or image digest for a reproducible production build.
15.7 Validate the Compose file
Open Terminal or PowerShell inside pomerium-lab.
docker compose config --quiet
This checks the Compose configuration.
If the command succeeds, it normally produces no output.
It does not fully validate Pomerium’s own configuration, certificates, or access policies.
15.8 Start the application stack
docker compose up -d
The command downloads images when necessary, creates the Docker network, and starts the services.
The -d option means detached mode: the containers run without occupying the current terminal.
15.9 Check container status
docker compose ps
Both services should show a running state.
To inspect Pomerium’s output:
docker compose logs --tail=100 pomerium
Look for successful startup and configuration messages.
Do not assume that a container being listed as running means the full authentication process works. We must test it.
15.10 Run Pomerium’s health check
docker compose exec pomerium pomerium health
Pomerium’s official container includes a health subcommand. It returns success when its applicable health checks pass.
For more detail:
docker compose exec pomerium pomerium health -v
Pomerium supports health reporting for components such as its proxy, authorization service, configuration, and storage. Pomerium
15.11 Open the application
Your local protected application
Open local Verify applicationThis URL only works after you have deployed the local lab successfully.
Your browser should reach Pomerium and enter the authentication flow.
Sign in using the email identity configured in config.yaml.
Once your identity is verified and the route policy is satisfied, you should reach the Pomerium Verify application.
Depending on the Verify image and assertion configuration, the application may display available identity and session details.
If your route is denied, confirm the configured email exactly matches the authenticated identity.
15.12 Test authorization
To understand why Pomerium is valuable, perform a small access-control experiment.
Open config.yaml and temporarily replace the permitted email address with a different address that you do not own.
For example:
policy:
- allow:
or:
- email:
is: another-user@example.comCode language: CSS (css)
Save the file.
Pomerium supports hot reloading of several configuration settings, but for a simple reproducible lab, explicitly restart its container:
docker compose restart pomerium
Return to the Verify application in your browser and refresh.
Your authenticated identity should no longer satisfy the route policy, so access should be denied.
Restore your actual email address in the configuration and restart Pomerium again.
This experiment demonstrates the difference between being authenticated and being authorized.
15.13 Test application isolation
Observe that the Verify container has no host-published application port.
For example, opening http://localhost:8000 should not reach this Verify service through the normal host network, assuming no unrelated application is listening there.
The Verify service is reached by Pomerium through the Docker network.
This is only a local demonstration of network isolation, not a complete container-hardening or production network-security design.
15.14 Shut down safely
When finished:
docker compose down
This stops and removes the containers and the Compose network.
Your configuration files and certificates remain in your project directory.
Do not add --volumes to a cleanup command casually in later projects, because named volumes may hold persistent data.
15.15 First-lab troubleshooting
| Symptom | Likely cause | First action |
|---|---|---|
| Docker command fails | Docker engine unavailable | Start Docker Desktop or Docker service |
| Port binding error | Port 443 already used | Identify the competing listener |
| Browser certificate warning | mkcert certificate not trusted | Check mkcert installation and trust |
| Login repeatedly fails | Authentication or browser session problem | Inspect logs and browser redirects |
| Access denied | Email does not match policy | Verify authenticated email |
| Proxy error | Verify container not reachable | Check Compose network and service status |
| Nothing loads | Container not running, port problem, or DNS issue | Check docker compose ps and logs |
15.16 Success criteria
You have completed your first Pomerium deployment when you can demonstrate all of the following:
- Pomerium starts in Docker.
- The Verify application is reachable through the Pomerium HTTPS route.
- Authentication completes successfully.
- The authorized identity can access the application.
- An identity that does not meet the policy is denied.
- The application is not independently published on the host’s port 8000.
- Pomerium logs and health checks can be inspected.
- You can stop the environment safely.
You have now built a working identity-aware application access gateway.
Part V — Commands, Configuration, and Daily Usage
Chapter 16. Essential Commands and Operations
Pomerium Core is primarily a service rather than an interactive command-line application.
In our Docker deployment, most daily operations use Docker Compose. The Pomerium server binary also supports operational commands such as health checks.
Do not confuse the Pomerium server binary with the separate pomerium-cli client used for certain non-HTTP connections.
16.1 Docker Compose command reference
| Command | Purpose | When to use it | Warning |
|---|---|---|---|
docker compose up -d | Start the stack | Initial startup | Starts configured services |
docker compose ps | Show service status | Routine inspection | Running does not prove application health |
docker compose logs | Show logs | Troubleshooting | Logs can contain sensitive data |
docker compose logs -f | Follow logs | Live diagnosis | Runs until interrupted |
docker compose restart pomerium | Restart Pomerium | Apply restart-requiring changes | Interrupts active connections |
docker compose pull | Download configured images | Preparing upgrades | May download newer images for floating tags |
docker compose config --quiet | Validate Compose configuration | Before deployment | Does not validate Pomerium policies |
docker compose exec pomerium pomerium health | Check Pomerium health | Routine verification | Requires running container |
docker compose down | Stop and remove Compose services | Lab cleanup | Interrupts access |
docker compose down --volumes | Remove containers and named volumes | Destructive lab reset | Deletes persisted volume data |
16.2 Basic daily operations
Start the environment:
docker compose up -d
Inspect it:
docker compose ps
Read recent logs:
docker compose logs --tail=100 pomerium
Follow new log messages:
docker compose logs -f pomerium
Stop following logs with Ctrl+C. This does not stop the container.
16.3 Inspect container health
docker compose exec pomerium pomerium health
Verbose diagnostic output:
docker compose exec pomerium pomerium health -v
Pomerium’s health command uses process exit status to indicate success or failure. A successful check exits with code zero.
16.4 Check the running image
docker compose images
This helps identify which container images are associated with the current Compose project.
To inspect the pinned Pomerium image:
docker image inspect pomerium/pomerium:v0.33.4
Image inspection provides container-image metadata. It is not a substitute for checking Pomerium’s release notes and security advisories.
16.5 Native Pomerium commands
For users running a native server binary, the documented startup form is:
./pomerium -config config.yaml
The configuration path identifies the file to load.
Pomerium’s operational health command is:
pomerium health
For command-specific assistance:
pomerium health --help
16.6 The separate Pomerium client
The pomerium-cli tool is used for supported client-side connections such as TCP routes.
After installing the appropriate client from the official Pomerium client documentation, inspect its available commands:
pomerium-cli --help
For example, the client supports TCP access to correctly configured Pomerium TCP routes.
This is an advanced topic because it requires more than a browser-accessible HTTP route. Pomerium
Chapter 17. Configuration Explained
17.1 Pomerium’s configuration model
Pomerium Core supports configuration files and environment variables.
Common configuration formats include YAML, JSON, and TOML, although this tutorial uses YAML because it is widely used in infrastructure management.
When the same configuration key is specified in both the configuration file and the environment, the environment variable takes precedence. Pomerium
17.2 Global settings versus route settings
Global configuration affects the Pomerium deployment more broadly.
Route-specific configuration affects a particular protected application.
For example:
# Global setting
log_level: info
routes:
# Route-specific settings
- from: https://portal.localhost.pomerium.io
to: http://portal:80
policy:
- allow:
or:
- email:
is: learner@example.comCode language: PHP (php)
The logging setting is global.
The URL mapping and policy are specific to the portal route.
17.3 Understanding Pomerium Policy Language
Pomerium Policy Language (PPL) lets administrators describe authorization logic.
The major building blocks are:
| Component | Meaning |
|---|---|
allow | Define circumstances granting access |
deny | Define circumstances denying access |
and | Require multiple conditions |
or | Permit matching any of several conditions |
email | Compare an authenticated email |
domain | Compare the email’s domain |
user | Compare a user identifier |
groups | Evaluate group-related identity data when available |
claim/... | Evaluate a particular identity claim |
For example, to permit either of two users:
policy:
- allow:
or:
- email:
is: alice@example.com
- email:
is: bob@example.comCode language: CSS (css)
17.4 Combining allow and deny logic
Suppose everyone in example.com may access a portal except a specifically blocked user.
policy:
allow:
and:
- domain:
is: example.com
deny:
and:
- email:
is: blocked@example.comCode language: CSS (css)
Pomerium’s policy evaluation requires an applicable allow decision and no matching deny condition. Explicit deny takes precedence over allow. Pomerium
A policy requiring a corporate email domain is convenient, but sensitive production access may require stronger constraints, such as explicit user or group permission and suitable identity-provider controls.
17.5 Handling secrets correctly
Our first lab intentionally used minimal configuration.
For a more persistent setup, we should explicitly configure cryptographic secrets and state storage.
Pomerium uses important secrets such as:
- Shared secret: Used for secure communication among Pomerium services.
- Cookie secret: Used to protect session cookies.
- Identity-provider credentials: Required when using a custom identity provider.
- Signing key: Used when providing signed identity assertions to applications.
These have different purposes. Do not reuse the same secret for unrelated settings.
17.6 Generate cryptographically random secrets
A simple cross-platform approach is to use a temporary Alpine Linux container.
Run this command twice:
docker run --rm alpine:3.20 \
sh -c "head -c 32 /dev/urandom | base64"Code language: JavaScript (javascript)
On Windows PowerShell, use the same command on one line:
docker run --rm alpine:3.20 sh -c "head -c 32 /dev/urandom | base64"Code language: JavaScript (javascript)
Each execution should produce a different Base64-encoded random value.
Use one value for SHARED_SECRET and another for COOKIE_SECRET.
17.7 Create a local environment file
In your project directory, create .env.
POMERIUM_SHARED_SECRET=REPLACE_WITH_FIRST_GENERATED_VALUE
POMERIUM_COOKIE_SECRET=REPLACE_WITH_SECOND_GENERATED_VALUE
Replace both placeholders.
Do not commit this file to a Git repository.
Create .gitignore:
.env
certs/*-key.pem
*.key
ec_private.pemCode language: CSS (css)
This file helps prevent accidental source-control commits, but it does not provide encryption or a production secrets-management system.
17.8 Enable persistent Databroker storage
Update config.yaml:
authenticate_service_url: https://authenticate.pomerium.app
certificate_file: /pomerium/certs/_wildcard.localhost.pomerium.io.pem
certificate_key_file: /pomerium/certs/_wildcard.localhost.pomerium.io-key.pem
# Persist session-related data locally.
databroker_storage_type: file
databroker_storage_connection_string: file:///data/pomerium
log_level: info
routes:
- from: https://verify.localhost.pomerium.io
to: http://verify:8000
pass_identity_headers: true
policy:
- allow:
or:
- email:
is: learner@example.comCode language: PHP (php)
Remember to replace the email.
Now replace compose.yaml with:
services:
pomerium:
image: pomerium/pomerium:v0.33.4
ports:
- "127.0.0.1:443:443"
environment:
SHARED_SECRET: ${POMERIUM_SHARED_SECRET}
COOKIE_SECRET: ${POMERIUM_COOKIE_SECRET}
volumes:
- ./config.yaml:/pomerium/config.yaml:ro
- ./certs:/pomerium/certs:ro
- pomerium-data:/data
networks:
- private
verify:
image: pomerium/verify:latest
expose:
- "8000"
networks:
- private
networks:
private: {}
volumes:
pomerium-data:Code language: PHP (php)
The named volume stores the local Databroker files across ordinary container recreation.
This is a persistent, single-instance configuration—not a high-availability architecture.
17.9 Apply the changes
docker compose config --quiet
docker compose up -d
Recreating Pomerium after introducing new secrets may invalidate previously established sessions. Sign in again if necessary.
Verify health:
docker compose exec pomerium pomerium health -v
The file-backed Databroker should now be configured, provided the volume is mounted and writable.
The hosted authentication service still has its own session limitations. Persistent local storage does not remove those limitations.
17.10 Configuration precedence
| Source | Behavior | Typical usage |
|---|---|---|
| YAML configuration file | Defines Core configuration | Routes, policies, runtime settings |
| Environment variables | Override duplicate file keys | Runtime secrets and deployment settings |
| Product-specific control plane | Manages supported configuration in Zero or Enterprise | Centralized administration |
| Bootstrap configuration | Initializes important runtime settings | Internal service secrets and addresses |
Pomerium can hot-reload several configuration categories, including routes and policies. Other changes may require service recreation or restart.
Always verify the effective behavior after a configuration update.
Chapter 18. Logs, Status, and Verification
18.1 Installation success is not operational success
A running container proves only that a process is running.
A fully working Pomerium deployment must also authenticate users, evaluate policies, connect to upstream applications, and return the correct responses.
18.2 Routine diagnostic commands
Inspect the deployment:
docker compose ps
Check Pomerium health:
docker compose exec pomerium pomerium health -v
Inspect recent logs:
docker compose logs --tail=200 pomerium
Inspect all service logs:
docker compose logs --tail=100
18.3 Understanding health endpoints
Pomerium exposes internal health checks, normally on 127.0.0.1:28080 within its runtime environment.
| Endpoint | Purpose |
|---|---|
/startupz | Startup readiness |
/readyz | Readiness to receive traffic |
/healthz | Liveness |
/status | Human-readable component status |
These are distinct from Pomerium’s public listener and routing endpoints.
The internal health port should not be exposed directly to the public Internet. Pomerium
18.4 HTTP connectivity check
On macOS or Linux:
curl -I https://verify.localhost.pomerium.io/pingCode language: JavaScript (javascript)
On Windows PowerShell:
curl.exe -I https://verify.localhost.pomerium.io/pingCode language: JavaScript (javascript)
Pomerium documents /ping as a special endpoint for basic connectivity and health checks.
A normal result is an HTTP success response such as 200 OK.
This proves basic HTTPS reachability to Pomerium, not that authentication or an upstream application is functioning.
18.5 Understanding HTTP status codes
| Status | Meaning | What to inspect |
|---|---|---|
| 200 | Successful response | Confirm it is from the expected endpoint |
| 301 / 302 | Redirect | Check login and destination URLs |
| 401 | Authentication-related failure | Credentials, tokens, session |
| 403 | Access forbidden | Policy and user identity |
| 404 | Resource not found | Hostname, route, application path |
| 502 | Bad gateway | Upstream availability and connectivity |
| 503 | Service unavailable | Gateway or backend health |
| 504 | Gateway timeout | Connectivity, slow backend, timeout configuration |
These are general HTTP meanings. The exact cause depends on the responding component.
18.6 Log levels
Pomerium supports logging levels such as info, warn, error, debug, and trace.
For routine operation:
log_level: infoCode language: HTTP (http)
For targeted troubleshooting:
log_level: debugCode language: HTTP (http)
Use more verbose levels temporarily when needed.
Verbose logs can increase data volume and expose sensitive operational context. Restrict access and return to the normal logging level after diagnosis.
18.7 A systematic troubleshooting method
#chatgpt-mermaid-_r_22g_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_22g_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_22g_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_22g_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_22g_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22g_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_22g_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_22g_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_22g_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_22g_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_22g_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_22g_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_22g_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_22g_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_22g_ p{margin:0;}#chatgpt-mermaid-_r_22g_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22g_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22g_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22g_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_22g_ .label text,#chatgpt-mermaid-_r_22g_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22g_ .node rect,#chatgpt-mermaid-_r_22g_ .node circle,#chatgpt-mermaid-_r_22g_ .node ellipse,#chatgpt-mermaid-_r_22g_ .node polygon,#chatgpt-mermaid-_r_22g_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_22g_ .rough-node .label text,#chatgpt-mermaid-_r_22g_ .node .label text,#chatgpt-mermaid-_r_22g_ .image-shape .label,#chatgpt-mermaid-_r_22g_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_22g_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_22g_ .rough-node .label,#chatgpt-mermaid-_r_22g_ .node .label,#chatgpt-mermaid-_r_22g_ .image-shape .label,#chatgpt-mermaid-_r_22g_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_22g_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_22g_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_22g_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_22g_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_22g_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_22g_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_22g_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_22g_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_22g_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_22g_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_22g_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22g_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22g_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_22g_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22g_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_22g_ .icon-shape,#chatgpt-mermaid-_r_22g_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_22g_ .icon-shape p,#chatgpt-mermaid-_r_22g_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_22g_ .icon-shape .label rect,#chatgpt-mermaid-_r_22g_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_22g_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_22g_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_22g_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_22g_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_22g_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_22g_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_22g_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_22g_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_22g_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_22g_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_22g_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_22g_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_22g_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_22g_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_22g_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_22g_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_22g_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_22g_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_22g_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_22g_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_22g_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_22g_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_22g_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_22g_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_22g_ .node rect,#chatgpt-mermaid-_r_22g_ .node circle,#chatgpt-mermaid-_r_22g_ .node ellipse,#chatgpt-mermaid-_r_22g_ .node polygon,#chatgpt-mermaid-_r_22g_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_22g_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_22g_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_22g_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_22g_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_22g_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Application cannot beaccessedPomerium running?Inspect container logsLocal HTTPS reachable?Check DNS, port, and TLSAuthentication works?Check identity provider andsessionPolicy allows user?Check authorization rulesUpstream reachable?Check Docker network andbackendSuccessful applicationresponseNoYesNoYesNoYesNoYesNoYes
Always find which layer is failing before changing configuration.
Part VI — Real-World Applications
Chapter 19. Practical Use Cases
Pomerium becomes particularly useful when access to multiple protected resources must be controlled consistently.
Use case 1 — Protect a personal development dashboard
Problem: A developer has a private dashboard and does not want arbitrary visitors reaching it.
Environment: A small self-hosted server or container environment.
Implementation: Deploy Pomerium in front of the dashboard and allow only the developer’s authenticated identity.
Expected outcome: The dashboard is accessed through the approved route, while direct access is restricted.
Success measurements: Successful authorized access, expected denial for unauthorized identities, and absence of unprotected public backend routes.
Risk: The developer must still maintain the server, certificates, identity provider, and security updates.
Use case 2 — Centralize access for a development team
Problem: A team maintains Jenkins, Grafana, internal documentation, and several engineering tools.
Environment: Applications hosted in Docker or virtual machines.
Implementation: Configure separate Pomerium routes and policies for different tools.
For example:
| Protected service | Example access rule |
|---|---|
| Developer wiki | Approved employee identities |
| Jenkins | Authorized engineering team |
| Grafana | Engineering and operations |
| Administration portal | Explicit administrator identities |
Expected outcome: Consistent, reviewable access policies across multiple applications.
Success measurements: Authorized versus denied requests, access-provisioning effort, and number of independently exposed services.
Risk: Incorrect policy scope could grant broader access than intended.
Use case 3 — Secure production administration tools
Problem: Production administration applications should not be freely reachable.
Environment: Private cloud networks or data centers.
Implementation: Deploy Pomerium as an enforced access boundary, integrate corporate authentication, require suitable MFA, and restrict routes to approved identities.
Expected outcome: Access to administration interfaces is controlled centrally.
Success measurements: Policy-test results, authentication success and failure trends, audit coverage, and gateway uptime.
Risk: A misconfigured network route could allow access around Pomerium. The administration application must also enforce its internal privileges.
Use case 4 — Protect applications running in Kubernetes
Problem: Teams need access to internal services running inside Kubernetes clusters.
Environment: Kubernetes with an ingress controller, internal services, and a corporate IdP.
Implementation: Deploy the supported Pomerium Kubernetes integration, configure routes, and enforce authorization policies.
Expected outcome: Developers reach approved applications through the protected ingress layer.
Success measurements: Valid ingress routing, successful allow/deny tests, deployment availability, and changes managed through version control.
Risk: Kubernetes networking, ingress configuration, TLS certificate issuance, and Pomerium access policies must be coordinated.
Use case 5 — Control contractor access
Problem: A contractor needs access to one project tool but should not receive unnecessary network or application permissions.
Environment: Corporate identity provider and internal project application.
Implementation: Establish a contractor identity, define a narrow route policy, and remove permission when work finishes.
Expected outcome: Access is limited to the approved service and duration.
Success measurements: Only approved identities succeed; access removal takes effect as expected.
Risk: Time-based access lifecycle and session revocation must be designed carefully. A route policy alone does not automatically implement every contract-expiration rule.
Use case 6 — Secure machine-to-machine access
Problem: An automated process needs to call a protected service without an interactive browser login.
Environment: Automated pipelines, scheduled jobs, or service integrations.
Implementation: Evaluate Pomerium Zero or Enterprise service accounts, or another supported token-based integration appropriate to the authentication model.
Expected outcome: An authorized machine identity can access the intended endpoint without sharing a human user’s password.
Success measurements: Successful token validation, least-privilege policy behavior, and safe credential rotation.
Risk: Exposed service-account tokens can be abused. Tokens require protection, expiration, and rotation.
Pomerium documents service-account creation for Zero and Enterprise, along with token-based access methods. Pomerium
Chapter 20. A Complete Hands-On Project
Project: Secure an Internal Operations Portal with Pomerium
We will now extend the first lab into a more realistic multi-application setup.
The project includes:
- A private NGINX web portal.
- The Pomerium Verify application.
- Two protected routes.
- Email-based authorization.
- Local HTTPS certificates.
- Persistent Pomerium state.
- Runtime secrets separated from configuration.
- A basic Pomerium health check.
20.1 Project objective
Build a private web portal that a permitted user can access through Pomerium, while unauthorized users are denied.
20.2 Architecture
#chatgpt-mermaid-_r_22r_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_22r_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_22r_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_22r_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_22r_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22r_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_22r_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_22r_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_22r_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_22r_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_22r_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_22r_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_22r_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_22r_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_22r_ p{margin:0;}#chatgpt-mermaid-_r_22r_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22r_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22r_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22r_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_22r_ .label text,#chatgpt-mermaid-_r_22r_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22r_ .node rect,#chatgpt-mermaid-_r_22r_ .node circle,#chatgpt-mermaid-_r_22r_ .node ellipse,#chatgpt-mermaid-_r_22r_ .node polygon,#chatgpt-mermaid-_r_22r_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_22r_ .rough-node .label text,#chatgpt-mermaid-_r_22r_ .node .label text,#chatgpt-mermaid-_r_22r_ .image-shape .label,#chatgpt-mermaid-_r_22r_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_22r_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_22r_ .rough-node .label,#chatgpt-mermaid-_r_22r_ .node .label,#chatgpt-mermaid-_r_22r_ .image-shape .label,#chatgpt-mermaid-_r_22r_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_22r_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_22r_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_22r_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_22r_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_22r_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_22r_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_22r_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_22r_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_22r_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_22r_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_22r_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22r_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22r_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_22r_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_22r_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_22r_ .icon-shape,#chatgpt-mermaid-_r_22r_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_22r_ .icon-shape p,#chatgpt-mermaid-_r_22r_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_22r_ .icon-shape .label rect,#chatgpt-mermaid-_r_22r_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_22r_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_22r_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_22r_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_22r_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_22r_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_22r_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_22r_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_22r_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_22r_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_22r_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_22r_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_22r_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_22r_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_22r_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_22r_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_22r_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_22r_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_22r_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_22r_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_22r_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_22r_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_22r_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_22r_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_22r_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_22r_ .node rect,#chatgpt-mermaid-_r_22r_ .node circle,#chatgpt-mermaid-_r_22r_ .node ellipse,#chatgpt-mermaid-_r_22r_ .node polygon,#chatgpt-mermaid-_r_22r_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_22r_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_22r_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_22r_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_22r_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_22r_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Private Docker networkVerify applicationNGINX operations portalBrowserPomerium CoreHosted authenticationPersistent Databroker filesHTTPSLoginRoute 1Route 2
20.3 Directory structure
Our completed project will look like this:
pomerium-lab/
├── .env
├── .gitignore
├── compose.yaml
├── config.yaml
├── certs/
│ ├── _wildcard.localhost.pomerium.io.pem
│ └── _wildcard.localhost.pomerium.io-key.pem
└── site/
└── index.html
Use the secrets and certificates generated in the previous chapters.
20.4 Create the application directory
macOS/Linux:
cd ~/pomerium-lab
mkdir -p site
Windows PowerShell:
Set-Location "$HOME\pomerium-lab"
New-Item -ItemType Directory -Force siteCode language: JavaScript (javascript)
20.5 Build the private web application
Create site/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1">
<title>Internal Operations Portal</title>
<style>
body {
font-family: Arial, sans-serif;
background: #f5f7fa;
margin: 0;
padding: 40px 20px;
}
main {
max-width: 700px;
margin: 0 auto;
padding: 32px;
background: white;
border-radius: 12px;
box-shadow: 0 4px 20px #00000010;
}
h1 {
color: #1e293b;
}
p {
line-height: 1.7;
}
.status {
display: inline-block;
padding: 8px 14px;
border-radius: 8px;
background: #dcfce7;
color: #166534;
}
</style>
</head>
<body>
<main>
<h1>Internal Operations Portal</h1>
<p>
This application is protected by Pomerium.
</p>
<p class="status">
Application is reachable.
</p>
<h2>About this demonstration</h2>
<p>
Pomerium authenticates and authorizes requests
before forwarding traffic to this private
NGINX application.
</p>
<p>
The NGINX container has no published host port.
</p>
</main>
</body>
</html>Code language: HTML, XML (xml)
This is intentionally a simple static application.
It does not implement its own login functionality. The access enforcement happens before traffic reaches it.
20.6 Create the final Pomerium configuration
Replace config.yaml with:
authenticate_service_url: https://authenticate.pomerium.app
certificate_file: /pomerium/certs/_wildcard.localhost.pomerium.io.pem
certificate_key_file: /pomerium/certs/_wildcard.localhost.pomerium.io-key.pem
databroker_storage_type: file
databroker_storage_connection_string: file:///data/pomerium
log_level: info
routes:
# Route 1: Identity verification demonstration
- from: https://verify.localhost.pomerium.io
to: http://verify:8000
pass_identity_headers: true
policy:
- allow:
or:
- email:
is: learner@example.com
# Route 2: Internal operations portal
- from: https://portal.localhost.pomerium.io
to: http://portal:80
policy:
- allow:
or:
- email:
is: learner@example.comCode language: PHP (php)
Replace both occurrences of learner@example.com with your authenticated email.
The two routes have different from addresses and different upstream destinations.
Their policies are currently identical, but each can be changed independently.
20.7 Create the final Compose configuration
Replace compose.yaml:
services:
pomerium:
image: pomerium/pomerium:v0.33.4
ports:
- "127.0.0.1:443:443"
environment:
SHARED_SECRET: ${POMERIUM_SHARED_SECRET}
COOKIE_SECRET: ${POMERIUM_COOKIE_SECRET}
volumes:
- ./config.yaml:/pomerium/config.yaml:ro
- ./certs:/pomerium/certs:ro
- pomerium-data:/data
healthcheck:
test: ["CMD", "pomerium", "health"]
interval: 30s
timeout: 5s
retries: 5
networks:
- private
verify:
image: pomerium/verify:latest
expose:
- "8000"
networks:
- private
portal:
image: nginx:stable-alpine
volumes:
- ./site:/usr/share/nginx/html:ro
expose:
- "80"
networks:
- private
networks:
private: {}
volumes:
pomerium-data:Code language: PHP (php)
This Compose file defines three services.
Only Pomerium publishes a host port.
The Verify and NGINX applications are private within the Compose network.
20.8 Ensure secrets exist
Your .env file must contain real, randomly generated values for:
POMERIUM_SHARED_SECRET=REPLACE_WITH_GENERATED_VALUE
POMERIUM_COOKIE_SECRET=REPLACE_WITH_ANOTHER_GENERATED_VALUE
Do not leave the placeholders unchanged.
The file should remain local and excluded from version control.
20.9 Validate and start the project
docker compose config --quiet
docker compose up -d
docker compose ps
The services should start.
Pomerium should eventually report healthy.
Check:
docker compose exec pomerium pomerium health -v
20.10 Test both applications
Verify application
Identity and authorization testOpen Verify
Operations portal
Private NGINX applicationOpen Operations Portal
These are local lab addresses and will only work on a computer where the corresponding Pomerium stack is running.
After successful authentication, the Operations Portal should display the HTML page we created.
20.11 Experiment: Different application permissions
Change the portal route to permit a different email while leaving the Verify route unchanged:
- from: https://portal.localhost.pomerium.io
to: http://portal:80
policy:
- allow:
or:
- email:
is: other-user@example.com
Restart Pomerium:
docker compose restart pomerium
Now test both routes.
Your original identity should still satisfy the Verify policy but should no longer satisfy the portal policy.
This demonstrates independent access rules for different applications.
Restore your original portal policy afterward.
20.12 Verify network exposure
Inspect published ports:
docker compose ps
Only the Pomerium service should show a published host port.
The NGINX container’s internal port 80 should not appear as a separately published host port.
Users with access to the Docker host, container runtime, or private Docker network may have other ways to contact it. Production isolation must consider those trust boundaries.
20.13 Failure experiment
Stop only the portal service:
docker compose stop portal
Try to access:
https://portal.localhost.pomerium.ioCode language: JavaScript (javascript)
Pomerium should remain available, but its portal upstream is now unavailable.
You may receive a gateway error such as a 502 or 503 response, depending on the failure state.
Restore the portal:
docker compose start portal
Verify that the application becomes reachable again.
This teaches the distinction between gateway availability and upstream application availability.
20.14 Cleanup
Stop the complete project:
docker compose down
This preserves the named volume and project files.
For an intentional complete lab reset, after confirming that no useful data remains in the named volumes:
docker compose down --volumes
The second command deletes Compose-managed named volumes associated with the project. Do not use it on production systems without reviewing the consequences.
20.15 Project completion criteria
Your project is complete when:
- Both applications are reachable through separate Pomerium routes.
- Unauthorized identities fail route authorization.
- The upstream containers are not exposed through host-published ports.
- Pomerium provides a successful health check.
- You can inspect logs and change policies.
- You understand how an upstream failure differs from a Pomerium failure.
- You can stop and restore the environment safely.
20.16 Suggested improvements
Once the project works, extend it by integrating a custom identity provider, adding a second user with different permissions, issuing signed identity assertions, monitoring authorization results, and testing a production-oriented storage configuration.
These improvements form the bridge to the advanced chapters.
Part VII — Advanced Capabilities
Chapter 21. Advanced Pomerium Features
The following capabilities are not required for the first local laboratory. Learn them after becoming comfortable with routing, authentication, policies, and operations.
21.1 Advanced authorization policies
A basic policy might allow one authenticated email address.
Advanced policies can combine multiple conditions, such as identity attributes, group membership, request context, and other supported criteria.
For example:
policy:
allow:
or:
- email:
is: lead@example.com
- domain:
is: engineering.example.com
deny:
and:
- email:
is: suspended@example.comCode language: CSS (css)
This illustrative policy allows a named user or users with the specified email domain, except the explicitly denied identity.
Prerequisite: Understanding how allow, deny, and, and or interact.
Trade-off: More complex policies can create surprising access decisions if they are not tested.
For sensitive services, create a test matrix before deploying a policy:
| Identity | Should access? | Why? |
|---|---|---|
| Approved engineer | Yes | Meets allow condition |
| Approved team lead | Yes | Meets allow condition |
| Suspended employee | No | Explicit deny |
| External visitor | No | No matching allow |
| Missing identity data | No | Required policy condition cannot be established |
Pomerium Enterprise also supports Rego policies for advanced logic using Open Policy Agent’s policy language. Rego is not required for ordinary Core policies. Pomerium
21.2 Integrating a custom identity provider
Our beginner lab uses Pomerium’s hosted authentication service.
A production deployment will often use a corporate identity provider.
The architecture changes to:
ApplicationCorporate IdPPomeriumBrowserApplicationCorporate IdPPomeriumBrowser#chatgpt-mermaid-_r_26s_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_26s_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_26s_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_26s_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_26s_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_26s_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_26s_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_26s_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_26s_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_26s_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_26s_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_26s_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_26s_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_26s_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_26s_ p{margin:0;}#chatgpt-mermaid-_r_26s_ .actor{stroke:rgb(83, 154, 248);fill:rgb(222, 234, 251);stroke-width:1;}#chatgpt-mermaid-_r_26s_ rect.actor.outer-path[data-look=”neo”]{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_26s_ rect.note[data-look=”neo”]{stroke:rgb(107, 198, 127);fill:rgb(243, 243, 243);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_26s_ text.actor>tspan{fill:rgb(13, 13, 13);stroke:none;}#chatgpt-mermaid-_r_26s_ .actor-line{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_26s_ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#chatgpt-mermaid-_r_26s_ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_26s_ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_26s_ [id$=”-arrowhead”] path{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_26s_ .sequenceNumber{fill:#707070;}#chatgpt-mermaid-_r_26s_ [id$=”-sequencenumber”]{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_26s_ [id$=”-crosshead”] path{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_26s_ .messageText{fill:rgb(13, 13, 13);stroke:none;}#chatgpt-mermaid-_r_26s_ .labelBox{stroke:rgba(0, 0, 0, 0.1);fill:rgb(252, 252, 252);filter:none;}#chatgpt-mermaid-_r_26s_ .labelText,#chatgpt-mermaid-_r_26s_ .labelText>tspan{fill:rgb(13, 13, 13);stroke:none;}#chatgpt-mermaid-_r_26s_ .loopText,#chatgpt-mermaid-_r_26s_ .loopText>tspan{fill:rgb(13, 13, 13);stroke:none;}#chatgpt-mermaid-_r_26s_ .sectionTitle,#chatgpt-mermaid-_r_26s_ .sectionTitle>tspan{fill:rgb(13, 13, 13);stroke:none;}#chatgpt-mermaid-_r_26s_ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:rgba(0, 0, 0, 0.1);fill:rgba(0, 0, 0, 0.1);}#chatgpt-mermaid-_r_26s_ .note{stroke:rgb(107, 198, 127);fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_26s_ .noteText,#chatgpt-mermaid-_r_26s_ .noteText>tspan{fill:rgb(13, 13, 13);stroke:none;font-weight:normal;}#chatgpt-mermaid-_r_26s_ .activation0{fill:rgb(243, 243, 243);stroke:hsl(0, 0%, 85.2941176471%);}#chatgpt-mermaid-_r_26s_ .activation1{fill:rgb(243, 243, 243);stroke:hsl(0, 0%, 85.2941176471%);}#chatgpt-mermaid-_r_26s_ .activation2{fill:rgb(243, 243, 243);stroke:hsl(0, 0%, 85.2941176471%);}#chatgpt-mermaid-_r_26s_ .actorPopupMenu{position:absolute;}#chatgpt-mermaid-_r_26s_ .actorPopupMenuPanel{position:absolute;fill:rgb(222, 234, 251);box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#chatgpt-mermaid-_r_26s_ .actor-man circle,#chatgpt-mermaid-_r_26s_ line{fill:rgb(222, 234, 251);stroke-width:2px;}#chatgpt-mermaid-_r_26s_ g rect.rect{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_26s_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_26s_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_26s_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_26s_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_26s_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_26s_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_26s_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_26s_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_26s_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_26s_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_26s_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_26s_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_26s_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_26s_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_26s_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_26s_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_26s_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_26s_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Request protected applicationInitiate authenticationAuthenticate through OIDCAuthentication redirectComplete authentication flowEstablish session and evaluate policyAuthorized requestApplication responseReturn response
For a custom OpenID Connect integration, typical setup involves registering an application with the identity provider.
The integration requires values such as the provider or issuer information, a client identifier, a client secret, and approved redirect addresses.
Pomerium exposes corresponding settings such as:
idp_provider: oidc
idp_client_id: REPLACE_WITH_CLIENT_ID
idp_client_secret: REPLACE_WITH_CLIENT_SECRETCode language: HTTP (http)
This is an illustrative configuration fragment, not a complete custom-IdP deployment. A real installation also requires the provider-specific discovery or endpoint settings, authentication-service configuration, and correctly registered redirect URIs.
Use the official instructions for the selected identity provider:Pomerium Authentication and SSO documentation
Do not commit identity-provider client secrets to version control.
21.3 Signed identity assertions and JWT verification
An internal application may need to know which user Pomerium authenticated.
Merely receiving an HTTP header containing a username is not sufficient proof of identity. Headers can be forged by a client unless the architecture establishes a trustworthy way to distinguish them.
Pomerium supports a signed identity assertion, commonly supplied through:
X-Pomerium-Jwt-Assertion: <SIGNED_JWT>Code language: HTML, XML (xml)
The application can verify the signature using Pomerium’s public key and validate important claims.
Generate a signing key
On a machine with OpenSSL installed:
openssl ecparam \
-genkey \
-name prime256v1 \
-noout \
-out ec_private.pem
openssl ec \
-in ec_private.pem \
-pubout \
-out ec_public.pemCode language: CSS (css)
The private key must remain protected.
After securely mounting the private key in the Pomerium container, configure:
signing_key_file: /pomerium/ec_private.pemCode language: JavaScript (javascript)
On a protected route:
pass_identity_headers: trueCode language: JavaScript (javascript)
An upstream application can retrieve the corresponding verification information from Pomerium’s JSON Web Key Set endpoint.
/.well-known/pomerium/jwks.json
A correct JWT verifier must validate more than the signature: it should also validate the issuer, audience, expiration, and other claims appropriate to the application.
Do not paste production identity tokens into public debugging websites. Pomerium
21.4 TCP and UDP access
Pomerium also supports certain non-HTTP protocols through its client tools.
This is a different workflow from accessing a normal website.
For example, a TCP route may look like:
routes:
- from: tcp+https://ssh.example.com:22
to: tcp://ssh.internal.example.com:22
policy:
- allow:
or:
- email:
is: engineer@example.comCode language: JavaScript (javascript)
This example assumes the named infrastructure exists and the upstream server is reachable.
A correctly configured client connection might use:
pomerium-cli tcp ssh.example.com:22 \
--listen 127.0.0.1:2222Code language: CSS (css)
In another terminal, a user can connect through the local forwarded port:
ssh 127.0.0.1 -p 2222Code language: CSS (css)
Important: These commands are not part of the earlier local Verify lab. They require a corresponding TCP route, reachable SSH server, suitable authentication, and the separately installed pomerium-cli.
Pomerium also supports UDP tunneling using the appropriate client and route configuration.
There is a significant security distinction: Pomerium’s documentation states that long-lived TCP and WebSocket connections are authorized when established, and changing the policy does not automatically terminate an existing long-running connection. Plan session termination and revocation accordingly. Pomerium
21.5 Kubernetes integration
In Kubernetes, Pomerium can be deployed using its supported Kubernetes integration and Ingress Controller.
An Ingress resource describes how incoming traffic is routed to a service.
The Pomerium controller integrates protected routing with Kubernetes configuration workflows.
#chatgpt-mermaid-_r_291_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_291_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_291_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_291_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_291_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_291_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_291_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_291_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_291_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_291_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_291_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_291_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_291_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_291_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_291_ p{margin:0;}#chatgpt-mermaid-_r_291_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_291_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_291_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_291_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_291_ .label text,#chatgpt-mermaid-_r_291_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_291_ .node rect,#chatgpt-mermaid-_r_291_ .node circle,#chatgpt-mermaid-_r_291_ .node ellipse,#chatgpt-mermaid-_r_291_ .node polygon,#chatgpt-mermaid-_r_291_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_291_ .rough-node .label text,#chatgpt-mermaid-_r_291_ .node .label text,#chatgpt-mermaid-_r_291_ .image-shape .label,#chatgpt-mermaid-_r_291_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_291_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_291_ .rough-node .label,#chatgpt-mermaid-_r_291_ .node .label,#chatgpt-mermaid-_r_291_ .image-shape .label,#chatgpt-mermaid-_r_291_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_291_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_291_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_291_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_291_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_291_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_291_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_291_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_291_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_291_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_291_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_291_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_291_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_291_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_291_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_291_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_291_ .icon-shape,#chatgpt-mermaid-_r_291_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_291_ .icon-shape p,#chatgpt-mermaid-_r_291_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_291_ .icon-shape .label rect,#chatgpt-mermaid-_r_291_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_291_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_291_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_291_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_291_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_291_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_291_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_291_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_291_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_291_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_291_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_291_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_291_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_291_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_291_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_291_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_291_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_291_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_291_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_291_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_291_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_291_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_291_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_291_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_291_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_291_ .node rect,#chatgpt-mermaid-_r_291_ .node circle,#chatgpt-mermaid-_r_291_ .node ellipse,#chatgpt-mermaid-_r_291_ .node polygon,#chatgpt-mermaid-_r_291_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_291_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_291_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_291_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_291_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_291_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}UserKubernetes external entrypointPomeriumIdentity providerKubernetes ServiceApplication Pods
A realistic Kubernetes deployment must account for service discovery, cluster networking, ingress configuration, persistent state, TLS certificates, and operational monitoring.
The Pomerium Ingress Controller documentation lists Kubernetes, supported CPU architectures, PostgreSQL, and a certificate-management solution among its prerequisites.
Check the requirements for the exact controller release being deployed. Do not install obsolete Kubernetes or PostgreSQL versions simply because they meet an older documented minimum. Pomerium
Official guide:Pomerium Kubernetes installation
21.6 High availability
High availability means designing the system so that an individual component failure does not necessarily interrupt the entire service.
A typical high-availability design includes:
- Multiple Pomerium replicas.
- Shared and persistent state where required.
- A load balancer.
- Correctly configured health checks.
- Consistent secrets across replicas.
- Redundant networking and certificate management.
- Monitoring and tested recovery procedures.
For a conventional multi-replica Core design, PostgreSQL-backed Databroker storage is an important option.
Simply starting multiple containers with independent in-memory Databrokers is not a correct substitute for shared session state.
21.7 Automatic certificate management
Public deployments can use automated certificate management.
Pomerium Core supports automatic certificates in suitable environments, including configurations using Let’s Encrypt.
A documented configuration key is:
autocert: trueCode language: JavaScript (javascript)
However, enabling that setting alone is not sufficient.
You also need a compatible deployment environment, correctly configured public DNS, certificate issuance connectivity, and the necessary network ports and storage behavior.
The first laboratory intentionally uses mkcert instead because it is local and does not require a publicly reachable server.
21.8 Managing machine access with service accounts
Human users commonly authenticate through interactive browser-based flows.
Automated jobs need a different authentication strategy.
Pomerium Zero and Enterprise provide service-account capabilities for supported machine-to-machine access.
A service account represents a non-human identity that can be authorized by policy.
Service-account credentials must be scoped carefully, stored securely, rotated, and revoked when no longer required.
21.9 Reverse tunneling
Pomerium also documents reverse-tunneling capabilities for situations where Pomerium cannot directly reach an upstream service.
In a reverse-tunnel architecture, an authenticated SSH client establishes an outbound connection to Pomerium and acts as an upstream endpoint.
This can help expose authorized access to services located behind restrictive network boundaries.
It introduces additional trust, key-management, and tunnel-availability responsibilities and should not be treated as a substitute for proper policy enforcement.
The official feature also has runtime configuration requirements and protocol limitations. Pomerium
21.10 Enterprise-scale management
When applications, teams, and policies multiply, management can become a larger challenge than proxying itself.
Pomerium Zero and Enterprise address different aspects of this complexity.
Important capabilities include centralized configuration, managed cluster settings, service accounts, operational visibility, and—depending on the offering—namespace governance, directory synchronization, and administration APIs.
Choose the management model based on organizational requirements rather than assuming that the largest edition is automatically necessary.
Chapter 22. Security Best Practices
Installing Pomerium does not automatically make all protected applications secure.
The complete security design depends on identity management, network architecture, configuration, application security, and operations.
22.1 The security model
Pomerium can enforce an important application access boundary, but the full security chain is broader.
#chatgpt-mermaid-_r_29o_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_29o_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_29o_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_29o_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_29o_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_29o_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_29o_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_29o_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_29o_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_29o_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_29o_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_29o_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_29o_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_29o_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_29o_ p{margin:0;}#chatgpt-mermaid-_r_29o_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_29o_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_29o_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_29o_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_29o_ .label text,#chatgpt-mermaid-_r_29o_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_29o_ .node rect,#chatgpt-mermaid-_r_29o_ .node circle,#chatgpt-mermaid-_r_29o_ .node ellipse,#chatgpt-mermaid-_r_29o_ .node polygon,#chatgpt-mermaid-_r_29o_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_29o_ .rough-node .label text,#chatgpt-mermaid-_r_29o_ .node .label text,#chatgpt-mermaid-_r_29o_ .image-shape .label,#chatgpt-mermaid-_r_29o_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_29o_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_29o_ .rough-node .label,#chatgpt-mermaid-_r_29o_ .node .label,#chatgpt-mermaid-_r_29o_ .image-shape .label,#chatgpt-mermaid-_r_29o_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_29o_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_29o_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_29o_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_29o_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_29o_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_29o_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_29o_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_29o_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_29o_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_29o_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_29o_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_29o_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_29o_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_29o_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_29o_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_29o_ .icon-shape,#chatgpt-mermaid-_r_29o_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_29o_ .icon-shape p,#chatgpt-mermaid-_r_29o_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_29o_ .icon-shape .label rect,#chatgpt-mermaid-_r_29o_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_29o_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_29o_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_29o_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_29o_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_29o_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_29o_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_29o_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_29o_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_29o_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_29o_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_29o_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_29o_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_29o_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_29o_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_29o_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_29o_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_29o_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_29o_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_29o_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_29o_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_29o_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_29o_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_29o_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_29o_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_29o_ .node rect,#chatgpt-mermaid-_r_29o_ .node circle,#chatgpt-mermaid-_r_29o_ .node ellipse,#chatgpt-mermaid-_r_29o_ .node polygon,#chatgpt-mermaid-_r_29o_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_29o_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_29o_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_29o_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_29o_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_29o_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Trusted identity providerSecure user authenticationCorrect PomeriumauthorizationProtected applicationnetworkApplication-level permissionsMonitoring and incidentresponse
If the identity provider is compromised, permissions are overly broad, or an application has an unprotected alternative entry point, the overall system may still be vulnerable.
Pomerium’s published threat model explicitly identifies several responsibilities outside its protection scope, including the security of trusted third-party identity providers and upstream applications’ own access-control systems. Pomerium
22.2 Insecure practices versus safer alternatives
| Insecure practice | Risk | Safer alternative |
|---|---|---|
| Allow every authenticated user to reach sensitive routes | Excessive permissions | Explicit least-privilege route policies |
| Publish the backend application directly | Bypass of Pomerium | Restrict backend network reachability |
| Use the hosted demo authentication service as an assumed production SLA | Authentication dependency risk | Evaluate a production IdP and authentication architecture |
| Store secrets in public Git repositories | Credential exposure | Secret manager or protected runtime injection |
| Use weak or reused secrets | Compromised sessions or services | Independent cryptographically random secrets |
| Disable TLS verification | Susceptibility to interception | Trusted certificates and correct hostname validation |
| Use local development certificates in production | Inappropriate certificate trust | Managed production certificates |
| Run unreviewed floating container versions | Unexpected changes | Tested version or digest pinning |
| Expose metrics or administration endpoints publicly | Information disclosure or attack surface | Private monitoring endpoints |
| Ignore authentication failures | Missed incidents | Logging, alerts, and investigation |
| Depend on one instance without recovery planning | Availability risk | Tested HA or recovery architecture |
| Rely only on Pomerium for all application permissions | Internal privilege errors | Enforce application-level authorization too |
22.3 Protect authentication
Use the identity provider’s appropriate security controls.
Depending on the organization’s requirements, these may include phishing-resistant MFA, managed-device policies, credential lifecycle management, and timely account deprovisioning.
Pomerium’s policies should use identity attributes that are trustworthy and consistently populated.
22.4 Follow least privilege
A good policy grants access only to the people and services that need it.
For example, a production administration portal should not automatically become available to every person with a company email address.
Use specific groups, identities, or contextual conditions when appropriate.
22.5 Protect upstream applications
The gateway cannot protect traffic that bypasses it.
Use private networks, host firewalls, cloud security groups, Kubernetes NetworkPolicies where appropriate, and application-side validation.
If the application trusts Pomerium-supplied identity information, verify the signed assertion rather than blindly trusting arbitrary client-controlled headers.
22.6 Secure secrets and state
Protect:
- Identity-provider credentials.
- Shared and cookie secrets.
- TLS private keys.
- JWT signing keys.
- Service-account tokens.
- Databroker database credentials.
Use access restrictions, backup encryption, credential rotation, and suitable secret-management systems.
22.7 Monitor and patch
Keep Pomerium, Envoy-related dependencies, the container runtime, the operating system, and protected applications updated according to a tested patch-management process.
Monitor security advisories and release notes.
Upgrades should include regression tests for authentication, authorization, session behavior, and upstream routing.
22.8 Security verification checklist
Production security review
0/12Corporate authentication is appropriately securedMFA requirements are establishedSensitive routes use least-privilege policiesNo uncontrolled direct path bypasses PomeriumPublic and internal TLS connections are validatedSecrets are stored securely and rotatedSession expiration and revocation are understoodLogs and metrics have appropriate access controlsPomerium and dependencies are patchedAllow and deny policies have automated testsBackup and recovery procedures are testedApplication-level permissions remain enforced
Completing this checklist supports a review; it is not a substitute for a security assessment or penetration test.
Chapter 23. Production-Ready Pomerium Architecture
The local experiment works on a single computer. Production environments need a more comprehensive design.
23.1 Reference production architecture
#chatgpt-mermaid-_r_2ah_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_2ah_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_2ah_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_2ah_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_2ah_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2ah_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_2ah_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_2ah_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_2ah_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_2ah_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_2ah_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_2ah_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_2ah_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_2ah_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_2ah_ p{margin:0;}#chatgpt-mermaid-_r_2ah_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2ah_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2ah_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2ah_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_2ah_ .label text,#chatgpt-mermaid-_r_2ah_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2ah_ .node rect,#chatgpt-mermaid-_r_2ah_ .node circle,#chatgpt-mermaid-_r_2ah_ .node ellipse,#chatgpt-mermaid-_r_2ah_ .node polygon,#chatgpt-mermaid-_r_2ah_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_2ah_ .rough-node .label text,#chatgpt-mermaid-_r_2ah_ .node .label text,#chatgpt-mermaid-_r_2ah_ .image-shape .label,#chatgpt-mermaid-_r_2ah_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_2ah_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_2ah_ .rough-node .label,#chatgpt-mermaid-_r_2ah_ .node .label,#chatgpt-mermaid-_r_2ah_ .image-shape .label,#chatgpt-mermaid-_r_2ah_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_2ah_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_2ah_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_2ah_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_2ah_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_2ah_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_2ah_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_2ah_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_2ah_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_2ah_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_2ah_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_2ah_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2ah_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2ah_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_2ah_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2ah_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_2ah_ .icon-shape,#chatgpt-mermaid-_r_2ah_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_2ah_ .icon-shape p,#chatgpt-mermaid-_r_2ah_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_2ah_ .icon-shape .label rect,#chatgpt-mermaid-_r_2ah_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_2ah_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_2ah_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_2ah_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_2ah_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_2ah_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_2ah_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_2ah_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_2ah_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_2ah_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_2ah_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_2ah_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_2ah_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_2ah_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_2ah_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_2ah_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_2ah_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_2ah_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_2ah_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_2ah_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_2ah_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_2ah_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_2ah_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_2ah_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_2ah_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_2ah_ .node rect,#chatgpt-mermaid-_r_2ah_ .node circle,#chatgpt-mermaid-_r_2ah_ .node ellipse,#chatgpt-mermaid-_r_2ah_ .node polygon,#chatgpt-mermaid-_r_2ah_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_2ah_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_2ah_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_2ah_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_2ah_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_2ah_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Protected application networkState infrastructurePrivate application access tierMonitoringEngineering toolsAdmin applicationsHighly available PostgreSQLEncrypted backupsPomerium Replica APomerium Replica BEmployees and approvedclientsCorporate DNSHTTPS Load BalancerCorporate IdPCentralized logs and metricsSecrets and certificatemanagement
This is a conceptual deployment reference, not a ready-to-apply production manifest.
It represents an all-in-one Pomerium replica model with shared persistent state and external operational dependencies.
23.2 Production design requirements
| Area | Essential concern | Implementation direction |
|---|---|---|
| Identity | Secure user authentication | Corporate IdP with appropriate MFA |
| Authorization | Correct permissions | Tested, least-privilege policies |
| Availability | Gateway failures | Multiple replicas where required |
| Session state | Shared persistence | Supported Databroker HA design |
| TLS | Encrypted communication | Managed certificates and trusted upstream TLS |
| DNS | Reliable application addresses | Correctly managed corporate domains |
| Secrets | Protection and rotation | Secret management platform |
| Logs | Operational and security visibility | Central collection and retention |
| Metrics | Capacity and error visibility | Prometheus-compatible monitoring |
| Recovery | Restore after failure | Tested backup and recovery |
| Upgrades | Controlled changes | Staging, canary rollout, rollback |
| Application isolation | Prevent bypass | Private routes and network controls |
23.3 Monitoring and alerting
Useful operational signals include:
- Availability of Pomerium replicas.
- Readiness and health-check failures.
- Successful and denied authorization requests.
- Authentication failures and unusual login patterns.
- HTTP error-rate changes.
- Upstream latency and connection failures.
- Storage connectivity and availability.
- Resource utilization.
- Certificate expiration.
- Configuration deployment failures.
Pomerium exposes Prometheus-compatible metrics. Core deployments require administrators to configure their collection and visualization; Zero and Enterprise provide additional management capabilities. Pomerium
23.4 Backup strategy
A useful backup plan must distinguish between configuration and runtime state.
Configuration backup: Version-control approved policies, route definitions, and deployment manifests.
Persistent state backup: Follow the procedures appropriate to the selected Databroker backend.
Secrets backup: Use protected, organization-approved secret-recovery procedures.
Certificate recovery: Plan certificate renewal and key recovery or replacement as appropriate.
Do not assume that backing up only config.yaml restores every active session or runtime record.
23.5 Upgrading safely
A production upgrade should follow a repeatable process:
- Read the new version’s release notes and migration guidance.
- Check for breaking changes and security fixes.
- Back up applicable configuration and state.
- Reproduce the upgrade in staging.
- Test authentication, authorization, and upstream routing.
- Test both allowed and denied identities.
- Deploy with controlled traffic shifting when supported.
- Verify health checks, metrics, and logs.
- Roll back using the documented procedure if required.
Rollback may be complicated by database or data-format migrations. Verify backward compatibility before relying on a previous image as a recovery strategy.
23.6 Cost considerations
Pomerium Core is open source, but the deployment still consumes resources.
Potential operating costs include compute infrastructure, databases, load balancers, certificates, networking, logging, monitoring, administration, and support.
Pomerium Zero and Enterprise introduce additional product-plan considerations.
Evaluate total operating cost rather than comparing only the software licence price.
23.7 Production-readiness assessment
A deployment is not production-ready simply because the browser login works.
It should demonstrate predictable behavior under normal load, user denial, component failure, expired credentials, configuration changes, and recovery scenarios.
The objective is not zero incidents. It is a system whose behavior, failure modes, and recovery procedures are sufficiently understood and controlled.
Part VIII — Troubleshooting and Mastery
Chapter 24. Common Problems and Solutions
Troubleshooting is one of the most important skills when operating identity-aware access systems.
A good engineer investigates failures systematically rather than repeatedly changing settings without understanding the cause.
24.1 Troubleshooting reference
The error descriptions below represent realistic diagnostic scenarios. Where an exact message has not been independently verified, the wording is illustrative.
| Problem or error | Likely cause | How to diagnose | Solution | How to verify |
|---|---|---|---|---|
| Docker daemon unavailable | Docker engine stopped | Check Docker status | Start Docker Desktop or Docker Engine | Run hello-world |
| Cannot bind port 443 | Another process owns the port | Inspect host listeners | Free port or redesign port mapping | Pomerium starts successfully |
| Certificate not trusted | Missing local CA trust | Inspect browser certificate | Reinstall or correct mkcert trust | Browser accepts HTTPS |
| DNS lookup fails | Local hostname resolution issue | Resolve hostname | Correct DNS or local proxy configuration | Host resolves to loopback |
| Pomerium fails to start | Invalid configuration | Inspect logs | Correct YAML or settings | Health check passes |
| Authentication redirects repeatedly | Cookie, session, IdP, or redirect configuration issue | Inspect redirect chain and logs | Correct the relevant authentication setting | Login completes |
| Authorization denied | User does not satisfy policy | Compare identity with route policy | Correct identity or approved policy | Allowed user succeeds |
| 502 gateway error | Upstream unreachable or unhealthy | Check upstream status and route | Restore upstream connectivity | Application responds |
| Shared secret mismatch | Inconsistent secrets across components | Inspect configuration and secret distribution | Use consistent approved secrets | Health and sessions work |
| Sessions lost after restart | Ephemeral secrets or state | Inspect secrets and Databroker backend | Configure persistent state and stable secrets | Test restart |
| TLS handshake failure | Certificate, hostname, protocol, or trust problem | Inspect TLS configuration | Fix certificate chain or hostname | Secure connection succeeds |
| Missing signed identity assertion | Header forwarding or signing configuration incomplete | Inspect headers and signing-key settings | Configure required identity assertion settings | Upstream validates JWT |
| Health check fails | Internal component unavailable | Use verbose health status | Investigate the failing component | Health returns success |
| Works locally, not remotely | DNS, listener, firewall, or load balancer problem | Trace connectivity | Correct deployment networking | Approved remote client connects |
| Application accessible around Pomerium | Direct backend exposure | Review all network paths | Restrict backend networking | Bypass attempt fails |
24.2 Diagnose port conflicts
On Ubuntu or other Linux systems with ss:
sudo ss -ltnp '( sport = :443 )'Code language: JavaScript (javascript)
On macOS:
sudo lsof -nP -iTCP:443 -sTCP:LISTENCode language: CSS (css)
On Windows PowerShell:
Get-NetTCPConnection -LocalPort 443 -State Listen
These commands help identify whether a listener already occupies port 443.
Do not stop an unfamiliar process without understanding what depends on it.
24.3 Diagnose YAML configuration failures
YAML uses indentation to represent structure.
Incorrect indentation can change the meaning of a configuration file.
For example, this is correctly nested:
routes:
- from: https://portal.localhost.pomerium.io
to: http://portal:80
policy:
- allow:
or:
- email:
is: learner@example.comCode language: JavaScript (javascript)
The policy belongs to the route.
When troubleshooting, check indentation, missing colons, whitespace, and whether the configuration key is valid for the Pomerium release.
Remember that Docker Compose validation alone does not validate the complete Pomerium configuration.
24.4 Diagnose unexpected access denial
A frequent mistake is assuming that successful login means the user should automatically reach the application.
Inspect the route’s policy and the authenticated identity.
For example, a user with email alice@example.org will not satisfy:
policy:
- allow:
or:
- domain:
is: example.comCode language: CSS (css)
The user authenticated successfully, but the allowed domain is different.
Do not weaken the policy to make the error disappear. Confirm the intended identity and access requirements first.
24.5 Diagnose reverse-proxy errors
If the browser reaches Pomerium but the application does not load, inspect the upstream service.
docker compose ps
docker compose logs --tail=100 portal
docker compose logs --tail=100 pomerium
Verify that the destination in the Pomerium route matches the service name and port.
For example:
to: http://portal:80Code language: JavaScript (javascript)
The name portal must resolve in the container network, and the service must be listening on the expected port.
24.6 Diagnose session persistence
If users are unexpectedly prompted to sign in after restarting Pomerium, inspect the configured secrets and Databroker storage backend.
Stable secrets and persistent state are important, but hosted authentication can still require reauthentication due to its own session lifecycle.
Pomerium documents limitations for its free hosted authentication service, including restricted login options, limited session refresh behavior, and no uptime commitment. Pomerium
24.7 A disciplined incident investigation
Record the following before applying changes:
- Exact time of the failure.
- Affected hostname and application.
- Whether all users or only certain users are affected.
- Authentication result.
- Authorization result.
- Pomerium health status.
- Upstream health status.
- Relevant configuration changes.
- Relevant log and trace information.
- The outcome of each corrective action.
This approach makes troubleshooting reproducible and helps prevent repeated incidents.
Chapter 25. Best Practices and Common Mistakes
Good Pomerium operations combine general engineering discipline with practices specific to identity-aware access systems.
25.1 Universal engineering best practices
| Practice | Why it matters |
|---|---|
| Version-control configurations | Supports review, history, and rollback |
| Separate development and production | Reduces accidental production impact |
| Test before deployment | Detects configuration and policy errors |
| Keep dependencies updated | Reduces exposure to known vulnerabilities |
| Monitor systems continuously | Detects failures and unusual behavior |
| Protect secrets | Prevents credential compromise |
| Define ownership | Ensures someone maintains every component |
| Maintain runbooks | Supports reliable incident response |
| Test recovery | Confirms that backups and procedures work |
25.2 Pomerium-specific best practices
| Practice | Why it matters |
|---|---|
| Restrict direct upstream access | Prevents bypassing the identity-aware gateway |
| Test authorized and denied identities | Confirms policy behavior |
| Use trusted identity providers | Establishes reliable authentication |
| Use independent random secrets | Protects sessions and component communication |
| Verify signed identity assertions when needed | Prevents blind trust in identity headers |
| Choose an appropriate Databroker backend | Preserves required state and availability |
| Keep health endpoints private | Protects operational information |
| Monitor policy changes | Detects unexpected access changes |
| Understand long-lived connection behavior | Supports realistic revocation planning |
| Review control-plane responsibilities | Avoids confusion among Core, Zero, and Enterprise |
25.3 Common mistakes beginners should avoid
One mistake is protecting an application with Pomerium while leaving another public address pointing directly to that application.
Another is confusing identity with permission: just because someone has a valid company account does not mean they require access to every engineering tool.
A third is moving directly from a successful Docker demonstration to production without planning certificate management, identity-provider availability, shared state, monitoring, and recovery.
Finally, copying configuration from an older tutorial without checking the current version can introduce deprecated or incompatible behavior.
The correct habit is to use version-matched documentation, test each change, and confirm the observed result.
Chapter 26. Learning Roadmap
The following roadmap organizes your learning into five stages.
The suggested durations are educational estimates, not official Pomerium training requirements.
| Stage | Topics | Practical activity | Expected skill | Suggested effort |
|---|---|---|---|---|
| 1. Fundamentals | Zero Trust, proxies, IdP, OAuth, TLS | Explain request flow and terminology | Conceptual understanding | 4–6 hours |
| 2. First use | Docker, Compose, certificates, routes | Complete Verify lab | Independent basic deployment | 4–6 hours |
| 3. Practical proficiency | PPL, multiple routes, sessions, logs | Complete Operations Portal project | Basic administration | 6–10 hours |
| 4. Advanced use | OIDC, JWTs, TCP, Kubernetes, HA | Integrate a realistic service | Advanced engineering | 10–16 hours |
| 5. Production readiness | Security, monitoring, state, upgrades, recovery | Design and review a production deployment | Operational readiness | 10–16 hours |
Recommended practice schedule
SUGGESTED FIVE-STAGE LEARNING PROGRESSION
- Stage 1 — Understand the domainLearn the identity, access, networking, and proxy concepts. Be able to explain why Pomerium is useful without referencing commands.
- Stage 2 — Build a working gatewayDeploy Core locally, authenticate, change a policy, and diagnose one deliberate failure.
- Stage 3 — Manage multiple applicationsProtect two services with different policies. Introduce persistent state and basic operational checks.
- Stage 4 — Integrate with infrastructurePractice corporate identity integration, JWT verification, and at least one additional protocol or deployment environment.
- Stage 5 — Design for productionProduce an architecture, security checklist, monitoring strategy, upgrade runbook, and tested recovery plan.
Official learning resources
The following resources are useful for continuing your studies.
| Resource | What to study |
|---|---|
| Pomerium documentation | Product overview and capabilities |
| Official quickstart | First deployment options |
| Core fundamentals | Step-by-step beginner lessons |
| Architecture | Components and request flow |
| Authorization | Policies and permissions |
| Health checks | Monitoring runtime health |
| Security model | Threats and responsibilities |
| Kubernetes deployment | Cluster deployments |
| Official releases | Version changes and security fixes |
| Documentation versions | Version-matched documentation |
Pomerium’s default documentation tracks its rolling main development documentation, with versioned snapshots available. For production work, consult the snapshot appropriate to your deployed release whenever possible. Pomerium
Chapter 27. Exercises and Knowledge Assessment
This chapter tests both conceptual understanding and practical engineering judgment.
Do not look at the answer key until you have attempted the exercises.
27.1 Beginner assessment
Beginner knowledge quiz
0 / 6 answered
1. What does authentication determine?Which application is fastestWho the user isWhich container uses the most memoryWhich IP address should be assigned
2. What is Pomerium primarily?A databaseA compilerAn identity-aware reverse proxyAn operating system
3. What does the ‘from’ field specify in a route?The externally accessed URLThe database usernameThe logging outputThe Docker image
4. If an allow rule and an applicable deny rule both match, what happens?Allow always winsDeny takes precedenceThe request always becomes publicThe user is automatically an administrator
5. What does HTTPS primarily provide?Automatic user authorizationContainer schedulingEncrypted and authenticated transportApplication backups
6. Why must the upstream application be isolated from uncontrolled access?To prevent bypassing PomeriumTo make Docker fasterTo disable DNSTo eliminate configuration files
Check answersReset
27.2 Intermediate exercises
Exercise 7 — Policy implementation
Write a Pomerium policy that allows exactly two authenticated email identities:
alice@example.com
bob@example.comCode language: CSS (css)
Do not allow everyone from the entire domain.
Exercise 8 — Operational troubleshooting
A Pomerium container is running, but the browser cannot reach the protected application.
Identify the first four diagnostic checks you would perform.
Exercise 9 — Session persistence
After restarting Pomerium, a user must authenticate again.
Identify at least two configuration-related factors that may contribute to the problem.
Exercise 10 — Secure the backend
The protected NGINX portal is accessible through Pomerium, but a user can also reach its unprotected public IP address.
Explain why this is a security problem and how you would correct it.
Exercise 11 — Policy verification
A new policy is intended to give production dashboard access to three specific engineers.
Describe a test plan that verifies both successful and denied access.
27.3 Advanced exercises
Exercise 12 — High availability
An engineer deploys three Pomerium replicas, each with independent in-memory Databroker storage.
Explain why this is not a suitable conventional high-availability state architecture and propose an improvement.
Exercise 13 — Identity assertions
A custom application trusts an unsigned X-User-Email header received from the client.
Explain the security issue and describe a safer Pomerium integration.
Exercise 14 — Incident response
An administrator removes a user’s access policy while the user already has a long-lived TCP connection.
Explain what must be considered about the existing connection.
Exercise 15 — Deployment model selection
A company wants to keep application traffic in its own infrastructure but would prefer externally managed configuration.
Which Pomerium deployment model should it evaluate first?
What operational responsibility would remain with the company?
Exercise 16 — Production architecture review
Design a Pomerium deployment for a company with:
- 1,000 employees.
- Multiple internal applications.
- A corporate identity provider.
- A Kubernetes environment.
- A high-availability requirement.
- Centralized logging.
Identify the essential infrastructure components and the top five security controls.
27.4 Answer key and explanations
Intermediate answers
Exercise 7
An appropriate policy is:
policy:
- allow:
or:
- email:
is: alice@example.com
- email:
is: bob@example.comCode language: CSS (css)
This permits either specified identity without permitting the entire email domain.
Exercise 8
Start by checking the running services, gateway logs, local DNS resolution, and HTTPS listener or certificate behavior.
Once those succeed, investigate authentication, authorization, and upstream availability.
Exercise 9
Check whether cryptographic secrets remain stable across restarts and whether the Databroker uses persistent storage.
Also review the authentication provider’s independent session limitations.
Exercise 10
Direct public access bypasses Pomerium’s intended security boundary.
Remove the unprotected public path or enforce appropriate network restrictions, and verify that the backend cannot be reached through an unauthorized route.
Exercise 11
Test all three approved engineers, at least one authenticated but unauthorized identity, and an unauthenticated request.
Also test that access is removed successfully after a policy change.
Advanced answers
Exercise 12
Independent in-memory state does not provide a consistent shared view of sessions and relevant Databroker records.
Use an appropriate supported shared persistent backend, such as PostgreSQL, and design its availability and backup strategy.
Exercise 13
An unsigned client-controlled identity header can be forged.
Use a trusted Pomerium-signed identity assertion, validate its cryptographic signature and claims in the upstream application, and protect the upstream network path.
Exercise 14
Pomerium’s documented behavior for certain long-lived connections evaluates authorization when the connection is established. Existing connections may continue after a policy changes.
Operational controls may be needed to terminate or rotate such connections when access must be revoked promptly.
Exercise 15
Pomerium Zero is a suitable first candidate because it offers a managed control plane while the Core data plane remains in the organization’s environment.
The organization still operates the deployed runtime and remains responsible for applicable networking, application security, and infrastructure controls.
Exercise 16
A suitable conceptual architecture would include a load balancer, multiple Pomerium replicas, corporate identity integration, persistent shared state, protected Kubernetes applications, centralized logs and metrics, certificate management, and secrets management.
Five major controls would include strong authentication, least-privilege authorization, backend isolation, encryption and secret protection, and monitored, tested operational recovery.
This design would still require sizing, threat modeling, and workload-specific validation before production deployment.
Chapter 28. Final Revision and Quick Reference
28.1 Pomerium in one paragraph
Pomerium is an identity-aware reverse proxy that helps control access to protected applications and supported services. It receives application traffic, establishes or validates user identity through an authentication system, evaluates configured access policies, and forwards authorized requests to upstream resources. Pomerium Core provides the open-source execution layer, while Pomerium Zero and Enterprise offer different management approaches and additional capabilities.
28.2 The five most important concepts
| Concept | What to remember |
|---|---|
| Authentication | Determines who is making the request |
| Authorization | Determines whether the request is permitted |
| Proxy | Receives and forwards protected traffic |
| Route | Defines the public-facing entry point and backend destination |
| Policy | Defines the conditions for granting or denying access |
28.3 Essential configuration reference
| Setting | Purpose |
|---|---|
routes | List of protected routes |
from | Address accessed by the user |
to | Backend destination |
policy | Authorization logic |
authenticate_service_url | Authentication service URL |
certificate_file | TLS server certificate |
certificate_key_file | TLS private key |
shared_secret / SHARED_SECRET | Internal shared secret |
cookie_secret / COOKIE_SECRET | Session cookie protection |
databroker_storage_type | Databroker storage backend |
databroker_storage_connection_string | Storage location or connection |
log_level | Logging verbosity |
pass_identity_headers | Forward Pomerium identity assertion headers |
signing_key_file | Signing key for identity assertions |
28.4 Essential command cheat sheet
| Task | Command |
|---|---|
| Validate Compose syntax | docker compose config --quiet |
| Start | docker compose up -d |
| Show status | docker compose ps |
| Show recent logs | docker compose logs --tail=100 pomerium |
| Follow logs | docker compose logs -f pomerium |
| Health check | docker compose exec pomerium pomerium health |
| Verbose health check | docker compose exec pomerium pomerium health -v |
| Restart Pomerium | docker compose restart pomerium |
| Show images | docker compose images |
| Stop stack | docker compose down |
| Start native Pomerium | ./pomerium -config config.yaml |
| Pomerium client help | pomerium-cli --help |
On Linux installations where Docker requires elevated permissions, use the appropriate sudo form for Docker commands.
28.5 Troubleshooting cheat sheet
The diagnostic order to remember
CHECK 1
Container status
Is the process running?
CHECK 2
Gateway reachability
Can HTTPS reach Pomerium?
CHECK 3
Authentication
Can the identity be established?
CHECK 4
Authorization
Does the policy allow access?
CHECK 5
Upstream access
Can Pomerium reach the backend?
CHECK 6
Application health
Can the backend serve the request?
28.6 Security precautions to remember
Always restrict backend access so traffic cannot bypass Pomerium. Use trusted identity sources, maintain narrowly scoped policies, secure secrets, validate transport security and identity assertions, monitor operational behavior, and test access denial as carefully as access success.
A successful login is never sufficient evidence that the full security design is correct.
28.7 Best situations for using Pomerium
Pomerium is particularly useful when an organization wants a central access layer for internal web applications, consistent identity-based route policies, integration with an existing identity provider, or secure access to selected supported services.
It is especially relevant to organizations operating developer and administrative tools across multiple infrastructure environments.
28.8 Situations where another tool may be preferable
A VPN or Tailscale may be preferable when the primary requirement is private device or network connectivity.
Authentik or Keycloak may be more directly relevant when the primary requirement is operating an identity provider.
Teleport may be more appropriate when infrastructure access workflows, SSH governance, and session-oriented controls are the dominant requirement.
Cloudflare Access may be attractive when a cloud-managed application access service better matches the organization’s operational preferences.
28.9 Final mental model
#chatgpt-mermaid-_r_2fk_{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;fill:rgb(13, 13, 13);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#chatgpt-mermaid-_r_2fk_ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_2fk_ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#chatgpt-mermaid-_r_2fk_ .error-icon{fill:rgb(243, 243, 243);}#chatgpt-mermaid-_r_2fk_ .error-text{fill:rgb(13, 13, 13);stroke:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2fk_ .edge-thickness-normal{stroke-width:1px;}#chatgpt-mermaid-_r_2fk_ .edge-thickness-thick{stroke-width:3.5px;}#chatgpt-mermaid-_r_2fk_ .edge-pattern-solid{stroke-dasharray:0;}#chatgpt-mermaid-_r_2fk_ .edge-thickness-invisible{stroke-width:0;fill:none;}#chatgpt-mermaid-_r_2fk_ .edge-pattern-dashed{stroke-dasharray:3;}#chatgpt-mermaid-_r_2fk_ .edge-pattern-dotted{stroke-dasharray:2;}#chatgpt-mermaid-_r_2fk_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_2fk_ .marker.cross{stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_2fk_ svg{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:16px;}#chatgpt-mermaid-_r_2fk_ p{margin:0;}#chatgpt-mermaid-_r_2fk_ .label{font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2fk_ .cluster-label text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2fk_ .cluster-label span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2fk_ .cluster-label span p{background-color:transparent;}#chatgpt-mermaid-_r_2fk_ .label text,#chatgpt-mermaid-_r_2fk_ span{fill:rgb(13, 13, 13);color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2fk_ .node rect,#chatgpt-mermaid-_r_2fk_ .node circle,#chatgpt-mermaid-_r_2fk_ .node ellipse,#chatgpt-mermaid-_r_2fk_ .node polygon,#chatgpt-mermaid-_r_2fk_ .node path{fill:rgb(222, 234, 251);stroke:rgb(83, 154, 248);stroke-width:1px;}#chatgpt-mermaid-_r_2fk_ .rough-node .label text,#chatgpt-mermaid-_r_2fk_ .node .label text,#chatgpt-mermaid-_r_2fk_ .image-shape .label,#chatgpt-mermaid-_r_2fk_ .icon-shape .label{text-anchor:middle;}#chatgpt-mermaid-_r_2fk_ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#chatgpt-mermaid-_r_2fk_ .rough-node .label,#chatgpt-mermaid-_r_2fk_ .node .label,#chatgpt-mermaid-_r_2fk_ .image-shape .label,#chatgpt-mermaid-_r_2fk_ .icon-shape .label{text-align:center;}#chatgpt-mermaid-_r_2fk_ .node.clickable{cursor:pointer;}#chatgpt-mermaid-_r_2fk_ .root .anchor path{fill:rgb(143, 143, 143)!important;stroke-width:0;stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_2fk_ .arrowheadPath{fill:rgb(143, 143, 143);}#chatgpt-mermaid-_r_2fk_ .edgePath .path{stroke:rgb(143, 143, 143);stroke-width:1px;}#chatgpt-mermaid-_r_2fk_ .flowchart-link{stroke:rgb(143, 143, 143);fill:none;}#chatgpt-mermaid-_r_2fk_ .edgeLabel{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_2fk_ .edgeLabel p{background-color:rgb(252, 252, 252);}#chatgpt-mermaid-_r_2fk_ .edgeLabel rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_2fk_ .labelBkg{background-color:rgba(252, 252, 252, 0.5);}#chatgpt-mermaid-_r_2fk_ .cluster rect{fill:rgb(243, 243, 243);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_2fk_ .cluster text{fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2fk_ .cluster span{color:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2fk_ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;font-size:12px;background:rgb(243, 243, 243);border:1px solid rgba(0, 0, 0, 0.1);border-radius:2px;pointer-events:none;z-index:100;}#chatgpt-mermaid-_r_2fk_ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:rgb(13, 13, 13);}#chatgpt-mermaid-_r_2fk_ rect.text{fill:none;stroke-width:0;}#chatgpt-mermaid-_r_2fk_ .icon-shape,#chatgpt-mermaid-_r_2fk_ .image-shape{background-color:rgb(252, 252, 252);text-align:center;}#chatgpt-mermaid-_r_2fk_ .icon-shape p,#chatgpt-mermaid-_r_2fk_ .image-shape p{background-color:rgb(252, 252, 252);padding:2px;}#chatgpt-mermaid-_r_2fk_ .icon-shape .label rect,#chatgpt-mermaid-_r_2fk_ .image-shape .label rect{opacity:0.5;background-color:rgb(252, 252, 252);fill:rgb(252, 252, 252);}#chatgpt-mermaid-_r_2fk_ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#chatgpt-mermaid-_r_2fk_ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#chatgpt-mermaid-_r_2fk_ .node .neo-node{stroke:rgb(83, 154, 248);}#chatgpt-mermaid-_r_2fk_ [data-look=”neo”].node rect,#chatgpt-mermaid-_r_2fk_ [data-look=”neo”].cluster rect,#chatgpt-mermaid-_r_2fk_ [data-look=”neo”].node polygon{stroke:url(#chatgpt-mermaid-_r_2fk_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_2fk_ [data-look=”neo”].swimlane.cluster rect{filter:none;}#chatgpt-mermaid-_r_2fk_ [data-look=”neo”].node path{stroke:url(#chatgpt-mermaid-_r_2fk_-gradient);stroke-width:1px;}#chatgpt-mermaid-_r_2fk_ [data-look=”neo”].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_2fk_ [data-look=”neo”].node .neo-line path{stroke:rgb(83, 154, 248);filter:none;}#chatgpt-mermaid-_r_2fk_ [data-look=”neo”].node circle{stroke:url(#chatgpt-mermaid-_r_2fk_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_2fk_ [data-look=”neo”].node circle .state-start{fill:#000000;}#chatgpt-mermaid-_r_2fk_ [data-look=”neo”].icon-shape .icon{fill:url(#chatgpt-mermaid-_r_2fk_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_2fk_ [data-look=”neo”].icon-shape .icon-neo path{stroke:url(#chatgpt-mermaid-_r_2fk_-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#chatgpt-mermaid-_r_2fk_ .node text{font-size:14px;font-weight:600;letter-spacing:normal;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_2fk_ .edgeLabels text{font-size:13px;font-weight:600;letter-spacing:-0.08px;fill:rgb(0, 79, 153);}#chatgpt-mermaid-_r_2fk_ .node tspan[font-weight=”normal”],#chatgpt-mermaid-_r_2fk_ .edgeLabels tspan[font-weight=”normal”]{font-weight:600;}#chatgpt-mermaid-_r_2fk_ .edgeLabel .label rect{opacity:1;rx:13px;ry:13px;fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-width:1px;}#chatgpt-mermaid-_r_2fk_ .node rect,#chatgpt-mermaid-_r_2fk_ .node circle,#chatgpt-mermaid-_r_2fk_ .node ellipse,#chatgpt-mermaid-_r_2fk_ .node polygon,#chatgpt-mermaid-_r_2fk_ .node path{fill:rgb(229, 243, 255);stroke:rgba(0, 0, 0, 0.1);stroke-width:1px;}#chatgpt-mermaid-_r_2fk_ .node rect{rx:16px;ry:16px;}#chatgpt-mermaid-_r_2fk_ .mermaid-decision .label-container{fill:rgb(245, 250, 255);stroke:rgb(206, 219, 229);stroke-dasharray:2,2;}#chatgpt-mermaid-_r_2fk_ .edgePaths .flowchart-link{stroke:rgb(143, 143, 143);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;}#chatgpt-mermaid-_r_2fk_ .marker{fill:rgb(143, 143, 143);stroke:rgb(143, 143, 143);}#chatgpt-mermaid-_r_2fk_ :root{–mermaid-font-family:-apple-system-body,ui-sans-serif,-apple-system,system-ui,”Segoe UI”,Helvetica,”Apple Color Emoji”,Arial,sans-serif,”Segoe UI Emoji”,”Segoe UI Symbol”;}Who are you?What may you access?Which application?Authorized connectionApplication responseAuthenticationAuthorizationRouting
When learning Pomerium, return to these questions whenever a configuration or architecture becomes complicated.
If you understand how identity is established, how policy decisions are made, how requests are routed, and how the system behaves during failure, you understand the essential Pomerium operating model.
Course completion
You have now covered all 28 chapters of the learning blueprint, progressing from the foundational security domain through Pomerium architecture, installation, configuration, practical deployment, advanced capabilities, operations, and assessment.
The most valuable next step is to complete the Chapter 15 lab and Chapter 20 project on a real machine, deliberately test both authorized and denied access, and practice recovering from failures. The tutorial provides the procedures and expected behavior; a successful execution in your own environment is what turns that knowledge into practical proficiency.
I’m Rajesh Kumar, a DevOps, SRE, DevSecOps, Cloud, and Platform Engineering expert passionate about sharing practical knowledge, real-world experiences, and industry best practices. I have worked at Cotocus and regularly write about technology, travel, investing, health, product reviews, and digital marketing through my various platforms.
I publish technical articles at DevOps School, travel stories at Holiday Landmark, stock market insights at Stocks Mantra, health and fitness guidance at My Medic Plus, product reviews at TrueReviewNow, and SEO and digital marketing strategies at Wizbrand.
Find Trusted Cardiac Hospitals
Compare heart hospitals by city and services — all in one place.
Explore Hospitals