Configuration reference
Configuration reference
.env.selfhost is generated by scripts/selfhost/install.sh. See .env.selfhost.example for the fully annotated list. Edit it by hand only if you're not using the installer, then restart the stack with the same --project-name.
Required
Configure credentials for at least one OAuth provider; you do not need all three.
| Variable | Description |
|---|---|
CODEMAGIC_PATCH_API_DOMAIN | API/dashboard domain (no scheme/path) |
CODEMAGIC_PATCH_STORAGE_DOMAIN | Bundled-storage viewer domain (must differ from the API domain); omit for external S3/GCS |
ACME_EMAIL | Email for Let's Encrypt certificates. Still required with SELFHOST_SCHEME=http, where no certificate is issued: Caddy rejects an empty value |
SERVER_URL | Public API URL, e.g. https://updates.example.com |
PUBLIC_BASE_URL | Public artifact base, default https://<storage-domain>/codemagic-patch |
POSTGRES_DB / _USER / _PASSWORD | Bundled PostgreSQL credentials; external database mode uses DATABASE_URL |
MINIO_ROOT_USER / _PASSWORD | Bundled MinIO credentials; omit for external S3/GCS |
WORKER_SHARED_SECRET | Protects worker routes (>= 32 chars) |
GITHUB_OAUTH_CLIENT_ID / GITHUB_OAUTH_CLIENT_SECRET | GitHub OAuth App credentials; configure at least one OAuth provider |
BITBUCKET_OAUTH_CLIENT_ID / BITBUCKET_OAUTH_CLIENT_SECRET | Bitbucket Cloud OAuth consumer credentials |
GITLAB_OAUTH_CLIENT_ID / GITLAB_OAUTH_CLIENT_SECRET | GitLab application credentials |
OAUTH_CLI_AUTH_SECRET | Local random secret (>= 32 chars); signs CLI browser-login authorization codes. The legacy name OAUTH_DEVICE_POLL_TOKEN_SECRET is still accepted |
INITIAL_ADMIN_EMAILS | Allowlist for the first invite-only admin sign-in |
The server refuses to boot while WORKER_SHARED_SECRET or the CLI OAuth signing secret are shorter than 32 chars, or if an OAuth provider's client ID is set without its secret, so a verbatim copy of the example file fails fast instead of running with known secrets.
Common optional
| Variable | Default | Description |
|---|---|---|
MODE | all | all · api · worker. Self-host Compose hardcodes MODE=all; changing .env.selfhost alone does not split API/worker |
GITLAB_OAUTH_BASE_URL | https://gitlab.com | GitLab origin; set for self-hosted GitLab |
REGISTRATION_MODE | invite_only | invite_only or open |
STORAGE_ADAPTER | s3 (self-host) | s3 · gcs · memory, see Infrastructure adapters |
DELIVERY_ADAPTER | base-url | base-url, cloudflare, or cloudfront; see Infrastructure adapters |
MANIFEST_CACHE_CONTROL | derived from DELIVERY_ADAPTER | Cache-Control for meta.json and manifest JSON. base-url gets no-cache, must-revalidate because its purge is a no-op; cloudflare and cloudfront get public, max-age=0, s-maxage=300, must-revalidate, so clients still revalidate while a failed purge is bounded to five minutes. Set it explicitly only to override. Applied when each object is next written, so a deployment that has not published since the change still serves its old header |
MAX_UPLOAD_SIZE | 200mb | Max artifact upload size. Self-host Compose does not pass this through; customize docker-compose.selfhost.yml to override |
RUN_MIGRATIONS | true | Run DB migrations on boot |
LOGGER | true | Set false to silence server logs |
SELFHOST_SCHEME | https | Self-host only. http serves the API and storage sites over plain HTTP on port 80 with no certificates (written by install.sh --allow-http); SERVER_URL, PUBLIC_BASE_URL, and the OAuth callback then use http://. Fixed at install time; not combinable with a CDN adapter |
SELFHOST_HTTP_STORAGE_PORT | 80 | Self-host, SELFHOST_SCHEME=http with bundled storage only. The port of a host:port storage domain, published by the plain-HTTP compose overlay; the installer derives it from the domain |
External object storage
SELFHOST_STORAGE_MODE=s3|gcs selects the matching Compose overlay; CLI R2 uses
shell mode s3. The wizard requires separate buckets for new external installs.
| Variable | Description |
|---|---|
S3_BUCKET | Public artifact bucket |
S3_INTERNAL_BUCKET | Optional private bucket for _internal/ objects; must differ when set. Unset preserves existing one-bucket routing |
S3_REGION, S3_ENDPOINT, S3_FORCE_PATH_STYLE | Provider region and explicit S3-compatible addressing |
S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY | Runtime object credentials |
GCS_PUBLIC_BUCKET, GCS_INTERNAL_BUCKET | Required distinct GCS buckets |
GCS_CREDENTIALS_FILE | Direct installer input: local runtime service-account JSON path |
GCS_CREDENTIALS_JSON_BASE64 | Alternate installer input for SSH transport; written to a private key file, not saved in .env.selfhost |
The GCS overlay copies the host key into tmpfs, then drops privileges before
starting the server. Keep the host key at mode 0600. Storage-key replacement
is manual and outside --repair-env; recreate the server with the same overlays
and rerun storage/release checks.
CloudFront
| Variable | Modes | Description |
|---|---|---|
CLOUDFRONT_DISTRIBUTION_ID | all | Required with DELIVERY_ADAPTER=cloudfront |
CLOUDFRONT_ACCESS_KEY_ID / CLOUDFRONT_SECRET_ACCESS_KEY | all | Set both for a distribution-scoped purge IAM key, or omit both for the AWS SDK default credential chain |
SELFHOST_STORAGE_ORIGIN_MODE | self-host | direct (default) or cdn-origin; bundled CloudFront requires cdn-origin, external storage requires direct |
CODEMAGIC_PATCH_STORAGE_ORIGIN_DOMAIN | bundled cdn-origin | Separate Caddy hostname used as the CloudFront origin; must differ from API and viewer domains |
CLOUDFRONT_ORIGIN_VERIFY_SECRET | bundled cdn-origin | Value the distribution must send in X-Codemagic-Patch-Origin-Verify; required |
CLOUDFRONT_ORIGIN_VERIFY_SECRET_PREVIOUS | bundled cdn-origin | Optional second accepted value, set only while rotating the origin header; omit it otherwise and it falls back to the current secret |
Full setup, IAM policy, DNS ordering, and rotation procedure: CloudFront setup. Cloudflare variables remain documented in Cloudflare setup.