Deployment
Getting started gets Authwall running. This page covers what to change when moving from a trial to a real deployment: HTTPS, a durable secret, a production database, logging, and health checks.
For the exact syntax and defaults of every variable mentioned here, see the configuration reference.
Runnable examples
examples/ holds self-contained Docker Compose setups for the
common topologies — Authwall as the entrypoint, behind nginx or Caddy, or as a
sidecar auth checker. Each is runnable with docker compose up and is a good
starting point to copy from. See examples/README.md for
a chooser.
The Docker image
Authwall is published as vbarbarosh/authwall on Docker Hub. The image:
- runs as the non-root
nodeuser; - uses
dumb-initas PID 1 for correct signal handling; - sets
NODE_ENV=production,LISTEN=0.0.0.0,PORT=3000, andAUTHWALL_LOGGER=stdoutby default; - is tagged per release (
:1,:1.12,:1.12.0) as well as:latest.
Pin a version tag in production so deployments are reproducible.
HTTPS and the public URL
Authwall does not terminate TLS itself. In production it runs behind a TLS terminator — a reverse proxy (nginx, Caddy) or a cloud load balancer — that holds the certificate and forwards plain HTTP to Authwall:
client → TLS terminator (HTTPS) → authwall → your appflowchart LR
client -->|HTTPS| tls["TLS terminator"] -->|HTTP| authwall --> app["your app"]Set AUTHWALL_PUBLIC_URL to the externally
visible HTTPS URL. This value builds the links in emails and OAuth redirects,
and — importantly — it drives the default of
AUTHWALL_COOKIE_SECURE: when AUTHWALL_PUBLIC_URL
begins with https://, the session cookie is marked Secure automatically.
AUTHWALL_PUBLIC_URL=https://auth.myapp.testWhen OAuth providers are configured, their redirect URLs must use this same public HTTPS origin — see OAuth providers.
The session secret
Authwall derives its session secret from one root secret, so that value must stay stable across restarts. Resolution order:
AUTHWALL_SECRETif it is set (must be at least 32 characters);- otherwise
/app/data/secret.keyif that file exists; - otherwise a new random secret is generated and written to that file.
For a single host, persisting /app/data (so secret.key survives restarts)
is enough. When secrets are managed by an orchestrator or an external secret
store — or when more than one Authwall instance must share sessions — set
AUTHWALL_SECRET explicitly and identically on every instance.
Generate one with bin/random-secret. Rotating
the secret invalidates all existing sessions and CSRF tokens by design.
Database
Authwall supports SQLite (default), MySQL, and PostgreSQL, selected by
AUTHWALL_DB.
- SQLite — the default when
AUTHWALL_DBis unset. Fine for a single instance; the database file lives under the data directory, which must be on a persistent volume. - MySQL / PostgreSQL — set
AUTHWALL_DBto amysql://orpostgres://URI. Use this when you want managed backups, or when running more than one Authwall instance against shared state.
Pending migrations are applied automatically when Authwall starts — there is no separate migration step to run for a deployed instance. On boot Authwall waits for the database to accept connections before serving traffic.
Cookies
The session cookie is normally fine with defaults, but two cases need attention:
- Subdomains — if users reach Authwall and the app on different subdomains,
set
AUTHWALL_COOKIE_DOMAINto the shared parent domain. - Cross-site —
AUTHWALL_COOKIE_SAMESITE=nonerequiresAUTHWALL_COOKIE_SECURE=true; Authwall refuses to start otherwise.
The cookie lifetime is fixed at 30 days.
Forwarding to the upstream
Requests that are not under /auth are proxied to
AUTHWALL_UPSTREAM_URL — Authwall's single
upstream. For authenticated, non-public requests Authwall adds an X-Auth-User
header so the upstream can identify the user.
AUTHWALL_UPSTREAM_MODE decides how requests
are forwarded: direct when one app sits behind Authwall, or proxy when the
upstream is a reverse proxy that fans out to several domains. Use
AUTHWALL_SET_HEADERS / AUTHWALL_UNSET_HEADERS
to add or strip headers on proxied requests.
Logging
The Docker image defaults to AUTHWALL_LOGGER=stdout,
which is correct for containers — a log collector or docker logs picks it up.
Use daily only when writing to a persistent log directory on disk.
Error reporting
Set AUTHWALL_SENTRY_DSN to send exceptions to Sentry.
Authwall strips cookies, authorization headers, and request bodies, and redacts
OAuth code / state / token parameters before events are sent.
Health checks
GET /auth/health is unauthenticated, returns OK with HTTP 200, and sets an
x-authwall-version response header. Point container, orchestrator, or load
balancer liveness/readiness probes at it.
GET /auth/health → 200 OKRestricting access
By default registration is open — anyone can sign up. To run Authwall as a
gate for a known set of users, configure the
access rules (AUTHWALL_ALLOWED_EMAILS,
AUTHWALL_ALLOWED_DOMAINS, and the deny lists). They apply to every sign-in
flow, including OAuth.
Per-IP rate limiting is on by default; leave it on unless an upstream proxy already throttles requests.
Production checklist
- [ ] Pin a versioned image tag (e.g.
vbarbarosh/authwall:1.12.0). - [ ] Terminate TLS in front of Authwall.
- [ ] Set
AUTHWALL_PUBLIC_URLto the HTTPS URL. - [ ] Persist the data directory, or set
AUTHWALL_SECRETexplicitly. - [ ] Use MySQL or PostgreSQL if you need backups or multiple instances.
- [ ] Confirm
AUTHWALL_COOKIE_SECUREistrue(automatic under HTTPS). - [ ] Configure a real mailer if any email-based flow is enabled.
- [ ] Restrict registration with the access rules if sign-up should not be open.
- [ ] Wire
/auth/healthinto your liveness/readiness probes. - [ ] Set
AUTHWALL_SENTRY_DSNif you want error reporting.