Environment Variable Reference
This is the authoritative table of environment variables recognized by Open ACE, grouped by where they are declared: .env.example (the 13 user-facing variables for Docker Compose), the Compose files themselves, the scheduler service, the Kubernetes ConfigMap, and application code. Bare-metal deployments set the same variables through their service manager or shell.
Rotation impact describes what happens when you change the value on an existing deployment.
Table of Contents
- Security Mode and Secrets (.env.example)
- Database (.env.example)
- Networking (.env.example)
- Workspace Mode (.env.example)
- Image and Compose Internals (docker-compose.yml)
- Scheduler Service (docker-compose.yml)
- Kubernetes (k8s/configmap.yaml)
- Application-Level Variables (code)
- Auto-Generated Secrets
Security Mode and Secrets (.env.example)
| Variable | Default | Purpose | Rotation impact | Source |
|---|---|---|---|---|
OPENACE_SECURITY_MODE | unset (Compose default: development) | Security mode: production (strict checks, secrets required), pilot (auto-generate with strong warnings), development (auto-generate). Required explicitly in production (Issue #2331) | Switching to production without explicit secrets refuses startup; switching away weakens checks | .env.example, docker-compose.yml, app/utils/security_mode.py |
SECRET_KEY | empty | Flask session signing key (>= 32 characters in production) | Rotation invalidates every logged-in session — all users are logged out; no data loss | .env.example, docker-compose.yml |
OPENACE_ENCRYPTION_KEY | empty | Fernet key material (>= 32 characters) encrypting all 8 secret stores (SSO, SMTP, API keys, integrations); also signs proxy tokens | Rotation without re-encrypting makes stored secrets permanently undecryptable and invalidates proxy tokens — see KEY_MANAGEMENT.md | .env.example, docker-compose.yml |
UPLOAD_AUTH_KEY | empty | Shared secret for workspace file-upload endpoints; empty disables the endpoint | Rotation breaks upload clients until they use the new key | .env.example, docker-compose.yml |
SSO_ALLOWED_REDIRECT_DOMAINS | empty (only localhost allowed) | Comma-separated allowlist of domains SSO may redirect to after login (Issue #3224) | Changing the list changes where SSO logins may land; removing a domain blocks its redirects | .env.example |
Database (.env.example)
| Variable | Default | Purpose | Rotation impact | Source |
|---|---|---|---|---|
DB_USER | ace | PostgreSQL user (also POSTGRES_USER) | Changing it requires re-provisioning the database user; not a routine rotation | .env.example, docker-compose.yml |
DB_PASSWORD | empty (Compose falls back to dev-password-change-in-production) | PostgreSQL password; passed both inside DATABASE_URL and standalone for the security baseline check (Issue #3095). Strong value (>= 9 chars) required in production (Issue #1893) | Must be changed in .env AND in PostgreSQL (ALTER USER ... WITH PASSWORD), then the stack restarted; mismatch = connection failures | .env.example, docker-compose.yml |
DB_NAME | ace | PostgreSQL database name (also POSTGRES_DB) | Renaming requires migrating the database; not a routine rotation | .env.example, docker-compose.yml |
Networking (.env.example)
| Variable | Default | Purpose | Rotation impact | Source |
|---|---|---|---|---|
PORT | 19888 | Host port mapping for the web UI; the container always listens on 19888 (Issue #1372) | None on data; users and bookmarks must use the new port | .env.example, docker-compose.yml |
SERVER_IP | empty (auto-detected) | Address browsers use to reach the service (Issue #1306). The entrypoint auto-detects, rejects unreachable reserved ranges (e.g. 0.0.0.0/8 falls back to localhost); set explicitly for access from other machines, e.g. SERVER_IP=192.168.1.100 | Changes generated URLs (workspace links, config server_url) | .env.example, docker-compose.yml, docker-entrypoint.sh |
WORKSPACE_PORT_RANGE_START | 3100 | Start of the published port range for per-user workspace WebUI instances; Compose expands the range into individual port mappings | Shrinking the range blocks instances on removed ports; requires recreating containers | .env.example, docker-compose.yml |
WORKSPACE_PORT_RANGE_END | 3200 | End of the published workspace port range | Same as above | .env.example, docker-compose.yml |
Workspace Mode (.env.example)
| Variable | Default | Purpose | Rotation impact | Source |
|---|---|---|---|---|
WORKSPACE_ISOLATION_BACKEND | shared | Workspace isolation backend (Issue #3446, v2.1.0; replaces WORKSPACE_MULTI_USER_MODE / WORKSPACE_REQUIRED_ISOLATION_LEVEL): shared = one shared WebUI, plain = one OS account per user (needs the root multi-user overlay with OPENACE_ALLOW_ROOT_MULTI_USER=1 and OPENACE_CONFIG_DIR=/home/open-ace/.open-ace), opensandbox = per-user pods on Kubernetes. Docker installs accept only these three values; the config.json counterpart is workspace.isolation (see WORKSPACE_ISOLATION.md and MULTI_USER_WORKSPACE.md) | Changes the runtime security posture; takes effect after restart | .env.example, docker-compose.yml, docker-entrypoint.sh |
WORKSPACE_MULTI_USER_MODE | false | Superseded in v2.1.0 (Issue #3446) by WORKSPACE_ISOLATION_BACKEND — use WORKSPACE_ISOLATION_BACKEND=plain for the old true. Historically: true enabled multi-user workspace mode (per-user system accounts + WebUI instances, container runs as root; with OPENACE_ALLOW_ROOT_MULTI_USER=1 and OPENACE_CONFIG_DIR=/home/open-ace/.open-ace, Issue #2242) | Setting it (or WORKSPACE_REQUIRED_ISOLATION_LEVEL) now makes docker-entrypoint.sh refuse to start, naming the replacement | pre-v2.1.0 .env.example / docker-compose.yml; rejected by docker-entrypoint.sh |
Image and Compose Internals (docker-compose.yml)
Declared inline by the Compose files; override in .env when needed.
| Variable | Default | Purpose | Rotation impact | Source |
|---|---|---|---|---|
IMAGE_NAME | ghcr.io/open-ace/open-ace:latest | Application image for the open-ace and scheduler services | Pinning a different tag is the standard upgrade/rollback lever (see UPGRADING.md) | docker-compose.yml |
BASE_REGISTRY | docker.io | Base-image registry override (e.g. docker.m.daocloud.io for mainland China); affects the build stage and the PostgreSQL image reference | Rebuild/re-pull required | docker-compose.yml |
DATABASE_URL | constructed | postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME} — assembled by Compose from the DB_* variables | Follows DB_* rotation | docker-compose.yml |
WORKSPACE_BASE_DIR | /workspace (Compose) | Root directory for user projects; supports comma-separated multiple directories. Bare-metal root runs must set it explicitly (/root is rejected by the blacklist) | Moving it orphans existing project directories | docker-compose.yml, app/utils/workspace.py |
OPENACE_CONFIG_DIR | /home/open-ace/.open-ace (overlay) | Config persistence directory; must match the config-data volume mount path | Pointing it elsewhere loses auto-generated config and secrets | docker-compose.multi-user.yml |
OPENACE_ALLOW_ROOT_MULTI_USER | 1 (overlay) | Explicit opt-in for root multi-user execution (Issue #1893) | Removing it makes multi-user mode refuse to start | docker-compose.multi-user.yml |
OPENACE_AGENT_STATE_ROOT | /var/lib/openace/agent-state | Carried CLI transcripts; must match exactly between the app and scheduler services (Issue #3237) | Mismatch breaks transcript purging between services | docker-compose.yml |
FETCH_USE_SUDO | false | Whether fetchers use sudo (container runs as root, so sudo is unnecessary; Issue #1121) | — | docker-compose.yml |
FLASK_ENV | production (scheduler service) | Legacy environment flag; as a security-mode signal it is deprecated and removed in v2.1.0 — migrate to OPENACE_SECURITY_MODE (use scripts/migrate_security_mode.sh) | Relying on it for security mode stops working in v2.1.0 | docker-compose.yml, .env.example, app/utils/security_mode.py |
Scheduler Service (docker-compose.yml)
The scheduler service (profile scheduler in single-user mode; enabled by default under the multi-user overlay) uses:
| Variable | Default | Purpose | Rotation impact | Source |
|---|---|---|---|---|
SCHEDULER_MODE | scheduler | Marks the process as the background scheduler worker (leader election ensures one active scheduler, Issue #2187) | — | docker-compose.yml |
SCHEDULER_HEARTBEAT_INTERVAL | 10 | Heartbeat interval (seconds) between scheduler beats | Mismatched values across replicas skew liveness detection | docker-compose.yml |
SCHEDULER_HEARTBEAT_TIMEOUT | 60 | Seconds after which a silent scheduler is considered dead | Same as above | docker-compose.yml |
SCHEDULER_LOCK_TIMEOUT | 1800 | Seconds before the scheduler leadership lock expires | Too-low values can cause duplicate schedulers during long tasks | docker-compose.yml |
SCHEDULER_METRICS_PORT | 9090 | Port for the scheduler metrics/health endpoint (healthcheck target) | Container-internal; no host mapping | docker-compose.yml |
Kubernetes (k8s/configmap.yaml)
Declared by the Kubernetes manifests (see KUBERNETES.md); several overlap with the variables above.
| Variable | Default (manifest) | Purpose | Rotation impact | Source |
|---|---|---|---|---|
LOG_LEVEL | INFO | Application log level | None; takes effect after restart | k8s/configmap.yaml |
AUDIT_LOG_RETENTION_DAYS | 90 | Audit log retention window (days) | Shortening deletes older audit entries on the next cleanup | k8s/configmap.yaml |
DATA_RETENTION_DAYS | 365 | Data retention window (days) for cleanup jobs | Shortening deletes older retained data on the next cleanup | k8s/configmap.yaml |
DB_HOST / DB_PORT | postgres / 5432 | Database endpoint inside the cluster | Restart required | k8s/configmap.yaml |
REDIS_HOST / REDIS_PORT | redis / 6379 | Redis endpoint | Restart required | k8s/configmap.yaml |
ENABLE_SSO / ENABLE_MULTI_TENANT / ENABLE_AUDIT_LOG / ENABLE_CONTENT_FILTER | true | Feature flags | Toggling enables/disables features after restart | k8s/configmap.yaml |
REDIS_PASSWORD | placeholder | Redis password (secret; replace the placeholder before production) | Must match the Redis deployment | k8s/configmap.yaml (Secret section) |
Application-Level Variables (code)
| Variable | Default | Purpose | Rotation impact | Source |
|---|---|---|---|---|
OPENACE_PLATFORM_ADMIN_STRICT_MODE | false | When true, only explicit platform_admin role passes platform-admin checks; legacy admin no longer does (Issue #2332). Migrate existing role='admin' accounts to platform_admin FIRST, then enable and restart every process; each logs Platform admin strict mode: ENABLED at startup | Enabling without migrating accounts locks legacy admins out | app/auth/permissions.py |
OPENACE_ENCRYPTION_KEYS | unset | JSON data-key registry: {"keys": [{"id": 1-255, "value": "...", "status": "active|deprecated|revoked"}, ...], "primary_key_id": N}; max 5 keys, exactly 1 active; takes precedence over OPENACE_ENCRYPTION_KEY; hot-reloaded roughly every 5 seconds | Key changes re-bind new ciphertexts (v1k<id>: prefix); see KEY_MANAGEMENT.md | app/utils/encryption_key_registry.py |
OPENACE_CORS_ALLOWED_ORIGINS | unset | Comma-separated explicit API CORS allowlist for non-loopback WebUI origins | Tightening blocks requests from removed origins | app/__init__.py |
OPENACE_WS_MAX_MESSAGE_BYTES | 8388608 | Maximum inbound browser WebSocket message size for the terminal / VSCode raw bridges | Lower values reject large paste payloads | app/ws_frame.py |
OPENACE_TEST_MODE | unset | 1 marks a CI/test context: skips production-level security checks | Never enable in production | app/utils/security_mode.py |
OPENCLAW_TOKEN | unset | OpenClaw API token for data collection | Rotation requires updating the collector | scripts/fetch_openclaw.py |
OPENACE_REPLICA_ENDPOINTS | unset | Comma-separated replica endpoints queried for encryption-key config-version sync status | Informational only (sync dashboard) | app/routes/encryption_keys.py |
Auto-Generated Secrets
For the single-user development path, missing secrets are not an error: docker-entrypoint.sh generates strong random values on first start and persists them to generated-secrets.env inside the config-data volume (/home/open-ace/.open-ace/generated-secrets.env); later restarts reuse them without rotating. Bare-metal development works the same via ~/.open-ace/generated-secrets.env (Issue #2667). Explicitly set values in .env always take precedence and are never written to that file. Production mode requires explicit secrets — see DEPLOYMENT.md.