Skip to content
Distr
Book DemoStart free trialLogin

Configuration Reference

This page lists all environment variables used to configure a self-hosted Distr instance. Variables marked as required must be set for Distr to start (some are only required when a related feature is enabled). Durations use Go’s duration syntax (e.g. 5s, 10m, 24h) and cron values use standard cron syntax (e.g. 0 * * * *).

VariableRequiredDefaultDescription
DATABASE_URLyes—PostgreSQL connection string (e.g. postgres://user:pass@host:5432/distr).
DATABASE_MAX_CONNSno—Overrides the maximum number of connections in the pgx pool.
JWT_SECRETyes—Base64-encoded secret every token is signed with, see Security & Encryption. Generate with openssl rand -base64 32.
DATABASE_ENCRYPTION_KEYyes—Base64-encoded key that sensitive database columns are encrypted with, see Security & Encryption.
DATABASE_ENCRYPTION_MIGRATE_ON_BOOTnofalseEncrypt values that are still stored in plaintext during startup. Single instance deployments only.
DISTR_HOSTyes—Base URL of your Distr instance. Its scheme is used for every link, callback URL and email.
DISTR_ENVno—Path to a dotenv file to load on startup (a matching .secret file is also loaded if present).
REGISTRATIONnoenabledSign-up mode, see below: enabled, hidden or disabled.
BLOCKED_EMAIL_DOMAINSno—Comma-separated email domains that cannot sign up, log in or reset a password, see Blocklists.
BLOCKED_IPSno—Comma-separated IP addresses and CIDR ranges that cannot sign up, log in or reset a password, see Blocklists.
USER_EMAIL_VERIFICATION_REQUIREDnotrueWhether new users must verify their email address before signing in.
SERVER_SHUTDOWN_DELAY_DURATIONno—Delay between receiving a shutdown signal and stopping the server (useful for rolling deploys).
ENABLE_QUERY_LOGGINGnofalseLogs all database queries. Intended for debugging only.
MAINTENANCE_MODEnofalseAnswers every API and registry request with 503, see below. The frontend keeps being served.
SUPPORT_EMAILno—Address users can contact for help, offered as a mailto: link where the frontend mentions support. Unset means support is mentioned without a link.

REGISTRATION accepts three values:

  • enabled: the login page links to the registration page and anyone can sign up.
  • hidden: the link is removed from the login page, but the registration page stays reachable at /register and sign-ups still succeed. Use it when you want to hand out the registration URL yourself instead of offering it to every visitor.
  • disabled: sign-up is rejected, both on /register and for OIDC logins with an unknown email address. Existing users can still sign in, and new users can still be added through an invitation.

BLOCKED_EMAIL_DOMAINS (e.g. example.com,spam.io) refuses registration, password login, password resets, invitations, email changes and login through the built-in OIDC providers for addresses of a listed domain or any of its subdomains, so example.com also covers mail.example.com. Matching ignores case. Organizations’ own OIDC providers are not affected.

BLOCKED_IPS (e.g. 203.0.113.7,198.51.100.0/24,2001:db8::/32) refuses every request to the sign-up, login and password reset endpoints whose client IP is a listed address or lies in a listed range. Behind a reverse proxy, the client IP is the rightmost entry of the X-Forwarded-For header.

Both answer with 403 Forbidden, except a blocked email domain on a built-in OIDC login, which redirects back to the login page and shows the reason there.

MAINTENANCE_MODE=true announces planned downtime instead of stopping the instance. Distr keeps serving the frontend, but answers every API request, including the ones from controllers, and every request to the OCI registry with 503 Service Unavailable, so that nothing reaches the database while you work on it. The frontend picks that up and shows a maintenance page that reconnects on its own once the instance is back.

The Kubernetes probes target /, which keeps being served, so the pod stays in its service, while /ready reports 503 for the whole maintenance window.

Distr unwraps JWT_SECRET, DATABASE_ENCRYPTION_KEY and LICENSE_KEY_PRIVATE_KEY with AWS KMS or GCP Cloud KMS during startup, so that no key material is kept in your configuration. See Security & Encryption for details.

VariableRequiredDefaultDescription
KMS_AWS_KEY_IDfor AWS—Key ARN, key id or alias/<name> of the AWS KMS key. Setting it selects AWS KMS.
KMS_AWS_REGIONno—Region of that key. Defaults to the region of KMS_AWS_KEY_ID when it is an ARN, then to the AWS SDK’s own resolution.
KMS_AWS_ENDPOINTno—Overrides the AWS KMS endpoint. Intended for testing against a local stand-in.
KMS_GCP_KEY_NAMEfor GCP—Full resource name of the Cloud KMS key (projects/…/locations/…/keyRings/…/cryptoKeys/…). Setting it selects Cloud KMS.
VariableRequiredDefaultDescription
INVITE_TOKEN_VALID_DURATIONno24hHow long user invite links remain valid.
RESET_TOKEN_VALID_DURATIONno1hHow long password reset links remain valid.
CONTROLLER_TOKEN_MAX_VALID_DURATIONno24hMaximum validity of a controller access token before it is refreshed. Replaces the deprecated AGENT_TOKEN_MAX_VALID_DURATION.

Registrations can optionally be protected with a Cloudflare Turnstile widget, which blocks automated sign-ups without asking users to solve a puzzle. It is disabled unless both variables below are set: the site key renders the widget in the browser and the secret verifies its token on the server, so a half-configured widget could only ever reject valid registrations. Distr logs a warning and stays unprotected when just one of them is present.

Create a widget in the Cloudflare dashboard under Turnstile, add the hostname of your instance to it and copy both keys. Turnstile does not require your instance to be proxied through Cloudflare. Because a widget only accepts challenges from its configured hostnames, registrations made through an organization’s custom app domain do not show the widget and are not verified; they remain subject to the rate limits of the authentication endpoints.

VariableRequiredDefaultDescription
TURNSTILE_SITE_KEYif TURNSTILE_SECRET—Public site key of the Turnstile widget, rendered on the registration form.
TURNSTILE_SECRETif TURNSTILE_SITE_KEY—Secret key used to verify tokens with Cloudflare’s siteverify API. Keep it secret.

Distr still reads every controller variable under its deprecated AGENT_* name when the new one is unset, and logs a warning on startup for each one it found that way.

VariableRequiredDefaultDescription
CONTROLLER_HOSTno—Host (and optional port, no scheme) controllers and connect commands reach this instance at, taking precedence over an organization’s custom app domain. Its scheme is taken from DISTR_HOST. Unset, controllers use the organization’s custom app domain if it has one and DISTR_HOST otherwise. Replaces the deprecated AGENT_HOST.
CONTROLLER_INTERVALno5sInterval at which controllers poll Distr for updates. Replaces the deprecated AGENT_INTERVAL.
CONTROLLER_DOCKER_CONFIGno—Base64-encoded Docker config JSON provided to Docker controllers for registry authentication. Replaces the deprecated AGENT_DOCKER_CONFIG.

Email sending is disabled unless MAILER_TYPE is set. MAILER_SMTP_* values only apply when MAILER_TYPE=smtp.

VariableRequiredDefaultDescription
MAILER_TYPEno—Mailer backend: smtp, ses or empty (disabled).
MAILER_FROM_ADDRESSif mailer enabled—Sender address used for outgoing emails.
MAILER_SMTP_HOSTno—SMTP server hostname.
MAILER_SMTP_PORTif smtp—SMTP server port.
MAILER_SMTP_USERNAMEno—SMTP username.
MAILER_SMTP_PASSWORDno—SMTP password.
MAILER_SMTP_IMPLICIT_TLSnofalseUse implicit TLS instead of STARTTLS (typically required on port 465).
NOTIFICATION_EMAIL_HOURLY_QUOTAno120Max status/metrics notification emails sent to a single address per hour. 0 disables the limit.

Registry variables are only read when REGISTRY_ENABLED=true. See Registry Configuration for a feature overview.

VariableRequiredDefaultDescription
REGISTRY_ENABLEDnofalseEnables the built-in OCI-compatible artifact registry.
REGISTRY_HOSTif registry enabled—Host (and optional port) clients use to reach the registry.
REGISTRY_S3_BUCKETif registry enabled—S3 bucket used to store registry blobs.
REGISTRY_S3_REGIONif registry enabled—S3 region of the bucket.
REGISTRY_S3_CREATE_BUCKETnofalseCreates the bucket on startup (requires s3:CreateBucket).
REGISTRY_S3_ENDPOINTno—Custom S3 endpoint (for MinIO or other S3-compatible storage).
REGISTRY_S3_ACCESS_KEY_IDno—S3 access key ID (falls back to the default AWS credential chain if unset).
REGISTRY_S3_SECRET_ACCESS_KEYno—S3 secret access key.
REGISTRY_S3_USE_PATH_STYLEnofalseUse path-style bucket addressing (required by most non-AWS S3 implementations).
REGISTRY_S3_ALLOW_REDIRECTnotrueAllow redirecting blob downloads directly to S3.
REGISTRY_S3_REQUEST_CHECKSUM_CALCULATIONnofalseOnly calculate request checksums when required (compatibility with some backends).
REGISTRY_S3_RESPONSE_CHECKSUM_VALIDATIONnofalseOnly validate response checksums when required.
REGISTRY_RESIGN_FOR_GCPnofalseRe-sign requests for Google Cloud Storage compatibility.
REGISTRY_SCRATCH_DIRno—Directory used for temporary files during uploads.
ARTIFACT_TAGS_DEFAULT_LIMIT_PER_ORGno0Default maximum number of artifact tags per organization. 0 means unlimited.
REGISTRY_UPSTREAM_SYNC_CRONno—Cron schedule for syncing tags from upstream registries (pull-through cache).
REGISTRY_UPSTREAM_SYNC_TIMEOUTno10mTimeout for a single upstream sync run.

These limit what a single client IP address may pull from public artifacts without credentials. Authenticated pulls are not rate limited. A value of 0 disables the limit it stands for. The limits are counted per Distr replica and in memory, so they reset on a rollout and a client spread across replicas may exceed them by up to a factor of the replica count.

VariableRequiredDefaultDescription
REGISTRY_ANONYMOUS_MANIFEST_RATE_LIMIT_PER_MINUTEno10Anonymous manifest and tag list requests per IP, per minute.
REGISTRY_ANONYMOUS_MANIFEST_RATE_LIMIT_PER_HOURno30Anonymous manifest and tag list requests per IP, per hour.
REGISTRY_ANONYMOUS_BLOB_RATE_LIMIT_PER_MINUTEno60Anonymous blob requests per IP, per minute.
REGISTRY_ANONYMOUS_BLOB_RATE_LIMIT_PER_HOURno300Anonymous blob requests per IP, per hour.

Custom domain self-service lets vendor organizations serve Distr under their own domains. It requires an edge proxy with on-demand TLS (e.g. Caddy, included in the Helm chart) in front of Distr. The feature is only offered in the UI when CUSTOM_DOMAIN_TARGET is set.

A domain is only used for links, mails, controller manifests and registry URLs once a CNAME check has found it to point at the CNAME target; until then the organization keeps the default host. The check runs when a domain is registered, can be triggered from the organization settings and needs to be scheduled so that domains recover on their own once a record is fixed. Serving requests on a domain does not depend on the check.

VariableRequiredDefaultDescription
CUSTOM_DOMAIN_TARGETno—Hostname customers point every custom domain’s CNAME record at (e.g. whitelabel.distr.sh). Enables custom domain self-service.
CUSTOM_DOMAIN_VERIFICATION_CRONno—Cron schedule of the built-in domain verification job. If unset, no verification is scheduled in Distr.
CUSTOM_DOMAIN_VERIFICATION_TIMEOUTno4mTimeout for a single verification run. Must be greater than 0.
CUSTOM_DOMAIN_VERIFICATION_REFRESH_AFTERno12hHow long a verified domain is left alone before it is checked again. Failing domains are checked on every run.
INTERNAL_SERVER_ADDRno:8085Listen address of the internal server exposing the Caddy on-demand TLS ask endpoint. Never expose it outside the cluster.
VariableRequiredDefaultDescription
SUPPORT_BUNDLE_RESOURCE_MAX_BYTESno1048576Maximum size of a single resource collected by the support bundle collect script, e.g. the logs of one container or the output of one script.
SUPPORT_BUNDLE_LOG_TAIL_LINESno1000Number of container log lines collected per container. The most recent lines are kept, and SUPPORT_BUNDLE_RESOURCE_MAX_BYTES still applies on top.
SUPPORT_BUNDLE_SCRIPT_TIMEOUTno1mHow long a single custom script may run on the customer’s host before the collect script stops it. Must be greater than 0.

Deployment and deployment target log records are stored in Grafana Loki. All shipped deployment methods (Docker Compose and the Helm chart) include a preconfigured Loki instance with a time-based retention of 30 days. Log retention is managed entirely by the Loki configuration (limits_config.retention_period).

VariableRequiredDefaultDescription
LOKI_URLyes—Base URL of the Loki instance, e.g. http://loki:3100.
LOKI_BEARER_TOKENno—Bearer token for authenticating against Loki.
LOKI_BASIC_AUTH_USERNAMEno—Username for basic authentication against Loki. Ignored when LOKI_BEARER_TOKEN is set.
LOKI_BASIC_AUTH_PASSWORDno—Password for basic authentication against Loki. Only used when LOKI_BASIC_AUTH_USERNAME is set.
LOKI_REQUEST_TIMEOUTno30sTimeout for a single Loki HTTP request.

These control the built-in job scheduler and retention limits. See Maintenance Jobs for details and Kubernetes CronJob alternatives. If a *_CRON variable is unset, the corresponding job is not scheduled. For the timeout variables listed below, 0 means no timeout.

VariableRequiredDefaultDescription
METRICS_ENTRIES_MAX_AGEno—Max age of deployment target metrics entries before cleanup removes them.
CLEANUP_DEPLOYMENT_TARGET_METRICS_CRONno—Cron schedule for pruning deployment target metrics.
CLEANUP_DEPLOYMENT_TARGET_METRICS_TIMEOUTno0Timeout for the metrics cleanup run.
CLEANUP_OIDC_STATE_CRONno—Cron schedule for pruning expired OIDC state entries.
CLEANUP_OIDC_STATE_CRON_TIMEOUTno0Timeout for the OIDC state cleanup run.
CLEANUP_ARTIFACT_BLOB_CRONno—Cron schedule for pruning unreferenced registry blobs from S3.
CLEANUP_ARTIFACT_BLOB_TIMEOUTno0Timeout for the artifact blob cleanup run.
CLEANUP_ARTIFACT_BLOB_MIN_AGEno24hMinimum age of an unreferenced blob before it is eligible for deletion.
CLEANUP_ORGANIZATION_CRONno—Cron schedule for permanently deleting soft-deleted organizations.
CLEANUP_ORGANIZATION_TIMEOUTno0Timeout for the organization cleanup run.
CLEANUP_ORGANIZATION_MIN_AGEno720hRetention period before a soft-deleted organization is permanently deleted.
CLEANUP_USER_ACCOUNT_CRONno—Cron schedule for deleting user accounts that belong to no organization.
CLEANUP_USER_ACCOUNT_TIMEOUTno0Timeout for the user account cleanup run.
CLEANUP_USER_ACCOUNT_MIN_AGEno720hTime since the last login, or creation if never logged in, before deletion.
CLEANUP_FILE_CRONno—Cron schedule for deleting files that nothing references any more.
CLEANUP_FILE_TIMEOUTno0Timeout for the file cleanup run.
CLEANUP_FILE_MIN_AGEno24hMinimum age of an unreferenced file before it is deleted.
DEPLOYMENT_STATUS_NOTIFICATION_CRONno—Cron schedule for sending deployment status notification emails.
DEPLOYMENT_STATUS_NOTIFICATION_TIMEOUTno0Timeout for the deployment status notification run.

Each provider is enabled independently and its credentials are only required when it is enabled.

VariableRequiredDefaultDescription
OIDC_GITHUB_ENABLEDnofalseEnables GitHub sign-in.
OIDC_GITHUB_CLIENT_IDif GitHub enabled—GitHub OAuth App client ID.
OIDC_GITHUB_CLIENT_SECRETif GitHub enabled—GitHub OAuth App client secret.
OIDC_GOOGLE_ENABLEDnofalseEnables Google sign-in.
OIDC_GOOGLE_CLIENT_IDif Google enabled—Google OAuth 2.0 client ID.
OIDC_GOOGLE_CLIENT_SECRETif Google enabled—Google OAuth 2.0 client secret.
OIDC_MICROSOFT_ENABLEDnofalseEnables Microsoft (Entra ID) sign-in.
OIDC_MICROSOFT_CLIENT_IDif MS enabled—Entra ID application (client) ID.
OIDC_MICROSOFT_CLIENT_SECRETif MS enabled—Entra ID client secret.
OIDC_MICROSOFT_TENANT_IDif MS enabled—Directory (tenant) ID, or common/organizations/consumers.
OIDC_GENERIC_ENABLEDnofalseEnables a generic OIDC provider.
OIDC_GENERIC_CLIENT_IDif generic enabled—Client ID issued by the provider.
OIDC_GENERIC_CLIENT_SECRETif generic enabled—Client secret issued by the provider.
OIDC_GENERIC_ISSUERif generic enabled—Issuer URL exposing /.well-known/openid-configuration.
OIDC_GENERIC_SCOPESif generic enabled—Comma- or space-separated scopes (e.g. email,profile); openid is always added.
OIDC_GENERIC_PKCE_ENABLEDnofalseEnables PKCE (S256) for the generic provider.
WELLKNOWN_MICROSOFT_IDENTITY_ASSOCIATION_JSONno—Base64-encoded JSON served at /.well-known/microsoft-identity-association.json.

Every provider needs a callback URL registered at the identity provider. It is built from DISTR_HOST and must match exactly, including the scheme:

ProviderCallback URLWhere to register it
Google{DISTR_HOST}/api/v1/auth/oidc/google/callbackGoogle Cloud Console, as an authorized redirect URI of a Web application client
GitHub{DISTR_HOST}/api/v1/auth/oidc/github/callbackGitHub OAuth Apps, as the Authorization callback URL
Microsoft (Entra ID){DISTR_HOST}/api/v1/auth/oidc/microsoft/callbackMicrosoft Entra admin center, as a Web redirect URI of an app registration
Generic{DISTR_HOST}/api/v1/auth/oidc/generic/callbackat your provider, which must expose a /.well-known/openid-configuration document

With GitHub, only the primary and verified email address is accepted, so a user whose primary address is unverified there cannot sign in.

The first sign-in connects the identity the provider reports (its issuer and the sub claim, or the numeric user ID for GitHub) to the Distr account, and every later sign-in is matched on that identity rather than on the email address, so the account survives an email change at the provider. Users see and disconnect their connections under User Settings → Connected identities. A sign-in started on the sign-up page creates an account and an organization for an unknown email address; invite the user beforehand to place them in an existing organization instead, or set REGISTRATION=disabled (see Sign-up modes) to reject unknown addresses.

Vendor organizations on the Business plan can also configure their own providers in the UI, which requires no instance configuration. Those issuer URLs are entered by organization administrators and fetched by Distr, so they must be https and must not resolve to a private network address. Both restrictions are lifted when DISTR_HOST is an http URL, so that an instance running locally can be developed against an identity provider next to it. An operator whose identity provider only exists inside their own network configures it as the instance-wide generic provider above instead.

Tracing and error reporting via Sentry and OpenTelemetry. Sampler variables accept always_on, always_off, traceidratio, parentbased_always_on, parentbased_always_off or parentbased_traceidratio.

VariableRequiredDefaultDescription
SENTRY_DSNno—Sentry DSN for backend error reporting.
SENTRY_DEBUGnofalseEnables Sentry debug logging.
SENTRY_ENVIRONMENTno—Environment name reported to Sentry (e.g. production).
OTEL_EXPORTER_SENTRY_ENABLEDnofalseExports OpenTelemetry traces to Sentry.
OTEL_EXPORTER_OTLP_ENABLEDnofalseExports OpenTelemetry traces via OTLP (configured with standard OTEL_* variables).
OTEL_CONTROLLER_SAMPLERno—Trace sampler used for controller requests. Replaces the deprecated OTEL_AGENT_SAMPLER.
OTEL_CONTROLLER_SAMPLER_ARGno1.0Argument for the controller sampler (e.g. ratio for traceidratio). Replaces the deprecated OTEL_AGENT_SAMPLER_ARG.
OTEL_REGISTRY_SAMPLERno—Trace sampler used for registry requests.
OTEL_REGISTRY_SAMPLER_ARGno1.0Argument for the registry sampler.

Exposes an internal Prometheus metrics endpoint. See Prometheus Integration for details.

VariableRequiredDefaultDescription
METRICS_ENABLEDnofalseEnables the Prometheus metrics endpoint.
METRICS_ADDRno:3000Address the metrics endpoint listens on.
METRICS_BEARER_TOKENno—Bearer token required to scrape the metrics endpoint, if set.

These values are injected into the compiled frontend at runtime.

VariableRequiredDefaultDescription
FRONTEND_SENTRY_DSNno—Sentry DSN for frontend error reporting.
FRONTEND_SENTRY_TRACE_SAMPLE_RATEno—Frontend trace sample rate (0.0–1.0).
FRONTEND_POSTHOG_TOKENno—PostHog project API token for frontend analytics.
FRONTEND_POSTHOG_API_HOSTno—PostHog API host.
FRONTEND_POSTHOG_UI_HOSTno—PostHog UI host.

Options relevant to Distr Pro and hosted billing.

VariableRequiredDefaultDescription
LICENSE_KEYno—Distr license key unlocking the plan it was issued for.
LICENSE_KEY_PRIVATE_KEYno—PEM-encoded private key used to sign customer license keys, see Security & Encryption.
STRIPE_API_KEYno—Stripe API key for billing integration.
STRIPE_WEBHOOK_SECRETno—Secret used to verify incoming Stripe webhooks.
STRIPE_WEBHOOK_VERSION_MISMATCH_BEHAVIORnoerrorBehavior on Stripe API version mismatch: error or ignore.