Skip to content
Early preview. authbase is in active development. The source code and packages are not public yet; these docs describe how it works ahead of the first public release.

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.

  • 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 version says 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 with openssl rand -base64 32 or authbase 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/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.

Terminal window
cp deploy/.env.example deploy/.env # set AUTHBASE_MASTER_KEY, POSTGRES_PASSWORD, AUTHBASE_PUBLIC_URL
docker compose -f deploy/compose.yaml up -d --build

For production, in deploy/.env:

Terminal window
AUTHBASE_PUBLIC_URL=https://auth.example.com # https: Secure, __Host- cookies and HSTS follow from it
AUTHBASE_PORT=127.0.0.1:8080 # publish on loopback only; the reverse proxy is the way in
AUTHBASE_TRUSTED_PROXIES=172.16.0.0/12 # where the proxy's requests come from, see below
AUTHBASE_ALLOW_SIGNUP=false # once every developer who needs an account has one

Anything else that runs a container and a PostgreSQL works the same way: the container needs the variables above plus AUTHBASE_DATABASE_URL.

authbase speaks plain HTTP and expects a reverse proxy to terminate TLS. Two things matter:

  1. AUTHBASE_PUBLIC_URL is 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 on Secure + __Host- session cookies and HSTS.
  2. AUTHBASE_TRUSTED_PROXIES lists the addresses the proxy’s requests arrive from (CIDRs). Only then is X-Forwarded-For believed, 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 (within 172.16.0.0/12 by default); check the ip of 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.

  • 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 unless AUTHBASE_METRICS_ADDR is set, and never on the public port. With compose, set AUTHBASE_METRICS_ADDR=:9090 and 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_*).

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.

Terminal window
# database, compressed custom format
docker compose -f deploy/compose.yaml exec -T postgres pg_dump -U authbase -Fc authbase > authbase-$(date +%F).dump
# restore, with authbase stopped
docker compose -f deploy/compose.yaml exec -T postgres pg_restore -U authbase -d authbase --clean --if-exists < authbase-2026-09-28.dump

Keep 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.

  1. Back up the database.
  2. Update the checkout (or image) and restart: git pull && docker compose -f deploy/compose.yaml up -d --build.
  3. Migrations run at start-up (AUTHBASE_AUTO_MIGRATE=true, the default). With false, run authbase migrate up yourself; until then /readyz answers 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.

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.

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.

When: on a schedule, when someone who knew it leaves, or if it may have leaked. Takes a few seconds of downtime.

  1. Back up the database (above).

  2. Generate the new key and keep it in your secret store: openssl rand -base64 32.

  3. Stop authbase (every instance): docker compose -f deploy/compose.yaml stop authbase.

  4. Re-seal everything under the new key. The new key is read from the environment, never a flag; read -s keeps it out of the shell history:

    Terminal window
    read -rs AUTHBASE_NEW_MASTER_KEY && export AUTHBASE_NEW_MASTER_KEY # paste the new key
    docker compose -f deploy/compose.yaml run --rm -e AUTHBASE_NEW_MASTER_KEY authbase admin rotate-master-key
    unset AUTHBASE_NEW_MASTER_KEY

    It re-encrypts every signing key and queued email in one transaction and records instance.master_key_rotated in the audit log. If it fails, nothing changed. Running it again is harmless.

  5. Switch the key: set AUTHBASE_MASTER_KEY in deploy/.env (or your secret store) to the new key.

  6. 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 with AUTHBASE_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.

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.