Get a local Modgud running, sign in for the first time, and verify the OAuth/OIDC endpoints respond — in under 10 minutes.
- Docker Desktop (or Docker Engine + Compose)
- A free host port 80 (the Modgud container serves both the API and the admin SPA same-origin)
- About 200 MB of disk for the container and PostgreSQL data
This quickstart uses the published image ghcr.io/cocoar-dev/modgud — you do not clone the repo or build anything. You copy the compose file below, save it, and start it.
For requirements beyond a quick local run, see Requirements. For a production deployment (HTTPS issuer, reverse proxy, Prometheus token), see First-time setup and Deployment.
Save the following as compose.yml in an empty directory:
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_PASSWORD: postgres
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 10
modgud:
image: ghcr.io/cocoar-dev/modgud:latest
container_name: modgud
environment:
ASPNETCORE_ENVIRONMENT: Development # local eval only — see Deployment for production
DbSettings__ConnectionString: "Host=postgres;Database=modgud;Username=postgres;Password=postgres;Keepalive=30"
AppUrl: "http://0.0.0.0:8081"
OpenIddict__DevelopmentMode: "true"
ports:
- "80:8081"
depends_on:
postgres:
condition: service_healthy
volumes:
pgdata:Then start it:
docker compose up -dThis starts PostgreSQL + Modgud in the background. First boot takes about 15 seconds while Marten provisions the master database, tenant registry and Global Store. No realm or user exists yet.
::: tip Why ASPNETCORE_ENVIRONMENT: Development
The published image runs as Production by default, which fail-closes on a dev-shaped config: it refuses to boot with an http/localhost issuer, with OpenIddict__DevelopmentMode=true, or with Prometheus enabled but no bearer token. Those guards are exactly what you want in production and exactly what gets in the way of a 10-minute local eval. Setting Development legitimately allows the http://localhost issuer and ephemeral signing keys used here. Do not ship this compose to production — see Deployment.
:::
A fresh deployment has zero realms and zero users. Normal routes remain closed until an operator with shell access issues a short-lived, single-use installation link:
docker exec modgud \
dotnet Modgud.Api.dll recover install-link \
--base-url http://localhostOpen the printed /install?token=... URL. Enter a realm slug and display name,
use localhost as the primary domain, then choose the first administrator's
username, email and password. Completion creates the first ordinary realm and
its tenant database, assigns IsControlPlane, creates the administrator with
realm:admin, and redirects to the login page.
::: tip Password rules The installation API enforces the same Identity password policy as the regular admin UI (length, mixed case, digit). A weak password is rejected — see Settings for how to adjust the policy if needed. :::
::: details Automated installation for CI/test
recover install-link --json returns the plaintext bearer token in a
machine-readable final line. A trusted runner can submit it together with the
realm and administrator payload to POST /api/install/complete. The browser
uses the same API. See First-time setup
for a complete curl example.
:::
Open http://localhost and sign in with the credentials chosen during installation. The admin SPA is served same-origin by the Modgud container on port 80 — there is no separate frontend port in the Docker flow. You land in the admin SPA's dashboard.
The sidebar shows everything because you hold realm:admin:
- Authorization — Users, Service Accounts, Roles, Groups
- OAuth & Federation — Login Providers, OAuth Clients, Scopes, APIs, Invite Codes
- System — Applications, Realms, Realm Settings, Logs, Scheduled Jobs, Change Requests
In a separate terminal:
# Discovery document
curl http://localhost/.well-known/openid-configuration | jqYou should see issuer, authorization_endpoint, token_endpoint,
userinfo_endpoint, etc. The endpoints are rooted at http://localhost/ —
Modgud resolves the realm from the Host header, not from a URL path segment.
Because localhost was registered during installation, it resolves to your
first realm.
# JWKS (signing keys)
curl http://localhost/.well-known/jwks | jq '.keys[0].kid'::: tip JWKS path
The discovery document advertises the JWKS endpoint at jwks_uri. Modgud serves it at /.well-known/jwks (no .json suffix) — use the path from the discovery document if you want to be format-agnostic.
:::
You should get a key ID — that's the public key resource servers use to validate JWT access tokens. Note that Modgud's default token format is Reference (opaque); JWKS validation only applies to clients you switch to JWT (see step 6).
Register a client in the admin SPA: OAuth & Federation → OAuth Clients → Create. The create modal lets you set grants, scopes, redirect URIs, and the app at create time, so the client is functional immediately. For a quick test:
- Set Access Token Type = JWT if you want a decodable token (otherwise you get an opaque reference token).
- Add a redirect URI — e.g. the test redirect on oidcdebugger.com.
- Copy the discovery URL from step 4 and the client ID into oidcdebugger.
Click Send Request in oidcdebugger → log in as admin → consent → you'll see an access token. If you chose JWT, decode it at jwt.io — sub, email and aud; once the token targets a registered OAuth API, requesting roles and/or permissions adds the corresponding arrays under resource_access[<audience>].
You're now ready for the linear walkthrough that turns Modgud into the IdP for a real app of yours: SaaS Integration Walkthrough.
If you have cloned the repository (contributors only — not part of this Docker quickstart), it ships a Node script that POSTs a complete demo dataset (extra users, granular roles, auto-membership groups, OAuth clients, scopes, an API and a sample external login provider) through the regular admin API:
node scripts/seed-demo.mjsThe script uses your admin login (defaults: admin / ABC12abc!; pass --user= and --password= to change). It is idempotent — re-running only creates what's missing. At the end it prints any generated OAuth client secrets — capture them, those values are not retrievable from the API later. This step is optional and secondary to the core path above, and it needs the repo checked out (it is not in the published image).
::: details I get 401 "Invalid credentials" on the login page
Check that installation completed successfully and use the username, not the
email address, unless both are identical. docker logs modgud shows migration
or provisioning failures. If the container is still starting, wait for
/health/ready and retry.
:::
::: details Magic-link emails don't arrive With no SMTP configured, Modgud silently drops outbound email — there is no on-disk dev mailbox. Realm-admin invitation endpoints return the one-time URL, so local setup is still possible. To capture emails locally, point Modgud at a dev SMTP catcher such as Mailpit or smtp4dev via the SMTP settings — see Settings. For real delivery, configure your production SMTP host. :::
::: details OIDC discovery returns 404
Modgud resolves the realm from the Host header. Make sure the requested host is
listed in the realm's Domains and that one of them is the Primary Domain. Check
docker logs modgud for RealmMiddleware warnings if you suspect a
host-resolution problem.
:::
::: details Is the container healthy?
The container exposes /health/ready (DB + signing-cert readiness) and /health/live (liveness). There is no plain /health endpoint.
curl http://localhost/health/ready
curl http://localhost/health/live:::
::: details I want to start over For this disposable quickstart, remove the Compose volume and start again. This deletes the master database and every realm database:
docker compose down -v
docker compose up -dThen repeat step 2. Do not use down -v on an environment whose data you need;
it is intentionally destructive.
:::
- First-time setup — the bootstrap paths explained, when to use which, and the production hostname / Prometheus steps
- Concepts: Apps & resource_access — the mental model behind the permission system
- Integrating a Resource Server — wire your own ASP.NET Core backend to validate tokens
- Recovery CLI — break-glass operations beyond bootstrap