Configure
Set up the host
Section titled “Set up the host”The stack runs on one host behind a Cloudflare Tunnel, so the host needs no public ports. A deploy
is three commands on the server: pull the repository, pick a published image tag, start the stack.
You can also send them from another machine over an ssh key whose forced command is
scripts/remote_deploy.sh, which allows those steps and nothing else (see deploy/DEPLOY-PROD.md
Part 1.6). Every command runs as just stack <prod|staging> <command>. A command that changes state
takes YES to confirm which host it acts on. Deployment and operations
describes the topology these steps produce.
-
Create a Cloudflare Tunnel, one of two ways.
-
By hand: in the Cloudflare dashboard, create a remotely managed tunnel and add a public hostname per service, forwarding to
app:8081andapi:8000. The landing page and docs hostnames are Workers Custom Domains instead (step 1 of Deploy). -
With OpenTofu:
infra/cloudflare/manages the DNS records, the tunnels, and the ingress rules. Its README lists the credentials, the API token scopes, and the state encryption passphrase. With those exported, plan and apply per environment:Terminal window just cloudflare-checkjust cloudflare-plan prodjust cloudflare-apply prod # plans and saves it; review the diffjust cloudflare-apply prod YES # applies the plan you just reviewed
Either way, copy the tunnel token: step 3 puts it in a secret file.
-
-
Copy
.env.exampleto.envand fill it in.Terminal window cp .env.example .envThe root
.envis gitignored and holds every host-local value Compose interpolates. One host serves one environment. Every key is described in.env.example. The required ones:ENVIRONMENT:prodorstaging.just stackrefuses to run against a host whose.envsays otherwise.API_PUBLIC_URL,APP_PUBLIC_URL,SITE_PUBLIC_URL,DOCS_PUBLIC_URL: the four public origins on your domain.IMAGE_TAG: the published image tag to run, see step 1 of Deploy. SetIMAGE_REGISTRYtoo when the images come from your own fork.GOOGLE_OAUTH_CLIENT_ID,GITHUB_OAUTH_CLIENT_ID: OAuth client IDs for social login.EMAIL_PROVIDERand the sender fields. Withsmtp, also fillSMTP_HOST,SMTP_USERNAME, andsecrets/<env>/smtp_password. Withmicrosoft_graph, fill the tenant, client, and sender values andsecrets/<env>/microsoft_graph_client_secret. Backend startup validates whichever provider you chose.BOOTSTRAP_SUPERUSER_EMAIL: the first admin account. The migrator creates it with the password insecrets/<env>/bootstrap_superuser_password.MALWARE_SCAN_ENABLED:truestarts ClamAV with the stack, see step 2 of Deploy.
Upload quotas:
MAX_UPLOAD_FILES_PER_USERandMAX_UPLOAD_BYTES_PER_USER_MBcapcontributoraccounts. The*_LAB_USER*pair capslabaccounts and must not be lower. The quota counts existing rows, so on a host with existing data, raise the limits before the first start: an owner already above the limit cannot upload at all. Every account starts ascontributor. A superuser promotes lab members withPUT /v1/admin/users/{user_id}/roleand the body{"role": "lab"}.Per-image caps:
MAX_IMAGE_UPLOAD_SIZE_MBandMAX_IMAGE_UPLOAD_PIXELScapcontributorimages (10 MB, 12.6 MP);MAX_IMAGE_UPLOAD_SIZE_LAB_MBandMAX_IMAGE_UPLOAD_PIXELS_LABcaplabimages (20 MB, 24 MP). No pixel cap may exceed 50 MP. An image takes about 6 bytes per pixel of memory while it is processed (about 145 MB at 24 MP), so sizeIMAGE_RESIZE_WORKERSxWEB_CONCURRENCYagainst the backend’s memory limit before raising a pixel cap. -
Create the runtime secret files.
Terminal window just deploy-secrets-template prodReplace every placeholder under
secrets/prod/, and paste the tunnel token from step 1 intosecrets/prod/cloudflare_tunnel_token. Runtime secrets (database passwords, the auth token secret, the restic password, the tunnel token, provider secrets) live only there, never in.env.deploy/env/variables.tomlis the inventory;just env-inventoryprints it. -
Validate the configuration.
Terminal window just compose-config # the Compose overlays render for every environmentjust deploy-secrets-check # every secret file exists, has mode 0644 in a 0700 dir, and is not a placeholder
With the configuration valid, deploy the stack.
Optional: central telemetry
Section titled “Optional: central telemetry”Prod and staging can ship to a central monitoring stack (Grafana, Loki, Tempo, and Prometheus).
Set OTEL_EXPORTER_OTLP_ENDPOINT, OTLP_AUTH_TOKEN, and TELEMETRY_EDGE_KEY in the host’s root
.env; your monitoring operator supplies them. just stack <env> up then includes
compose.telemetry.yml. Hosts without the endpoint ship nothing, and docker logs stays the only
log path.
Deployment and operations describes each variable and how the Alloy agent is sandboxed.