Self-hosted Worklist is distributed as a Docker image and configured with a signed offline license file. This page shows the real deployment model before you buy: what you run, what you mount, and what your operators maintain.
The quickstart uses the supplied image digest, a self-hosted environment file, a mounted license, and secret-backed database credentials prepared in the steps below.
docker compose \ -f docker-compose.self-hosted.yml \ --env-file .env.self-hosted up -d
Start the stack with the self-hosted Compose template and environment file.
Distribution
A Worklist self-hosted Docker image plus a Compose template.
License
A signed offline license file mounted read-only into the container.
Database
PostgreSQL plus bundled MinIO object storage in the supplied Compose stack.
Security posture
Same-origin app/API, TLS reverse proxy, and no public marketing site inside the image.
Before you buy
What you need to run it.
The self-hosted package is intentionally boring infrastructure: one Worklist application container, PostgreSQL, MinIO for encrypted attachments, Docker secrets, and your reverse proxy. The marketing site is not shipped in the self-hosted image; the image serves the workspace app and API.
Runtime
Docker Engine with Docker Compose, or an equivalent container platform.
Network
A public HTTPS origin behind a reverse proxy such as Caddy, nginx, Traefik, or your ingress controller.
Storage
Persistent PostgreSQL and MinIO volumes; external S3-compatible storage can replace MinIO.
Secrets
Stable JWT secrets, OPAQUE server setup, database credentials, and the Worklist license file.
Operator access
An administrator who can back up PostgreSQL, rotate license files, and restart the application.
Install
From license bundle to running stack.
Worklist supplies the image reference, Compose template, environment example, signed license file, and one-time bootstrap token. Operators pin the image, mount the license as a Docker secret, generate runtime secrets once, and start the stack.
Step 1
Prepare the environment file.
Copy the supplied example, load it for the current shell, and fail early if the licensed image reference is missing.
Prepare .env.self-hosted
host shell
cp .env.self-hosted.example .env.self-hosted# Set WORKLIST_IMAGE to the tag@digest reference from your license bundle.set -a. ./.env.self-hostedset +a: "${WORKLIST_IMAGE:?set WORKLIST_IMAGE in .env.self-hosted to the supplied tag@digest image reference}"
You should now have a local .env.self-hosted file with WORKLIST_IMAGE set to the supplied tag@digest reference.
Step 2
Install the offline license.
Place the signed license where the Compose stack expects it. On Linux, make it group-readable by the Worklist container group.
The stack should report running containers before you put your HTTPS proxy in front of it.
The Worklist container reads files as UID/GID 10001. On Linux hosts, the Worklist secrets are 0640 and group-readable by that container group; the PostgreSQL, MinIO, and object-storage secret files stay 0600 on the host and are mounted through Docker secrets. The generated Worklist object-storage secret is a 40-character MinIO-compatible secret key.
1
Verify the image.
Use the tag and digest supplied with the license bundle. Avoid mutable floating tags in production.
2
Preserve secrets.
Keep the OPAQUE setup value, JWT secrets, database URL, and license file backed up with the database.
3
Put TLS in front.
Expose the app through HTTPS and configure email/invite URL templates to the same external origin.
Configure
The settings that matter.
Secret-valued settings should use the matching *_FILE variables. Startup rejects ambiguous sources when both a direct value and a file value are set for the same secret.
WORKLIST_IMAGE
The immutable self-hosted image reference supplied with your license bundle. Pin a tag and digest in production.
BILLING_MODE
Set to self_hosted. Self-hosted mode is loaded at startup and is not a runtime toggle.
SELF_HOSTED_LICENSE_PATH
Path inside the container to the signed license file. The Compose template uses /run/secrets/worklist_license.
SELF_HOSTED_ALLOW_PUBLIC_SIGNUP
Keep false for internet-reachable instances unless a separate access-control layer protects registration.
DATABASE_URL_FILE
Path to the PostgreSQL connection URL secret. Do not set both DATABASE_URL and DATABASE_URL_FILE.
AUTH_OPAQUE_SERVER_SETUP_B64_FILE
Generate once and preserve exactly as emitted by generate_opaque_setup; losing it can strand existing accounts.
JWT_ACCESS_SECRET_FILE and JWT_REFRESH_SECRET_FILE
Stable production secrets generated separately with at least 32 characters each.
EMAIL_* URL templates
The example uses localhost for a local boot; set production values to your external HTTPS origin.
R2_BUCKET, R2_ENDPOINT, and R2_PUBLIC_ENDPOINT
Bucket name plus internal and browser-facing S3-compatible endpoints. The default stack points them at bundled MinIO.
MINIO_CORS_ALLOWED_ORIGIN
Local boot allows the localhost app origin. Production should set this to the Worklist app origin, using only scheme, host, and optional port.
Variable
Purpose
WORKLIST_IMAGE
The immutable self-hosted image reference supplied with your license bundle. Pin a tag and digest in production.
BILLING_MODE
Set to self_hosted. Self-hosted mode is loaded at startup and is not a runtime toggle.
SELF_HOSTED_LICENSE_PATH
Path inside the container to the signed license file. The Compose template uses /run/secrets/worklist_license.
SELF_HOSTED_ALLOW_PUBLIC_SIGNUP
Keep false for internet-reachable instances unless a separate access-control layer protects registration.
DATABASE_URL_FILE
Path to the PostgreSQL connection URL secret. Do not set both DATABASE_URL and DATABASE_URL_FILE.
AUTH_OPAQUE_SERVER_SETUP_B64_FILE
Generate once and preserve exactly as emitted by generate_opaque_setup; losing it can strand existing accounts.
JWT_ACCESS_SECRET_FILE and JWT_REFRESH_SECRET_FILE
Stable production secrets generated separately with at least 32 characters each.
EMAIL_* URL templates
The example uses localhost for a local boot; set production values to your external HTTPS origin.
R2_BUCKET, R2_ENDPOINT, and R2_PUBLIC_ENDPOINT
Bucket name plus internal and browser-facing S3-compatible endpoints. The default stack points them at bundled MinIO.
MINIO_CORS_ALLOWED_ORIGIN
Local boot allows the localhost app origin. Production should set this to the Worklist app origin, using only scheme, host, and optional port.
Bootstrap and seats
First account is deliberate.
Bootstrap admin
The first registration requires the one-time setup token supplied with the license bundle when public signup is disabled. Once the first owner is created, bootstrap is recorded in the database and the setup token cannot be reused.
Seat accounting
The signed license carries an instance-wide seat limit and expiry date. Registered users and pending seat invitations count toward the limit, so new registrations and invitations stop when capacity is reached.
Keep SELF_HOSTED_ALLOW_PUBLIC_SIGNUP=false for internet-reachable deployments unless your proxy, VPN, identity-aware access layer, or network boundary already restricts who can reach registration.
After license expiry, existing data remains readable, but billable writes, registrations, and new seat invitations are rejected until a renewed license file is mounted and the service is restarted.
When email delivery is disabled, seat invite creation returns a one-time join token in the authenticated API response. Keep request and response body logging disabled for invite-management routes.
Maintain
Runbook-level basics.
Backups
Back up PostgreSQL, MinIO data, the license file, .env.self-hosted, and the secrets directory together. If you use external S3 storage, back up that bucket instead.
Renewals
Replace worklist-license.json with the renewed file and restart the Worklist container. The license is read at startup; there is no hot reload path.
Upgrades
Pull the new image reference, keep the digest pinned, and restart through Compose. The API runs bundled database migrations before accepting traffic.
Transport
Terminate HTTPS at your proxy, preserve Host and scheme headers, and keep the default loopback bind unless a trusted firewall or proxy is in front.
Email
Self-hosted mail can be disabled for manual invite links or sent through Postmark; SMTP support is a tracked product follow-up.
Attachments
The default Compose stack starts MinIO, creates R2_BUCKET, creates a bucket-scoped Worklist access key, and applies CORS for presigned browser uploads. Set MINIO_CORS_ALLOWED_ORIGIN to the Worklist HTTPS origin in production. Rotating the object-storage secret or changing R2_PUBLIC_ENDPOINT can invalidate active presigned URLs. External R2 or S3 works too, but operators should remove the MinIO services and minio-init dependency from their Compose copy.