Self-hosting
Running authbase for real: what it needs, how to put it behind HTTPS, what to back up, how to upgrade, and the runbooks for rotating keys. Start with the quick start if you have not run it yet.
What it needs
Section titled “What it needs”- authbase: one stateless binary, from a release archive (Linux or
macOS, amd64 or arm64, with checksums and an SBOM) or the container
image built from
deploy/Dockerfile;authbase versionsays which build it is. Memory follows CPUs: a password check holds 19 MiB for a moment and at most max(2, CPUs) run at once. Measured: 30 MiB idle, about 410 MiB on 12 CPUs during a burst of 60 simultaneous logins. 1–2 vCPUs and 512 MB serve a small product comfortably. - PostgreSQL 16 or newer (CI tests 16 and 18). All state lives here.
- One secret, the master key (
AUTHBASE_MASTER_KEY, 32 random bytes, base64): it encrypts the apps’ signing keys and queued email. Generate it withopenssl rand -base64 32orauthbase keygen. - An SMTP relay, if you want password reset, verification and security emails.
Every setting is an environment variable; deploy/.env.example lists them
with comments. Secrets never go in
the image, the compose file or the logs. authbase serve --check-config
validates the configuration and exits.
Deploy with Docker Compose
Section titled “Deploy with Docker Compose”deploy/compose.yaml is the reference deployment: authbase and
PostgreSQL, configured from deploy/.env. The image is built from source
on the server. The authbase-self-host skill
(Claude skills) walks through a fresh server step by
step.
cp deploy/.env.example deploy/.env # set AUTHBASE_MASTER_KEY, POSTGRES_PASSWORD, AUTHBASE_PUBLIC_URLdocker compose -f deploy/compose.yaml up -d --buildFor production, in deploy/.env:
AUTHBASE_PUBLIC_URL=https://auth.example.com # https: Secure, __Host- cookies and HSTS follow from itAUTHBASE_PORT=127.0.0.1:8080 # publish on loopback only; the reverse proxy is the way inAUTHBASE_TRUSTED_PROXIES=172.16.0.0/12 # where the proxy's requests come from, see belowAUTHBASE_ALLOW_SIGNUP=false # once every developer who needs an account has oneAnything else that runs a container and a PostgreSQL works the same way:
the container needs the variables above plus AUTHBASE_DATABASE_URL.
HTTPS and the reverse proxy
Section titled “HTTPS and the reverse proxy”authbase speaks plain HTTP and expects a reverse proxy to terminate TLS. Two things matter:
AUTHBASE_PUBLIC_URLis the https origin users see. It is the token issuer (changing it later invalidates every issued token and every integration’s configuration), the base of email links, and it turns onSecure+__Host-session cookies and HSTS.AUTHBASE_TRUSTED_PROXIESlists the addresses the proxy’s requests arrive from (CIDRs). Only then isX-Forwarded-Forbelieved, so rate limits, lockout and the audit log see the real client IP. A proxy on the Docker host reaches the container from the Docker network’s gateway (within172.16.0.0/12by default); check theipof a request in the audit log if unsure. Leave it empty when nothing sits in front.
Caddy (certificates included):
auth.example.com { reverse_proxy 127.0.0.1:8080}nginx:
server { listen 443 ssl; server_name auth.example.com; # ssl_certificate …; ssl_certificate_key …; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; }}Serve authbase on its own host name; the dashboard, the API and the hosted pages share it, and the dashboard needs no CORS.
Set AUTHBASE_SMTP_HOST, _PORT, _USER, _PASSWORD and _FROM.
AUTHBASE_SMTP_TLS is starttls by default and then required (a relay
without it is refused), tls for implicit TLS (port 465), or none for a
local relay only. Mail is queued in the database, encrypted, and sent in
the background with retries, so requests never wait for SMTP. Without
SMTP, email endpoints answer 503 email_not_configured and apps cannot
turn email verification on. Templates and hosted pages can be overridden
with files under AUTHBASE_TEMPLATES_DIR.
Health and metrics
Section titled “Health and metrics”GET /healthz: 200 while the process runs (liveness).GET /readyz: 200 when the database answers and migrations are current, 503 otherwise (readiness)./metrics(Prometheus) is served only on its own listener, off unlessAUTHBASE_METRICS_ADDRis set, and never on the public port. With compose, setAUTHBASE_METRICS_ADDR=:9090and publish it to loopback (127.0.0.1:9090:9090) or your monitoring network only.
Worth alerting on: authbase_http_requests_total{code=~"5.."} rising,
authbase_rejections_total (429s: an attack or a misconfigured client),
authbase_audit_events_total{action="token.reuse_detected"} (possible
token theft), authbase_email_outbox_queued growing (SMTP down), and the
database pool (authbase_db_connections_*).
Backups
Section titled “Backups”A backup is the database and the master key, and they must be kept apart: the database alone cannot sign tokens (its signing keys are encrypted), and the key alone is useless.
# database, compressed custom formatdocker compose -f deploy/compose.yaml exec -T postgres pg_dump -U authbase -Fc authbase > authbase-$(date +%F).dump# restore, with authbase stoppeddocker compose -f deploy/compose.yaml exec -T postgres pg_restore -U authbase -d authbase --clean --if-exists < authbase-2026-09-28.dumpKeep the master key in a password manager or secret store, never next
to the dumps. A dump is readable with the master key that was current
when it was taken: after a master key rotation, keep the old key as long
as you keep older dumps. Practise a restore now and then; a restored
instance started with the right key answers /readyz and signs tokens.
Upgrades
Section titled “Upgrades”- Back up the database.
- Update the checkout (or image) and restart:
git pull && docker compose -f deploy/compose.yaml up -d --build. - Migrations run at start-up (
AUTHBASE_AUTO_MIGRATE=true, the default). Withfalse, runauthbase migrate upyourself; until then/readyzanswers 503.
Going back to an older version means restoring the backup taken before the upgrade. Several instances can run side by side (lockout, email sending and the background sweeper coordinate through the database); the per-IP rate limits are counted per instance.
Runbooks
Section titled “Runbooks”Rotate an app’s signing key
Section titled “Rotate an app’s signing key”When: on a schedule (yearly, say) or if you suspect the key leaked.
Dashboard: App → API keys → Signing keys → Rotate signing key, or
POST /v1/apps/{appId}/signing-keys/rotate. New tokens are signed with
the new key immediately; the old key stays in the JWKS until the tokens
it signed have expired (access-token lifetime + 5 minutes), then is
retired. Verifiers that refetch the JWKS on an unknown kid (both SDKs,
the examples) pick it up by themselves; ones that only refresh on a timer
reject new tokens until their next refresh.
Rotate or revoke an API key
Section titled “Rotate or revoke an API key”Routine: Rotate, which creates a new key and gives the old ones a grace period (the app’s setting, 24 hours by default). Put the new key in your backend’s environment and deploy; the old key stops working when the grace period ends.
Leaked: Revoke it (it stops at once), create a new one, deploy. Then check the audit log, filtered by the key’s id as actor, for what it was used for.
Rotate the master key
Section titled “Rotate the master key”When: on a schedule, when someone who knew it leaves, or if it may have leaked. Takes a few seconds of downtime.
-
Back up the database (above).
-
Generate the new key and keep it in your secret store:
openssl rand -base64 32. -
Stop authbase (every instance):
docker compose -f deploy/compose.yaml stop authbase. -
Re-seal everything under the new key. The new key is read from the environment, never a flag;
read -skeeps it out of the shell history:Terminal window read -rs AUTHBASE_NEW_MASTER_KEY && export AUTHBASE_NEW_MASTER_KEY # paste the new keydocker compose -f deploy/compose.yaml run --rm -e AUTHBASE_NEW_MASTER_KEY authbase admin rotate-master-keyunset AUTHBASE_NEW_MASTER_KEYIt re-encrypts every signing key and queued email in one transaction and records
instance.master_key_rotatedin the audit log. If it fails, nothing changed. Running it again is harmless. -
Switch the key: set
AUTHBASE_MASTER_KEYindeploy/.env(or your secret store) to the new key. -
Start authbase:
docker compose -f deploy/compose.yaml up -d authbase. authbase checks at start-up that its key opens the database’s signing keys; an instance still on the old key refuses to start withAUTHBASE_MASTER_KEY does not open this database's signing keys.
Keep the old key as long as you keep backups taken before step 4. Tokens, API keys and passwords are unaffected: users notice nothing.
After a leak
Section titled “After a leak”| What leaked | Do |
|---|---|
| An API key | Revoke it; issue a new one (above). |
| A database dump | Nothing is directly usable: passwords are Argon2id hashes, API keys and refresh tokens SHA-256 hashes, signing keys and queued email encrypted. Emails, usernames, roles and metadata are exposed; judge whom to inform. Rotate the master key if it could have leaked as well. |
| The master key alone | Rotate the master key. |
| The master key and the database | Rotate the master key, then rotate every app’s signing key (an attacker could have minted tokens), and tell integrators. Consider a password reset for all users if hashes should be treated as exposed. |
| A dashboard account | Reset its password; its sessions end. Review the apps’ audit logs. |
For agents: this page as markdown · llms.txt