|
| 1 | +--- |
| 2 | +title: Self-Hosted LLM Behind Pomerium |
| 3 | +sidebar_label: Secure LLM WebUI |
| 4 | +lang: en-US |
| 5 | +keywords: |
| 6 | + - pomerium |
| 7 | + - access proxy |
| 8 | + - llm |
| 9 | + - open webui |
| 10 | + - authentication |
| 11 | + - authorization |
| 12 | + - ollama |
| 13 | +description: Secure a self-hosted LLM web interface (Open WebUI) behind Pomerium. |
| 14 | +--- |
| 15 | + |
| 16 | +# Self-Hosted LLM Behind Pomerium |
| 17 | + |
| 18 | +This guide shows how to run a self-hosted LLM web interface (e.g., [Open WebUI](https://github.com/open-webui/open-webui)) and protect it behind Pomerium. Similar to the [code-server guide](https://www.pomerium.com/docs/guides/code-server), this setup applies enterprise-grade access controls to a local LLM, while passing user identity information through trusted headers so the upstream application knows who’s accessing it. |
| 19 | + |
| 20 | +## Why Use Open WebUI? |
| 21 | + |
| 22 | +Open WebUI is a self-hosted interface for interacting with local or remote LLMs. Hosting it locally allows: |
| 23 | + |
| 24 | +- Private experimentation with different LLMs |
| 25 | +- Full control over data and configuration |
| 26 | +- Integration of custom features like retrieval augmentation, voice, or browsing |
| 27 | + |
| 28 | +## Why Pomerium? |
| 29 | + |
| 30 | +By placing Open WebUI behind Pomerium, you can: |
| 31 | + |
| 32 | +- Authenticate users with your existing IdP (e.g., Google, GitHub, corporate SSO). |
| 33 | +- Enforce policy-based access using user attributes like email domain. |
| 34 | +- Pass identity headers (e.g., `X-Pomerium-Claim-Email`) to the upstream app, letting it personalize the experience without separate logins. |
| 35 | + |
| 36 | +Pomerium injects trusted identity information, so Open WebUI can recognize authenticated users and skip its own password handling. |
| 37 | + |
| 38 | +## Overview |
| 39 | + |
| 40 | +1. **Run Open WebUI** in Docker. |
| 41 | +2. **Configure Pomerium Zero** to secure it. |
| 42 | +3. **Access the LLM UI** through a protected URL. Pomerium handles auth, passes user identity, and ensures only allowed users access the LLM. |
| 43 | + |
| 44 | +## Prerequisites |
| 45 | + |
| 46 | +- [Pomerium Zero](https://console.pomerium.app/create-account) account |
| 47 | +- Docker and Docker Compose |
| 48 | +- A machine capable of running your chosen LLM backend |
| 49 | + |
| 50 | +For GPU acceleration, follow Open WebUI’s CUDA instructions. |
| 51 | + |
| 52 | +## Configure Pomerium Zero |
| 53 | + |
| 54 | +### Create a Policy |
| 55 | + |
| 56 | +Define who can access the LLM interface. For example, restrict access to your email domain: |
| 57 | + |
| 58 | +1. In the Zero Console, go to **Policies** → **New Policy**. |
| 59 | +2. Name it **LLM Access**. |
| 60 | +3. Add an allow condition: `Domain` equals `example.com`. |
| 61 | + |
| 62 | +This ensures only authenticated users with `example.com` email addresses can access the route. |
| 63 | + |
| 64 | +### Create a Route |
| 65 | + |
| 66 | +Map an external domain to the internal Open WebUI service. For example: |
| 67 | + |
| 68 | + |
| 69 | + |
| 70 | +1. In the Zero Console, go to **Routes** → **New Route**. |
| 71 | +2. Name: `LLM Access` |
| 72 | +3. From: `https://llm.your-domain.pomerium.app` |
| 73 | +4. To: `http://openwebui:8080` |
| 74 | +5. Policies: Select **LLM Access** |
| 75 | + |
| 76 | +Save the route. Now only authorized users can reach `llm.your-domain.pomerium.app`. |
| 77 | + |
| 78 | +### Enable Websockets |
| 79 | + |
| 80 | +LLM UIs often use streaming responses: |
| 81 | + |
| 82 | + |
| 83 | + |
| 84 | +- Under **Timeouts**, toggle **Allow Websockets** to `On`. |
| 85 | + |
| 86 | +### Preserve Host Header & Pass Identity |
| 87 | + |
| 88 | +Enable these settings so Open WebUI receives the correct host header and identity claims: |
| 89 | + |
| 90 | + |
| 91 | + |
| 92 | +- **Pass Identity Headers**: On |
| 93 | +- **Preserve Host Header**: On |
| 94 | + |
| 95 | +This ensures Pomerium forwards identity info like `X-Pomerium-Claim-Email`. In **Settings**, you can define how claims map to headers: |
| 96 | + |
| 97 | + |
| 98 | + |
| 99 | +With these settings, Open WebUI trusts the identity headers and can attribute actions to the authenticated user. |
| 100 | + |
| 101 | +## Example Docker Compose |
| 102 | + |
| 103 | +Use placeholders for secrets and adjust `WEBUI_URL` to your external route. If you trust Pomerium’s headers, set `WEBUI_AUTH=False` in Open WebUI. |
| 104 | + |
| 105 | +```yaml |
| 106 | +version: '3.9' |
| 107 | +services: |
| 108 | + pomerium: |
| 109 | + image: pomerium/pomerium:v0.27.2 |
| 110 | + ports: |
| 111 | + - 443:443 |
| 112 | + restart: always |
| 113 | + environment: |
| 114 | + POMERIUM_ZERO_TOKEN: '<YOUR_CLUSTER_TOKEN>' |
| 115 | + XDG_CACHE_HOME: /var/cache |
| 116 | + volumes: |
| 117 | + - pomerium-cache:/var/cache |
| 118 | + networks: |
| 119 | + main: |
| 120 | + aliases: |
| 121 | + - authenticate.<YOUR_CLUSTER_SUBDOMAIN>.pomerium.app |
| 122 | + |
| 123 | + openwebui: |
| 124 | + image: ghcr.io/open-webui/open-webui:main |
| 125 | + environment: |
| 126 | + HOST: '0.0.0.0' |
| 127 | + OLLAMA_HOST: '0.0.0.0' |
| 128 | + WEBUI_URL: 'https://llm.your-domain.pomerium.app' |
| 129 | + WEBUI_AUTH_TRUSTED_EMAIL_HEADER: 'X-Pomerium-Claim-Email' |
| 130 | + WEBUI_AUTH: 'False' |
| 131 | + ports: |
| 132 | + - 3000:8080 |
| 133 | + volumes: |
| 134 | + - open-webui-data:/app/backend/data |
| 135 | + restart: always |
| 136 | + networks: |
| 137 | + main: {} |
| 138 | + |
| 139 | +networks: |
| 140 | + main: |
| 141 | + |
| 142 | +volumes: |
| 143 | + pomerium-cache: |
| 144 | + open-webui-data: |
| 145 | +``` |
| 146 | +
|
| 147 | +Replace `<YOUR_CLUSTER_TOKEN>` and `YOUR_CLUSTER_SUBDOMAIN`. For GPU support, see Open WebUI docs. |
| 148 | + |
| 149 | +## Run and Access |
| 150 | + |
| 151 | +```bash |
| 152 | +docker compose up -d |
| 153 | +``` |
| 154 | + |
| 155 | +Visit `https://llm.your-domain.pomerium.app`. After authenticating via your IdP, Pomerium routes you to Open WebUI with identity headers included. |
| 156 | + |
| 157 | +## Test the LLM |
| 158 | + |
| 159 | +- Load a model, prompt it, and interact. |
| 160 | +- Your identity is passed through; Open WebUI sees your user email. No extra passwords needed. |
| 161 | +- The entire session is protected behind Pomerium’s authentication and authorization. |
| 162 | + |
| 163 | +### Example of a Secured UI |
| 164 | + |
| 165 | + |
| 166 | + |
| 167 | +## Next Steps |
| 168 | + |
| 169 | +- Refine policies: Add group-based rules or restrict certain endpoints. |
| 170 | +- Integrate different backends: Adjust `OLLAMA_BASE_URL` or `OPENAI_API_KEY`. |
| 171 | +- Add more routes behind Pomerium to scale your secure environment. |
| 172 | + |
| 173 | +You’ve successfully secured a self-hosted LLM WebUI using Pomerium, with identity-aware access control and automatic user recognition upstream. |
0 commit comments