Skip to content

Docker compose

The deploy/ directory ships a four-container stack:

ContainerRole
caddyTLS termination via local_certs, forward-auth integration with Authelia.
painscaler-apiGo binary, distroless image, port 8080 internal only.
painscaler-webnginx serving the built React SPA.
autheliaFile-based auth with TOTP MFA.

Caddy is the only container with published ports. All other containers run on the painscaler bridge network and are reached through Caddy.

Prerequisites

  • Docker and Docker Compose v2.
  • make.
  • Local DNS or /etc/hosts entries for painscaler.lan and auth.lan.

Quickstart

Terminal window
cd deploy
make init # generate .env, secrets, render templated configs
$EDITOR .env # fill ZPA_CLIENT_ID, ZPA_CLIENT_SECRET, ZPA_CUSTOMER_ID, ZPA_VANITY, ZPA_IDP
make build
make up
make show-admin # print the generated admin password
make ca # extract Caddy root CA -> ./painscaler-ca.crt

Add to /etc/hosts on every machine that should reach the stack:

<docker-host-ip> painscaler.lan auth.lan

Trust painscaler-ca.crt in the browser or OS certificate store, then visit https://painscaler.lan.

Make targets

TargetPurpose
make helpList all targets.
make initGenerate env, secrets, and rendered configs.
make upStart the stack.
make downStop the stack (volumes retained).
make logsTail every container’s logs.
make caExtract the root CA cert.
make rotateRegenerate all secrets. Invalidates active sessions.
make hash PASSWORD=xxxArgon2id-hash a custom password.
make mfaTail Authelia notifications.txt for the TOTP enrolment URL.
make nukeWipe volumes. Destructive.

Generated files

PathContents
secrets/Random secrets (gitignored, mode 600).
authelia/configuration.ymlRendered from .tmpl (gitignored).
authelia/users_database.ymlRendered from .tmpl (gitignored).
.envZPA credentials (gitignored).

Network topology

PortContainerExposure
80, 443caddyPublic.
8080painscaler-apiIntra-network only. /metrics is scrapable from inside the network.
80painscaler-webIntra-network only. Served via Caddy.
9091autheliaIntra-network only. Forward-auth target.

External access to painscaler-api is not possible: the service uses expose: rather than ports:. Caddy enforces authentication via Authelia before forwarding upstream.

Domain configuration

Default uses painscaler.lan and auth.lan. Change in Caddyfile and authelia/configuration.yml.tmpl (session.cookies[0].domain, authelia_url, default_redirection_url) to use a different suffix.

.local triggers mDNS resolution on macOS and Linux. Use .lan or .home.arpa instead.

First MFA enrolment

Authelia’s file-notifier writes TOTP enrolment links and codes to authelia/notifications.txt:

Terminal window
make mfa

Open the link in a fresh tab to register an authenticator app.

Public deployment

For a public deployment with painscaler.com:

  1. Replace local_certs (or tls internal) in Caddyfile with the default Let’s Encrypt directive.
  2. Open ports 80 and 443 publicly.
  3. Point DNS at the host.

The Authelia user database, session secrets, and the rest of the stack require no changes.