The mental model
Authentik / Keycloak: full IdP. Users are stored in the IdP's database; the IdP issues OIDC tokens.
Dex: protocol bridge. Users live in an upstream system; Dex's job is "give me OIDC for these LDAP / GitHub / etc. users." Small, stateless (except for refresh tokens), no user management UI.
If you already have a user directory and just need OIDC in front of it, Dex is dramatically lighter than running a full IdP.
Install via Docker
docker run -d \
--name dex \
--restart unless-stopped \
-p 127.0.0.1:5556:5556 \
-v ./config.yaml:/etc/dex/config.yaml:ro \
ghcr.io/dexidp/dex:latest dex serve /etc/dex/config.yaml
Configure with GitHub as the upstream
# config.yaml
issuer: https://dex.example.com
storage:
type: sqlite3
config:
file: /var/dex/dex.db
web:
http: 0.0.0.0:5556
allowedOrigins: ["*"]
connectors:
- type: github
id: github
name: GitHub
config:
clientID: <github-oauth-client-id>
clientSecret: <github-oauth-secret>
redirectURI: https://dex.example.com/callback
orgs:
- name: myorg
teams: [ "platform", "engineering" ]
loadAllGroups: true
teamNameField: slug
- type: ldap
id: ldap
name: Corporate LDAP
config:
host: ldap.example.com:636
bindDN: cn=dex,ou=svc,dc=example,dc=com
bindPW: <bind-password>
usernamePrompt: Email
userSearch:
baseDN: ou=People,dc=example,dc=com
filter: "(objectClass=person)"
username: mail
idAttr: uid
emailAttr: mail
nameAttr: cn
groupSearch:
baseDN: ou=Groups,dc=example,dc=com
filter: "(objectClass=groupOfNames)"
userMatchers:
- userAttr: DN
groupAttr: member
nameAttr: cn
staticClients:
- id: kubernetes
name: Kubernetes cluster
secret: <random-secret>
redirectURIs:
- http://localhost:8000 # for kubectl oidc-login
- http://localhost:18000
- id: argocd
name: Argo CD
secret: <random-secret>
redirectURIs:
- https://argocd.example.com/auth/callback
- https://argocd.example.com/applications
- id: grafana
name: Grafana
secret: <random-secret>
redirectURIs:
- https://grafana.example.com/login/generic_oauth
oauth2:
skipApprovalScreen: true
alwaysShowLoginScreen: false
The Kubernetes kubectl use case
For "OIDC-authenticated kubectl access for human users," Dex is the canonical bridge:
# Configure the K8s API server with OIDC
# Add to kube-apiserver flags:
# --oidc-issuer-url=https://dex.example.com
# --oidc-client-id=kubernetes
# --oidc-username-claim=email
# --oidc-groups-claim=groups
On the user's machine:
# Install kubelogin (the OIDC kubectl plugin)
go install github.com/int128/kubelogin/cmd/kubectl-oidc_login@latest
# kubeconfig entry
- name: dex-user
user:
exec:
command: kubectl
apiVersion: client.authentication.k8s.io/v1beta1
args:
- oidc-login
- get-token
- --oidc-issuer-url=https://dex.example.com
- --oidc-client-id=kubernetes
- --oidc-client-secret=<same as in dex config>
- --oidc-extra-scope=email,groups
# Use
kubectl --user=dex-user get pods
# Browser opens to dex → GitHub login → redirects back → kubectl gets token
RBAC bindings in K8s reference the user's email or GitHub team names; access is auto-managed by the source identity system.
For other apps
Any OIDC client can use Dex. Examples that already work:
- Argo CD — built-in OIDC integration; point at Dex (see Argo CD tutorial)
- Grafana — OIDC auth, group-mapped roles
- Harbor — OIDC for container registry
- cert-manager — OIDC for ACME challenges with external account binding
- OpenShift — uses Dex as its OAuth flow component
Dex vs alternatives
- Authentik (see that tutorial) — full IdP with own user store + UI + flows. Heavier; more features; better for "I want self-hosted SSO with its own user management."
- Keycloak — Red Hat's heavyweight IdP; massive feature surface; Java-based; heavy operationally. Picks up where Dex stops if you need full IdP features.
- Gluu, FusionAuth, ZITADEL — other full-featured IdPs; all heavier than Dex.
- Plain reverse-proxy + oauth2-proxy / forward-auth — for "I just need to put SSO in front of one app," sometimes simpler than running an IdP at all.
When Dex is the right pick
- You have an existing identity source (LDAP, GitHub org, SAML IdP) and just need OIDC tokens from it.
- Kubernetes + kubectl OIDC access is the immediate driver.
- You want a small + stateless OIDC bridge, not a full IdP with its own user database.
When it isn't
- You don't have an upstream IdP — Dex doesn't store users itself. Authentik / Keycloak is the right shape.
- You need MFA / passkey / fine-grained policy — Dex passes through upstream auth; multi-factor lives at the upstream.
- You need a polished admin UI for user management — Dex has no such UI; everything is config files + the upstream.
Operational notes
- Storage backends: sqlite3 (default), Postgres / MySQL (scale), etcd / Kubernetes CRDs (for K8s-native). The Kubernetes backend is interesting: client / connector / token state lives as CRs.
- Refresh tokens are stored in the backend; rotation policies are configurable.
- TLS: terminate at the reverse proxy (Caddy / Traefik / nginx); Dex itself supports TLS too but external proxy is the common pattern.
For "I run Kubernetes and want my engineers to authenticate to it via GitHub teams without standing up Keycloak," Dex is the right size in 2026.