Citadel Docs
Reference

Environment variables

Configure Core, Direct Agents, and Edge Agents, including execution limits and deployment settings.

Citadel runtime settings use CITADEL_ followed by uppercase words separated by single underscores. Standard tool settings such as RUST_LOG, DOCKER_HOST and PG_* retain their names. The .env downloaded in the installation guide contains the settings needed to start Citadel. Add optional settings from this reference as needed. The defaults are for local HTTP; configure TLS before sharing an installation.

The sections below cover Core and its Compose deployment, followed by Direct Agent and Edge Agent configuration. Set each variable on the process or container it belongs to; Core's .env does not configure remote Agents.

Instance controls and deployment overrides

Administrators can manage the 26 controls described in Instance settings. MFA applies on save; the other controls apply at the next Core start. An explicitly supplied environment value overrides the saved choice even when it equals the default. The page shows deployment-managed fields and pending restarts. Remove the override from every deployment environment source and restart/recreate Core to manage that field through the UI. Existing overrides are not removed automatically. CITADEL_MFA_POLICY remains a minimum; release-check deployment restrictions remain in Updates. RUST_LOG module filters remain deployment managed.

Installation and transport

SettingRequired whenPurpose
CITADEL_IMAGEOptionalBlank or unset uses ghcr.io/citadel-p/citadel:latest; set a complete image address to override
CITADEL_BIND_ADDRESSOptionalHost bind address; defaults to 127.0.0.1
CITADEL_HTTP_PORTOptionalBrowser/API host port; defaults to 18000
CITADEL_EDGE_PORTEdge host-port overlayEdge host port; defaults to 18001; the base installer does not publish it
PG_HOSTExternal or renamed PostgreSQLPostgreSQL hostname; Compose defaults to pg_db
PG_PASSWORDAlwaysPostgreSQL password; set before starting
PG_USER, PG_DATABASEOptional in the installerBoth default to citadel; preserve existing values on upgrades
CITADEL_TRANSPORT_MODENetwork accessHow Core serves traffic: HTTPS through a proxy (ReverseProxy), TLS served by Core itself (Direct), or local HTTP (Disabled, installer default)
CITADEL_PUBLIC_URLNetwork accessCanonical browser and API origin; installer defaults to http://localhost: plus CITADEL_HTTP_PORT
CITADEL_EDGE_PUBLIC_GRPC_URLWhenever Core uses HTTPS, even without Edge AgentsHTTPS address for Edge Agent connections; without Edge Agents, use the browser HTTPS URL and leave Edge port publishing disabled
CITADEL_HTTP_ALLOWED_HOSTSNetwork accessSemicolon-separated hostnames accepted by Core; installer defaults to localhost;127.0.0.1
CITADEL_HTTP_TRUSTED_PROXIESReverse proxyImmediate trusted proxy address
CITADEL_HTTP_TRUSTED_PROXY_NETWORKSReverse proxy alternativeImmediate trusted proxy network
CITADEL_TLS_CERTIFICATE_PATHDirect TLSPEM certificate chain inside Core
CITADEL_TLS_PRIVATE_KEY_PATHDirect TLSPEM private key inside Core
CITADEL_TLS_HOST_DIRECTORYDirect-TLS Compose overlayHost certificate directory mounted into Core; defaults to ./tls
CITADEL_JWT_ISSUER, CITADEL_JWT_AUDIENCEOptional overrideToken validation identities; otherwise derived from the public URL
CITADEL_JWT_KEYOptionalExplicit signing key; otherwise persisted in citadel_data
CITADEL_SECRETS_ENCRYPTION_KEYOptionalExplicit 32-byte base64 key; otherwise persisted in citadel_data
CITADEL_MFA_POLICYOptionalMinimum installation MFA policy: Optional, RequiredForAdministrators, or RequiredForAllUsers; the UI can strengthen it
CITADEL_PASSWORDS_MINIMUM_LENGTHOptionalMinimum password length, default 15; accepts 8–128, with a fixed maximum password length of 128
CITADEL_HTTP_OPENAPI_ENABLEDOptionalExposes Swagger and the full/public OpenAPI documents; false by default

For explicit PostgreSQL maintenance, PG_IMAGE selects an official PostgreSQL image digest and PG_DATA selects its data directory (default /var/lib/postgresql/18/docker). These are Compose controls, not application settings. Changing the major image version alone does not upgrade the data. The optional maintenance overlay uses PG_UPGRADE_IMAGE for the locally built helper and PG_SOURCE_DATA for its source directory.

CITADEL_IMAGE, COMPOSE_PROJECT_NAME, and host port settings control Compose. The application settings must reach the Core container; the supplied Compose file reads .env for this purpose. Recreate Core after editing it:

docker compose up -d server

Keep the installation's project and overlay options on that command. A container restart alone does not load a changed Compose environment.

Update checks

SettingDefaultPurpose
CITADEL_UPDATE_CHECKS_ENABLEDtruePermit release checks controlled by setup or Administration → Updates; false prohibits automatic and manual checks. The saved frequency is 6, 12 or 24 hours (default: 6); no automatic installation
CITADEL_UPDATE_STATE_DIRECTORYSet internally by the supplied Compose fileStandard Compose installations use /var/lib/citadel-updates, mapped read-only from .citadel-updates beside Compose; Core resolves the host path and registers automatically after administrator setup. Do not add this setting to .env for standard installs. Custom deployments can supply an absolute host path to an existing private registration. Invalid or unresolved recovery state keeps Core in maintenance. Updates still require a qualified release and approval

No Citadel telemetry or instance data is sent. GitHub receives normal connection information, including the outbound IP address. See update checks and privacy for details and how to turn checks on or off.

Database and persistent paths

Core chooses its database connection in this order:

  1. CITADEL_DATABASE_URL, if set: a PostgreSQL URI (postgres:// or postgresql://). A blank or malformed selected URI fails startup.
  2. Otherwise, PG_HOST, PG_PORT, PG_USER, PG_PASSWORD, and PG_DATABASE; credentials are escaped when constructing the URI.

Remove unused overrides instead of leaving them blank. The supplied Compose PostgreSQL service still requires its PG_* credentials. Changing the initial database password in .env does not reset the password of an existing database.

SettingDefaultPurpose
PG_PORT5432PostgreSQL port used with the PG_* connection settings
CITADEL_DATA_ROOT/app/dataPersistent Core data and generated recovery keys; match the container's data mount
CITADEL_HOST_ROOT/hostRead-only host mount used for Docker storage filesystem metrics
CITADEL_PG_DUMP_PATHpg_dumpPostgreSQL dump executable for control-plane backups
CITADEL_PG_RESTORE_PATHpg_restorePostgreSQL restore executable used by offline restore-system

Preserve original keys and verify the database target before control-plane recovery. A new Compose project does not isolate an external database configured by an override.

Jobs and diagnostics

SettingDefaultPurpose
CITADEL_STATS_POLL_INTERVAL_SECONDS10Platform metric collection interval, in seconds
CITADEL_STATS_FLUSH_INTERVAL_SECONDS60Metric persistence interval, in seconds
CITADEL_SCRIPTS_ENABLEDtrueEnables Script execution
CITADEL_SCRIPTS_MAX_PARALLEL_RUNS4Script runs executing locally on Core; remote runs have a separate limit, see script execution capacity
CITADEL_SCRIPTS_DEFAULT_TIMEOUT_SECONDS300Default script timeout
CITADEL_SCRIPTS_MAX_TIMEOUT_SECONDS1800Maximum script timeout
RUST_LOGinfoCore log verbosity; accepts levels such as debug and module filters such as info,sqlx=warn
CITADEL_LOG_FORMATTextCore log format: Text or Json

These settings do not override resource permissions, enabled states, or license requirements. Resource schedules are configured on the resource itself.

For first-run password-file bootstrap, use the complete unattended setup procedure. For common configuration changes, see Docker Compose configuration.

Concurrency limits

Core prepares and tracks work; the execution host admits the expensive operation. Scripts run on Core or an Agent according to their Run on setting.

SettingDefaultWhat it limits
CITADEL_BUILDS_PREPARATION_SLOTS2Git preparation and outbound build contexts on Core; accepts 1–16
CITADEL_BUILDS_TRACKING_SLOTS16Active build controllers, including retained completion results; accepts 1–128
CITADEL_BACKUPS_TRACKING_SLOTS16Active backup, restore and repository operation controllers combined; accepts 1–128
CITADEL_SCRIPTS_MAX_PARALLEL_RUNS4Core Deno runs, including manual runs and tests; accepts 1–64
CITADEL_AGENT_BUILD_SLOTS2Complete builds on an Agent, including registry pushes and cleanup; accepts 1–64
CITADEL_AGENT_DATA_SLOTS2Backup items, restores and repository maintenance on an Agent; accepts 1–64
CITADEL_AGENT_HEAVY_SLOTS2Builds, data operations and scripts combined on an Agent; accepts 1–64

A small installation can keep these defaults. Three independent Agents with two build slots and two heavy-work slots each can execute six builds while Core prepares at most two contexts. Set a pool's Maximum concurrent builds high enough for the intended workload; the effective limit is the smallest of the pool policy, Agent build capacity and Agent shared heavy-work capacity. Aliases of one Docker daemon share its capacity.

Tracking slots do not launch extra Docker work when a host is full. A multi-volume backup processes its items sequentially and reserves the host for each item. Repository, source and restore-destination locks remain held for the protected operation. These locks renew during execution; expiry alone never permits a second operation to use storage whose cleanup is uncertain. A source-contention retry retains the resolved source plan.

Core-local builds, backup helpers and system-backup staging share two local heavy-work slots. Edge contexts have a separate 32 MiB transfer budget, charged for raw and encoded payloads; output has its own 32 MiB budget. Control commands retain independent headroom. These are bounded implementation limits, not extra configuration knobs.

More slots do not make one job faster or provide CPU/memory isolation. Increase capacity only when the executing hosts have spare resources. Lowering a pool policy prevents new over-limit admission while accepted work drains. Agent capacities are startup settings: let active work finish before recreating an Agent to change them.

Builds wait when a host is offline or busy. A known pre-execution refusal retries with a 5–15 second backoff, preserving a commit once resolved. All queued builds, including those waiting for pool capacity, expire after 24 hours. Ambiguous execution is never automatically replayed. All script runs, including manual runs and tests, queue when their selected execution hosts are full or offline. Closing a progress sheet does not cancel its run.

After changing Core variables, recreate Core with docker compose up -d server, retaining the project's overlay options. Set Agent capacities on each Agent and recreate it to apply them. The old lifecycle-wide build/backup worker variables are no longer read.

Core also persists remote build reservations per Docker daemon, so aliases do not multiply capacity. Failed or interrupted execution retains its reservation unless the same Agent confirms cleanup. Core stores that evidence before releasing capacity with the final run result; restarting an Agent is not cleanup evidence. Core also rechecks terminal reservations in batches of 16, with five-second Agent checks and 30 seconds between batches. Confirmed cleanup releases capacity without retrying the build or changing its outcome. Unreachable Agents, changed Agent identities and uncertain execution remain held. Administrators can resolve terminal holds from the build run sheet after verifying cleanup and restarting the original Agent on the same Docker daemon. Their acknowledgment is audited without retrying the build. These reconciliation bounds are not additional .env settings.

HTTP listeners and runtime limits

Host-published ports above are separate from the container listeners. The supplied Compose packages use the listen-port settings for container port targets and the health check reads the same HTTP port. Bare binaries have no static-root default; the packaged image sets CITADEL_STATIC_ROOT=/app/wwwroot and accepts an override.

SettingDefault / units
CITADEL_HTTP_LISTEN_PORT / CITADEL_EDGE_GRPC_LISTEN_PORT8000 / 8001
CITADEL_HTTP_CORS_ORIGIN_<n>No origins; indexed list ordered numerically, sparse indices allowed; blank entries rejected
CITADEL_HTTP_FORWARDED_LIMIT1 trusted hop
CITADEL_HTTP_BODY_LIMIT_BYTES2097152 bytes
CITADEL_HTTP_REQUESTS_PER_MINUTE600
CITADEL_DATABASE_MAX_CONNECTIONS5
CITADEL_DOCKER_SOCKET/var/run/docker.sock inside Core
CITADEL_DOCKER_TIMEOUT_SECONDS10 seconds
CITADEL_STATIC_ROOTOptional frontend directory
CITADEL_SHUTDOWN_TIMEOUT_SECONDS10 seconds
CITADEL_LOG_COLOR_ENABLEDTerminal detection; JSON always omits ANSI color
CITADEL_STATS_BATCH_SIZE500
CITADEL_EVENT_QUEUE_CAPACITY256
CITADEL_SWARM_RECONCILIATION_INTERVAL_SECONDS1800 seconds
CITADEL_RETENTION_INTERVAL_SECONDS900 seconds
CITADEL_REALTIME_ENABLEDtrue; disabled realtime skips its other settings
CITADEL_REALTIME_QUEUE_CAPACITY / CITADEL_REALTIME_MAX_CONNECTIONS64 / 8
CITADEL_REALTIME_SUBSCRIBE_TIMEOUT_SECONDS / CITADEL_REALTIME_SEND_TIMEOUT_SECONDS5 / 2 seconds
CITADEL_REALTIME_AUTH_RECHECK_SECONDS30 seconds
CITADEL_REALTIME_SNAPSHOT_LIMIT1024

In the development overlay, CITADEL_DEV_DOCKER_SOCKET selects the host socket mount source; Core and the entrypoint both use /var/run/docker.sock inside the container. The launcher derives this source from its native CITADEL_DOCKER_SOCKET.

Identity and bootstrap

SettingDefault / units
CITADEL_JWT_ACCESS_TOKEN_LIFETIME_MINUTES15 minutes
CITADEL_JWT_REFRESH_TOKEN_LIFETIME_DAYS30 days
CITADEL_MFA_CHALLENGE_LIFETIME_MINUTES / CITADEL_MFA_SETUP_LIFETIME_MINUTES5 / 10 minutes
CITADEL_MFA_MAX_FAILED_ATTEMPTS / CITADEL_MFA_RECOVERY_CODE_COUNT5 / 10
CITADEL_SERVICE_ACCOUNTS_DEFAULT_TOKEN_LIFETIME_DAYS / CITADEL_SERVICE_ACCOUNTS_MAXIMUM_TOKEN_LIFETIME_DAYS90 / 365 days
CITADEL_SERVICE_ACCOUNTS_MAXIMUM_ACTIVE_TOKENS_PER_ACCOUNT10
CITADEL_SERVICE_ACCOUNTS_LAST_USED_WRITE_INTERVAL_MINUTES5 minutes
CITADEL_SERVICE_ACCOUNTS_LAST_USED_TRACKING_CAPACITY10000
CITADEL_BOOTSTRAP_ADMIN_NAME / CITADEL_BOOTSTRAP_ADMIN_EMAIL / CITADEL_BOOTSTRAP_ADMIN_PASSWORD_FILEOptional unattended setup; resolved only when setup is required

Keep generated keys in jwtsecret and secret-encryption-key under the persistent data root. Routine configuration changes do not require copying them into .env. If you explicitly configured CITADEL_JWT_KEY or CITADEL_SECRETS_ENCRYPTION_KEY, preserve those values across upgrades and recovery. Offline recovery never generates replacement keys.

Execution tools, paths and scheduling

SettingDefault / units
CITADEL_DOCKER_PATH / CITADEL_RESTIC_PATH / CITADEL_SHOUTRRR_PATHdocker / restic / shoutrrr
CITADEL_DENO_PATHdeno
CITADEL_INTERNAL_BASE_URLhttp://127.0.0.1:<resolved HTTP listen port>; Script API origin, separate from the public browser URL. Allow its hostname in CITADEL_HTTP_ALLOWED_HOSTS. Direct TLS requires an explicit reachable HTTPS origin and a certificate trusted by Deno; setup and trust limits.
CITADEL_GIT_KNOWN_HOSTS_PATH<data root>/git-known-hosts
CITADEL_SCRIPTS_WORK_DIR<data root>/scripts/runs; each private run directory also holds its Deno cache. Source and cache are removed together after execution.
CITADEL_SCRIPTS_ALLOW_NETOptional Deno network allowlist; existing comma-separated format
CITADEL_SCRIPTS_MAX_LOG_BYTES1048576 bytes, clamped to 1024–16777216
CITADEL_SCRIPTS_POLL_INTERVAL_SECONDS / CITADEL_SCRIPTS_SCHEDULE_POLL_INTERVAL_SECONDS2 / 30 seconds
CITADEL_BUILDS_PREPARATION_SLOTS / CITADEL_BUILDS_TRACKING_SLOTS2 / 16; build concurrency
CITADEL_BUILDS_RUN_CLEANUP_ENABLED / CITADEL_BUILDS_RUN_RETENTION_DAYStrue / 90 days; disabling cleanup disables retention
CITADEL_BACKUPS_ENABLED / CITADEL_BACKUPS_TRACKING_SLOTStrue / 16; backup, restore, and repository operation tracking combined, see concurrency limits
CITADEL_BACKUPS_POLL_INTERVAL_SECONDS / CITADEL_BACKUPS_SCHEDULE_POLL_INTERVAL_SECONDS2 / 30 seconds
CITADEL_BACKUPS_WORKING_DIRECTORY / CITADEL_BACKUPS_CORE_DATA_PATH<data root>/backups/work / <data root>
CITADEL_BACKUPS_ALLOWED_CORE_PATH_<n><data root>/backups/repositories; indices 0–127, numeric order, sparse indices allowed; paths retain spaces and commas
CITADEL_BACKUPS_DEFAULT_TIMEOUT_SECONDS120 seconds
CITADEL_BACKUPS_MAX_LOG_BYTES / CITADEL_BACKUPS_MAX_LOG_LINE_BYTES1048576 / 8192 bytes
CITADEL_BACKUPS_REPOSITORY_LEASE_SECONDS / CITADEL_BACKUPS_SOURCE_LEASE_SECONDS300 / 300 seconds; minimum 90 each, renewed every 30 seconds
CITADEL_RESTIC_IMAGErestic/restic:0.18.1
CITADEL_VOLUME_HELPER_IMAGEOptional helper image override

Core connections to Agents

SettingDefault / units
CITADEL_CORE_AGENT_ADDRESS / CITADEL_CORE_AGENT_PRIVATE_KEY_PATHOptional pair; configure both together
CITADEL_CORE_AGENT_TIMEOUT_SECONDSRequired positive seconds when that pair is set
CITADEL_CORE_AGENT_ALLOW_INSECUREtrue; set false to require HTTPS
CITADEL_CORE_AGENT_CA_CERTIFICATE_PATHOptional PEM trust bundle for Direct Agents
CITADEL_NODE_AGENT_CA_CERTIFICATE_PATHOptional Core CA distributed to Swarm node agents
CITADEL_EDGE_AGENT_IMAGEOptional full image override
CITADEL_EDGE_AGENT_IMAGE_REPOSITORY / CITADEL_EDGE_AGENT_IMAGE_TAGghcr.io/citadel-p/citadel.agent / current build version; used without the full-image override
CITADEL_EDGE_AGENT_SUPPORTED_NODE_ARCHITECTURE_<n>amd64, arm64; indices 0–31, numeric order, sparse indices allowed; blank/unknown values rejected
CITADEL_EDGE_AGENT_NODE_AGENT_BOOTSTRAP_MINUTES / CITADEL_EDGE_AGENT_NODE_AGENT_SETUP_MINUTES10 / 5 minutes
CITADEL_EDGE_AGENT_NODE_AGENT_REMOVAL_GRACE_MINUTES10 minutes
CITADEL_EDGE_AGENT_NODE_AGENT_LIMIT_NANO_CPUS500000000 nanocpus
CITADEL_EDGE_AGENT_NODE_AGENT_LIMIT_MEMORY_BYTES / CITADEL_EDGE_AGENT_NODE_AGENT_PIDS_LIMIT536870912 bytes / 256 processes

Direct Agent settings

A Direct Agent is the Agent connector in the Platform form, also called a regular or inbound Agent in these guides. Core initiates the connection to it; this connection type is separate from Core's TLS mode.

Set these on the Agent container. The generated setup command supplies its identity and Core public key; follow the Direct Agent guide and TLS configuration.

SettingDefault / requirementPurpose
CITADEL_AGENT_MODEUnset selects Direct modeSet edge only for an Edge Agent
CITADEL_AGENT_IDRequiredPlatform or dedicated Build Pool UUID from setup; remains stable when the address changes
CITADEL_CORE_PUBLIC_KEYRequiredBase64 Ed25519 public key emitted by Core setup
CITADEL_AGENT_PORT9000Agent listener port inside the container
CITADEL_AGENT_BUILD_SLOTS2Complete build attempts on this Agent, including pushes; accepts 1–64
CITADEL_AGENT_DATA_SLOTS2Data operations including helper cleanup; accepts 1–64
CITADEL_AGENT_HEAVY_SLOTS2Builds, data operations, and scripts combined on an Agent; accepts 1–64
CITADEL_AGENT_TLS_MODEDisabledDirect enables TLS and requires the certificate and key below
CITADEL_AGENT_TLS_CERTIFICATE_PATH / CITADEL_AGENT_TLS_PRIVATE_KEY_PATHRequired for Direct TLSPEM certificate chain and private key inside the Agent container
DOCKER_HOSTunix:///var/run/docker.sockDocker endpoint used by the Agent; also applies to Edge Agents
CITADEL_HOST_ROOT/hostHost filesystem mount used for storage metrics; also applies to Edge Agents

Edge Agent settings

Set these on the Edge Agent container. Use the command generated by Citadel; see the Edge Agent guide. Edge mode does not read the Direct-only identity, Core public-key, or listener TLS settings above.

SettingDefault / requirementPurpose
CITADEL_AGENT_MODEedge requiredSelects the outbound Edge connection
CITADEL_AGENT_BUILD_SLOTS2Shared build capacity across Edge commands and replacement connections; accepts 1–64
CITADEL_AGENT_DATA_SLOTS2Data operations including helper cleanup; accepts 1–64
CITADEL_AGENT_HEAVY_SLOTS2Builds, data operations, and scripts combined on an Agent; accepts 1–64
CITADEL_CORE_URLRequiredCore's Edge gRPC URL; it can share the browser URL when a proxy routes gRPC to the Edge listener
CITADEL_EDGE_AGENT_PROFILEedge-agentedge-build-agent selects a dedicated Build Pool Agent; Swarm node profiles are generated by Core
CITADEL_EDGE_ENROLLMENT_TOKENRequired for initial enrollmentEnrollment credential from Citadel; an enrolled Agent reconnects using its persisted identity
CITADEL_EDGE_CORE_CA_CERTIFICATE_PATHOptionalPEM trust bundle for an HTTPS Core endpoint using a private CA
CITADEL_EDGE_AGENT_KEY_PATH/app/data/edge-agent.keyPersisted private-key path
CITADEL_EDGE_IDENTITY_PATH/app/data/edge-agent.identity.jsonPersisted enrollment identity path

Preserve the Agent's data volume. Dedicated Build Pool setup commands can select profile-specific key and identity paths; keep those generated values. Core also supplies Swarm node bootstrap and identity variables automatically.

Script execution capacity

VariableDefaultPurpose
CITADEL_SCRIPTS_MAX_PARALLEL_RUNS4Deno processes running locally on Core.
CITADEL_SCRIPTS_MAX_REMOTE_RUNS16Remote script runs Core tracks concurrently (1–256); no local Deno process is created for them.
CITADEL_AGENT_SCRIPT_SLOTS4Concurrent Deno scripts on each Agent (1–64), also bounded by CITADEL_AGENT_HEAVY_SLOTS.

All triggers enter the durable queue. A busy host defers work; the Core local limit does not prevent execution on a free Agent. Increase Agent capacity only when that host has enough memory and CPU for scripts alongside builds and backups. Remote scripts use Core's public HTTPS origin, not Core's loopback API address.

On this page