A tiny demo container that runs both:
- the exact
whoamibinary from thetraefik/whoamiimage, and - its own Traefik instance reverse-proxying to it,
so you can reconfigure Traefik freely for demos without touching an external proxy.
:8080 (host) :8081 (host)
│ │
┌───────▼──────────────────────────────▼───────┐
│ container │
│ Traefik :80 (web) ──▶ whoami 127.0.0.1:8000│
│ :8080 (dashboard/API) │
└───────────────────────────────────────────────┘
make up # build + start in the background
make logs # follow logs
make down # stopThen:
- whoami through Traefik: http://localhost:8080/
- Traefik dashboard: http://localhost:8081/dashboard/
A request to :8080 that comes back with X-Forwarded-* headers confirms it
traversed Traefik rather than hitting whoami directly:
curl -s localhost:8080/ | grep -i x-forwardedTraefik can enforce OIDC login in front of whoami using the
lukaszraczylo/traefikoidc
plugin (declared in traefik/traefik.yml). Config comes from environment
variables and is rendered into Traefik's dynamic config at container startup by
entrypoint.sh.
cp env.example .env
# edit .env: set REEF_ISSUER, REEF_CLIENT_ID, REEF_CLIENT_SECRET
make upRegister the callback with reef as a redirect URI: http://localhost:8080/oauth2/callback.
Now unauthenticated requests are challenged:
curl -sI localhost:8080/ # 302 -> ${REEF_ISSUER}/authorize...Open http://localhost:8080/ in a browser → reef login → back to whoami.
To demo the contrast, set OIDC_ENABLED=false in .env and make up again —
whoami is served directly (200, no redirect).
Set in .env (see env.example); passed through by docker-compose.yml.
| Var | Default | Meaning |
|---|---|---|
OIDC_ENABLED |
true |
false serves whoami with no auth. |
REEF_ISSUER |
— | OIDC issuer URL (discovery at …/.well-known/openid-configuration). Required when enabled. |
REEF_CLIENT_ID |
— | OAuth client id from reef. Required when enabled. |
REEF_CLIENT_SECRET |
— | OAuth client secret from reef. Required when enabled. |
OIDC_CALLBACK_URL |
/oauth2/callback |
Callback path Traefik handles (register the full URL with reef). |
OIDC_LOGOUT_URL |
/oauth2/logout |
RP-initiated logout path. |
OIDC_FORCE_HTTPS |
false |
Keep false for the local http:// demo. |
OIDC_LOG_LEVEL |
info |
debug / info / error. |
OIDC_SCOPES |
openid,profile,email |
Comma-separated OAuth scopes. |
OIDC_EXCLUDED_URLS |
— | Comma-separated paths that bypass auth. |
OIDC_SESSION_ENCRYPTION_KEY |
auto | Cookie key (≥32 bytes). Blank ⇒ generated once and persisted to the oidc-session-key volume so it survives restarts. |
WHOAMI_PORT |
8000 |
Internal port whoami listens on. |
WHOAMI_NAME |
reef-traefik-demo |
Name whoami reports. |
- Plugin download: on first start Traefik fetches the plugin source from GitHub — the container needs network egress.
OIDC_FORCE_HTTPSmust befalsefor the plain-http local demo, or the plugin buildshttps://redirect/callback URLs that won't resolve.- Issuer reachability: the browser and the container must reach
REEF_ISSUERat the same URL (the tokenissmust match). If reef runs on your host, usehttp://host.docker.internal:<port>and set reef's issuer to match — plainlocalhostmeans different things inside vs outside the container. - "Critical session error" (HTTP 500): the plugin can't decode a session
cookie because the session key changed since the cookie was set, and it won't
clear the bad cookie itself. To recover: clear cookies for
localhost:8080(or use a fresh/incognito window), then reload. The key is now persisted across restarts (oidc-session-keyvolume) so this shouldn't recur; if you rotate it deliberately (changeOIDC_SESSION_ENCRYPTION_KEY, ordocker compose down -v), clear cookies again.
- Static config lives in
traefik/traefik.yml(entrypoints, dashboard, the plugin declaration). It's bind-mounted; edits apply aftermake restart. - Dynamic config (router + service + OIDC middleware) is generated at
startup by
entrypoint.shfrom the environment, so change behaviour via.envand re-runmake up. To alter the routing structure itself, edit the heredoc inentrypoint.sh.
entrypoint.sh renders /etc/traefik/dynamic/dynamic.yml from the environment,
starts whoami in the background, then execs Traefik in the foreground (so
Traefik is PID 1 and shuts down cleanly on docker stop). The image is built by
copying the whoami binary onto the official traefik base image — see
Dockerfile.