{"id":2099,"date":"2026-04-05T20:07:08","date_gmt":"2026-04-05T18:07:08","guid":{"rendered":"https:\/\/cln.io\/blog\/?p=2099"},"modified":"2026-04-05T20:07:09","modified_gmt":"2026-04-05T18:07:09","slug":"open-webui-authentik-sso-with-automatic-admin-role-mapping","status":"publish","type":"post","link":"https:\/\/cln.io\/blog\/open-webui-authentik-sso-with-automatic-admin-role-mapping\/","title":{"rendered":"Open WebUI + Authentik SSO with automatic admin role mapping"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Notes on getting Open WebUI working with Authentik SSO on a Docker Swarm setup with Traefik. Took me a bit to get all the pieces right, the main gotchas are the JWT signing key (Authentik doesn\u2019t set one by default &#x1f926;&#x1f3fb;&#x200d;&#x2640;&#xfe0f;) and the self-signed cert handling inside the container.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What you need<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Authentik running as your identity provider<\/li>\n\n\n\n<li>Traefik as reverse proxy (with TLS)<\/li>\n\n\n\n<li>Open WebUI<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Part 1: Authentik setup<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. Create scope mappings<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Authentik has <a href=\"https:\/\/integrations.goauthentik.io\/miscellaneous\/open-webui\/\">official integration docs for Open WebUI<\/a> which cover the basics. Below is the role mapping part which isn\u2019t covered there.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There are two approaches to giving SSO users admin in Open WebUI. You can set up both in Authentik and switch between them on the Open WebUI side.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Option A: hardcoded admin for everyone<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The simplest approach. Every SSO user gets admin. Create a scope mapping in <strong>Customization \u2192 Property Mappings \u2192 Create \u2192 Scope Mapping<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Name:<\/strong> Open WebUI Admin Role<\/li>\n\n\n\n<li><strong>Scope name:<\/strong> roles<\/li>\n\n\n\n<li><strong>Expression:<\/strong><\/li>\n<\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">return {\"roles\": \"admin\"}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then on the Open WebUI side you use <code>OAUTH_ROLES_CLAIM: \"roles\"<\/code> and <code>OAUTH_ADMIN_ROLES: \"admin\"<\/code>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Option B: LDAP group-based admin<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Cleaner if you already have LDAP groups, only users in a specific group get admin. No custom scope mapping needed here! When you request the <code>groups<\/code> scope in OIDC, Authentik already returns the user\u2019s groups in the token natively.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On the Open WebUI side:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  OAUTH_SCOPES: \"openid profile email groups\"\n  ENABLE_OAUTH_ROLE_MANAGEMENT: \"true\"\n  OAUTH_ROLES_CLAIM: \"groups\"\n  OAUTH_ADMIN_ROLES: \"chat_admin\"<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Replace <code>chat_admin<\/code> with whatever your LDAP group is called.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In my case I have a <code>chat_admin<\/code> group in LDAP with alice as a member. Authentik syncs it via the LDAP source, and Open WebUI checks for it in the JWT groups claim. Bob is not in <code>chat_admin<\/code>, so he gets regular user &#x1f44c;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. Create an OAuth2\/OpenID provider<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Go to <strong>Applications \u2192 Providers \u2192 Create \u2192 OAuth2\/OpenID Provider<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Name:<\/strong> Open WebUI Provider<\/li>\n\n\n\n<li><strong>Authorization flow:<\/strong> default-provider-authorization-implicit-consent<\/li>\n\n\n\n<li><strong>Client type:<\/strong> Confidential<\/li>\n\n\n\n<li><strong>Client ID:<\/strong> openwebui (or leave the random default Authentik generates)<\/li>\n\n\n\n<li><strong>Client Secret:<\/strong> (Authentik generates one by default)<\/li>\n\n\n\n<li><strong>Redirect URIs:<\/strong> https:\/\/chat.yourdomain.com\/oauth\/oidc\/callback<\/li>\n\n\n\n<li><strong>Signing Key:<\/strong> select any available signing key (don\u2019t skip this!)<\/li>\n\n\n\n<li><strong>Scopes:<\/strong> openid, email, profile + the roles mapping you created (Option A) or just groups (Option B, built-in)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. Create an application<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Go to <strong>Applications \u2192 Applications \u2192 Create<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Name:<\/strong> Open WebUI<\/li>\n\n\n\n<li><strong>Slug:<\/strong> openwebui<\/li>\n\n\n\n<li><strong>Provider:<\/strong> Open WebUI Provider<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Part 2: Open WebUI config<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The env vars you need in your docker compose \/ stack. Pick either Option A or B for the role mapping:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">environment:\n  # SSO via Authentik (OIDC)\n  ENABLE_OAUTH_SIGNUP: \"true\"\n  OAUTH_PROVIDER_NAME: \"Authentik\"\n  OPENID_PROVIDER_URL: \"https:\/\/auth.yourdomain.com\/application\/o\/openwebui\/.well-known\/openid-configuration\"\n  OAUTH_CLIENT_ID: \"openwebui\"\n  OAUTH_CLIENT_SECRET: \"your-client-secret\"\n  OAUTH_SCOPES: \"openid profile email groups\"\n  WEBUI_URL: \"https:\/\/chat.yourdomain.com\"\n  # Role management - pick one:\n  ENABLE_OAUTH_ROLE_MANAGEMENT: \"true\"\n  # Option A: hardcoded admin (via Authentik \"roles\" scope mapping)\n  # OAUTH_ROLES_CLAIM: \"roles\"\n  # OAUTH_ADMIN_ROLES: \"admin\"\n  # Option B: LDAP group-based admin\n  OAUTH_ROLES_CLAIM: \"groups\"\n  OAUTH_ADMIN_ROLES: \"chat_admin\"\n  # Ensure env vars take priority over DB config\n  ENABLE_PERSISTENT_CONFIG: \"False\"\n  RESET_CONFIG_ON_START: \"True\"\n  # Hide local login form (SSO only)\n  ENABLE_LOGIN_FORM: \"False\"<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Important: <code>ENABLE_OAUTH_ROLE_MANAGEMENT<\/code> is needed for both approaches. Without it, Open WebUI ignores the claims entirely. And the scope in <code>OAUTH_SCOPES<\/code> needs to match: use <code>roles<\/code> for Option A, <code>groups<\/code> for Option B (or just include both).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If this isn\u2019t a fresh install, you\u2019ll probably also want <code>ENABLE_PERSISTENT_CONFIG: \"False\"<\/code> and <code>RESET_CONFIG_ON_START: \"True\"<\/code>. Open WebUI saves config to its database on first boot, and after that the DB values take priority over env vars. So if you\u2019re adding SSO to an existing setup, your new env vars will just get ignored unless you set these. Fresh installs don\u2019t have this problem since there\u2019s nothing in the DB yet.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">First login: create an admin first!<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Open WebUI requires the very first account to be created locally. SSO users can\u2019t log in until that initial admin exists. So before clicking \u201cSign in with Authentik\u201d, go to the sign up page and create a local admin account. After that, SSO logins work.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also set the default user role to \u201cuser\u201d instead of \u201cpending\u201d in <strong>Admin Panel \u2192 Settings \u2192 General<\/strong>, otherwise every SSO user has to be manually activated &#x1f605;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The SSO flow<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Heres what it looks like end to end:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Open WebUI login page with the \u201cContinue with Authentik\u201d button:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"800\" src=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-owui-login-1.png\" alt=\"Open WebUI login page showing Continue with Authentik button\" class=\"wp-image-2104\" srcset=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-owui-login-1.png 1280w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-owui-login-1-300x188.png 300w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-owui-login-1-1024x640.png 1024w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-owui-login-1-768x480.png 768w\" sizes=\"auto, (max-width: 1280px) 100vw, 1280px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Clicking it redirects to Authentik, showing the app name \u201cOpen WebUI\u201d:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"800\" src=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-authentik-login-1.png\" alt=\"Authentik login page\" class=\"wp-image-2105\" srcset=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-authentik-login-1.png 1280w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-authentik-login-1-300x188.png 300w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-authentik-login-1-1024x640.png 1024w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-authentik-login-1-768x480.png 768w\" sizes=\"auto, (max-width: 1280px) 100vw, 1280px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Enter username, then password:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"800\" src=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-authentik-password-1.png\" alt=\"Authentik password prompt\" class=\"wp-image-2106\" srcset=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-authentik-password-1.png 1280w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-authentik-password-1-300x188.png 300w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-authentik-password-1-1024x640.png 1024w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-authentik-password-1-768x480.png 768w\" sizes=\"auto, (max-width: 1280px) 100vw, 1280px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And you land in Open WebUI, logged in as Alice with admin role &#x1f389;<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"800\" src=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-owui-loggedin-1.png\" alt=\"Open WebUI dashboard after SSO login\" class=\"wp-image-2107\" srcset=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-owui-loggedin-1.png 1280w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-owui-loggedin-1-300x188.png 300w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-owui-loggedin-1-1024x640.png 1024w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/blog-owui-loggedin-1-768x480.png 768w\" sizes=\"auto, (max-width: 1280px) 100vw, 1280px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Proof: LDAP group-based admin mapping<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To show the group-based approach actually works. Here\u2019s the admin users overview with alice (in <code>chat_admin<\/code> LDAP group) as ADMIN and bob (not in the group) as USER:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"900\" src=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/sso_groups_users_overview.png\" alt=\"Open WebUI admin users overview showing Alice as ADMIN and Bob as USER\" class=\"wp-image-2124\" srcset=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/sso_groups_users_overview.png 1280w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/sso_groups_users_overview-300x211.png 300w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/sso_groups_users_overview-1024x720.png 1024w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/sso_groups_users_overview-768x540.png 768w\" sizes=\"auto, (max-width: 1280px) 100vw, 1280px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The LDAP group membership flows all the way from OpenLDAP \u2192 Authentik sync \u2192 JWT groups claim \u2192 Open WebUI role assignment &#x1f44c;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Disable the login form<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Once SSO is working, you probably don\u2019t want the local email\/password form cluttering the login page. Set <code>ENABLE_LOGIN_FORM: \"False\"<\/code> to hide it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Before (form + SSO button):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"900\" src=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/login_before.png\" alt=\"Login page with email\/password form and SSO button\" class=\"wp-image-2128\" srcset=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/login_before.png 1280w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/login_before-300x211.png 300w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/login_before-1024x720.png 1024w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/login_before-768x540.png 768w\" sizes=\"auto, (max-width: 1280px) 100vw, 1280px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">After (SSO only):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"900\" src=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/login_after.png\" alt=\"Login page with only the Continue with Authentik button\" class=\"wp-image-2129\" srcset=\"https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/login_after.png 1280w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/login_after-300x211.png 300w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/login_after-1024x720.png 1024w, https:\/\/cln.io\/blog\/wp-content\/uploads\/2026\/03\/login_after-768x540.png 768w\" sizes=\"auto, (max-width: 1280px) 100vw, 1280px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Much cleaner. Just the one button.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Gotchas<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Don\u2019t forget the signing key<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When creating the OAuth2 provider, make sure you select a <strong>Signing Key<\/strong>. Authentik ships with a self-signed certificate that works fine for this. If you skip it, the JWKS endpoint returns <code>{}<\/code> and Open WebUI blows up with <code>ValueError: Invalid key set format<\/code> during the callback.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Self-signed certs<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If you\u2019re running with self-signed certs (like on a localhost dev setup), Open WebUI\u2019s OIDC discovery calls to Authentik will fail with SSL errors. The container needs to trust your cert or have SSL verification disabled.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a proper setup, you can mount your CA bundle and set <code>SSL_CERT_FILE: \"\/run\/secrets\/your_ca_bundle\"<\/code> so the container trusts your internal CA. I wrote a whole post about that: <a href=\"https:\/\/cln.io\/blog\/getting-applications-to-work-with-ca-bundles-onprem-self-hosted-with-dockers\/\">getting applications to work with CA bundles<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a quick dev setup tho, I just used a custom entrypoint script that patches Python\u2019s SSL context and adds Authentik\u2019s hostname to \/etc\/hosts:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#!\/bin\/sh\nset -e\n# Resolve auth.yourdomain.com to Traefik inside the container\nIP=$(python3 -c \"import socket; print(socket.gethostbyname('traefik_traefik'))\")\necho \"$IP auth.yourdomain.com\" >> \/etc\/hosts\n# Patch SSL for self-signed certs\nSITE_DIR=$(python3 -c \"import sysconfig; print(sysconfig.get_paths()['purelib'])\")\ncat > \"$SITE_DIR\/sitecustomize.py\" << 'PYEOF'\nimport ssl as _ssl\n_orig = _ssl.create_default_context\ndef _patched(*a, **kw):\n    ctx = _orig(*a, **kw)\n    ctx.check_hostname = False\n    ctx.verify_mode = _ssl.CERT_NONE\n    return ctx\n_ssl.create_default_context = _patched\nPYEOF\ncd \/app\/backend\nexec bash start.sh<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Obviously don\u2019t do this in production &#x1f643;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Role is set on every login<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">With <code>ENABLE_OAUTH_ROLE_MANAGEMENT<\/code> enabled, Open WebUI updates the user\u2019s role on every SSO login based on the claim. So if you change the scope mapping in Authentik or add\/remove someone from the LDAP group, it takes effect on the next login, no need to manually update roles in Open WebUI.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Full docker compose example<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Docker Swarm stack with Traefik labels (using LDAP group-based admin):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"yaml\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">version: \"3.8\"\nservices:\n  openwebui:\n    image: ghcr.io\/open-webui\/open-webui:main\n    environment:\n      OPENAI_API_BASE_URL: \"http:\/\/litellm:4000\"\n      OPENAI_API_KEY: \"your-litellm-key\"\n      OLLAMA_BASE_URL: \"\"\n      ENABLE_OAUTH_SIGNUP: \"true\"\n      OAUTH_PROVIDER_NAME: \"Authentik\"\n      OPENID_PROVIDER_URL: \"https:\/\/auth.yourdomain.com\/application\/o\/openwebui\/.well-known\/openid-configuration\"\n      OAUTH_CLIENT_ID: \"openwebui\"\n      OAUTH_CLIENT_SECRET: \"your-client-secret\"\n      OAUTH_SCOPES: \"openid profile email groups\"\n      WEBUI_URL: \"https:\/\/chat.yourdomain.com\"\n      WEBUI_SECRET_KEY: \"change-me\"\n      ENABLE_OAUTH_ROLE_MANAGEMENT: \"true\"\n      # Option A: hardcoded admin for all SSO users\n      #OAUTH_ROLES_CLAIM: \"roles\"\n      #OAUTH_ADMIN_ROLES: \"admin\"\n      # Option B: LDAP group-based admin\n      OAUTH_ROLES_CLAIM: \"groups\"\n      OAUTH_ADMIN_ROLES: \"chat_admin\"\n      ENABLE_PERSISTENT_CONFIG: \"False\"\n      RESET_CONFIG_ON_START: \"True\"\n      ENABLE_LOGIN_FORM: \"False\"\n    volumes:\n      - openwebui_data:\/app\/backend\/data\n    networks:\n      - proxy\n    deploy:\n      labels:\n        - \"traefik.enable=true\"\n        - \"traefik.http.routers.openwebui.rule=Host(`chat.yourdomain.com`)\"\n        - \"traefik.http.routers.openwebui.entrypoints=websecure\"\n        - \"traefik.http.routers.openwebui.tls=true\"\n        - \"traefik.http.services.openwebui.loadbalancer.server.port=8080\"\nvolumes:\n  openwebui_data:\nnetworks:\n  proxy:\n    external: true<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"has-text-color has-link-color wp-elements-967424fa0fe7e660ff6075a5cd01473a wp-block-paragraph\" style=\"color:#9ca3af;font-size:14px\">This post was written with the help of Claude (Opus 4), Anthropic's AI assistant.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Notes on getting Open WebUI working with Authentik SSO on a Docker Swarm setup with Traefik. Took me a bit to get all the pieces right, the main gotchas are the JWT signing key (Authentik [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2110,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[60,63,26,61],"tags":[],"class_list":["post-2099","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","category-authentication","category-it","category-llm"],"_links":{"self":[{"href":"https:\/\/cln.io\/blog\/wp-json\/wp\/v2\/posts\/2099","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cln.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cln.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cln.io\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cln.io\/blog\/wp-json\/wp\/v2\/comments?post=2099"}],"version-history":[{"count":14,"href":"https:\/\/cln.io\/blog\/wp-json\/wp\/v2\/posts\/2099\/revisions"}],"predecessor-version":[{"id":2130,"href":"https:\/\/cln.io\/blog\/wp-json\/wp\/v2\/posts\/2099\/revisions\/2130"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cln.io\/blog\/wp-json\/wp\/v2\/media\/2110"}],"wp:attachment":[{"href":"https:\/\/cln.io\/blog\/wp-json\/wp\/v2\/media?parent=2099"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cln.io\/blog\/wp-json\/wp\/v2\/categories?post=2099"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cln.io\/blog\/wp-json\/wp\/v2\/tags?post=2099"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}