Last updated: 2026-05-17
This guide covers the public Docker-based deployment assets in this repository.
If you are still deciding between the base install, Redis, production hardening, examples, or the separate source-build track, start with deployment-paths.md first.
certs/aspnetapp.pfx or a reverse proxy / load balancer that terminates TLS upstreamThe command examples below use docker compose. If your host still uses the legacy standalone binary, replace it with docker-compose. A first local run typically takes 3-5 minutes, depending on image pulls and startup time.
This guide is for the published Docker image path in the MrWho repository. Do not clone MrWhoOidc and do not replace docker compose up -d with docker compose -f docker-compose.yml up -d --build while following this guide.
mkdir -p "$HOME/src"
cd "$HOME/src"
git clone https://github.com/popicka70/MrWho.git
cd MrWho
bash ./scripts/generate-cert.sh localhost changeit
chmod 644 ./certs/aspnetapp.pfx
cp .env.example .env
# for the stock local path, edit at minimum:
# POSTGRES_PASSWORD
# BOOTSTRAP_TOKEN on a fresh empty database
# CERT_PASSWORD=changeit and OIDC_PUBLIC_BASE_URL=https://localhost:8443 already match the generated certificate and default local ports
# leave MAIL_* empty unless you plan to enable SMTP
# if you are reusing an existing local Docker volume and changed POSTGRES_PASSWORD,
# either keep the original password or reset the local database state first:
# docker compose down -v --remove-orphans
grep -q 'ghcr.io/popicka70/mrwhooidc:latest' docker-compose.yml && echo "published image compose file confirmed"
docker compose config | grep ghcr.io/popicka70/mrwhooidc:latest
docker compose up -d
# if the first bootstrap curl fails with a TLS or socket error,
# wait 5-10 seconds for HTTPS startup and retry the same request
curl -k -X POST https://localhost:8443/bootstrap \
-H "Content-Type: application/json" \
-H "X-Bootstrap-Token: ${BOOTSTRAP_TOKEN}" \
-d '{
"tenantSlug": "default",
"tenantName": "Default Tenant",
"adminEmail": "admin@example.com",
"adminPassword": "ChangeMeNow123!",
"adminName": "Administrator"
}'
# remove BOOTSTRAP_TOKEN from .env after bootstrap, then re-apply
docker compose up -d
If the image verification commands do not print ghcr.io/popicka70/mrwhooidc:latest, stop because you are not using the published Docker image compose path.
If MrWho already exists locally and you want a clean first-run test, clone into a different persistent directory or intentionally clean the existing checkout first so you do not mix old .env, certs/, or Docker state into the evaluation.
Check the deployment:
curl -k https://localhost:8443/t/default/.well-known/openid-configuration
curl -k -I https://localhost:8443/admin/clients
curl -k https://localhost:8443/t/default/jwks
bash ./scripts/health-check.sh https://localhost:8443 default
Expected discovery output includes fields such as issuer, authorization_endpoint, token_endpoint, and jwks_uri.
If you open https://localhost:8443/admin in a browser before trusting the generated local certificate, expect the normal self-signed certificate warning.
For a scenario chooser with use this when guidance, see deployment-paths.md.
docker compose up -d
Includes:
This base expects the backend container itself to serve HTTPS with a mounted PFX.
docker compose -f docker-compose.tls-termination.yml up -d
Includes:
Use this when the public https://... endpoint lives at the proxy and the backend should stay on private HTTP.
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
Adds:
docker compose -f docker-compose.yml -f docker-compose.redis.yml up -d
Adds:
You can compose this overlay on top of either docker-compose.yml or docker-compose.tls-termination.yml.
docker compose -f docker-compose.yml -f docker-compose.production.yml up -d
Adds:
You can compose this overlay on top of either docker-compose.yml or docker-compose.tls-termination.yml.
When a reverse proxy or cloud load balancer terminates TLS before traffic reaches the container, use the dedicated base file instead of the default docker-compose.yml path:
docker compose -f docker-compose.tls-termination.yml up -d
This deployment mode changes the backend assumptions:
:8080https://... via OIDC_PUBLIC_BASE_URLX-Forwarded-Proto, X-Forwarded-Host, and client IP informationcerts/aspnetapp.pfx mount and no CERT_PASSWORD are required for this fileRecommended .env choices for this path:
OIDC_PUBLIC_BASE_URL=https://auth.example.comFORWARDED_HEADERS_KNOWN_PROXY_* or FORWARDED_HEADERS_KNOWN_NETWORK_* set to trusted proxy valuesFORWARDED_HEADERS_ENFORCE_HOST_ALLOW_LIST=true when practicalFORWARDED_HEADERS_ALLOWED_HOST_0=auth.example.com when practicalUse FORWARDED_HEADERS_UNSAFE_TRUST_ALL=true only when proxy IP ranges are not stable and the backend is not directly reachable by clients.
You can still layer the existing overlays on top of this base:
docker compose -f docker-compose.tls-termination.yml -f docker-compose.redis.yml up -d
docker compose -f docker-compose.tls-termination.yml -f docker-compose.production.yml up -d
Validation for this mode is different from the local PFX path:
For a fresh production-style database, define BOOTSTRAP_TOKEN before startup. After the containers are healthy, call the bootstrap endpoint once:
curl -k -X POST https://localhost:8443/bootstrap \
-H "Content-Type: application/json" \
-H "X-Bootstrap-Token: ${BOOTSTRAP_TOKEN}" \
-d '{
"tenantSlug": "default",
"tenantName": "Default Tenant",
"adminEmail": "admin@example.com",
"adminPassword": "ChangeMeNow123!",
"adminName": "Administrator"
}'
After the initial bootstrap succeeds, remove BOOTSTRAP_TOKEN from .env and re-apply the containers so bootstrap is no longer exposed.
Post-bootstrap smoke tests for a fresh local install:
https://localhost:8443/t/default/.well-known/openid-configurationhttps://localhost:8443/admin/clientshttps://localhost:8443/t/default/jwkshttps://localhost:8443/jwksAnonymous requests to https://localhost:8443/admin/clients should redirect to the tenant login page before an administrator signs in.
If mrwho-oidc logs show password authentication failed for user "oidc" and PostgreSQL logs say the database directory already exists or initialization was skipped, the local PostgreSQL volume was created with different credentials. PostgreSQL only applies POSTGRES_PASSWORD when the data directory is first initialized. Either restore the previous password in .env or reset the local Docker state with docker compose down -v --remove-orphans, then start again.
If mrwho-oidc logs show Configured HTTPS certificate file '/https/aspnetapp.pfx' was not found, the container never saw the expected TLS certificate mount. Regenerate it with bash ./scripts/generate-cert.sh localhost changeit, run chmod 644 ./certs/aspnetapp.pfx on Linux/macOS, confirm ./certs:/https:ro is still present in docker-compose.yml, then restart the stack.
If you intentionally deployed with docker-compose.tls-termination.yml, this certificate warning should not apply because that base does not configure a local PFX.
If you deploy behind a cloud load balancer or reverse proxy, review the forwarded-header variables in .env.example.
The safest approach is:
FORWARDED_HEADERS_UNSAFE_TRUST_ALL=falseOIDC_PUBLIC_BASE_URL matches the externally visible HTTPS URLdocker-compose.tls-termination.yml when TLS ends at the proxy and the backend should not hold a local certificateMulti-tenancy is controlled by the installed platform license, not by a simple deployment toggle. Tenant-scoped endpoints use /t/{slug}.
See multitenancy-quick-reference.md.
POSTGRES_PASSWORDBOOTSTRAP_TOKEN removed after initial useAfter deployment, verify:
MAIL_ENABLED=truehttps://... URLs rather than backend http://... URLs