Deployment
ACE = AI Computing Explorer
This guide covers deploying Open ACE: the Docker production path, a local trial without Docker, configuration, data persistence, data collection, and troubleshooting.
Table of Contents
- Read This First: Security Mode
- Quick Start (Docker)
- Local Trial (without Docker)
- Configuration
- Data Persistence
- Management Commands
- Data Collection
- Deployment Scenarios
- System Services
- Multi-User Workspaces
- Upgrading
- Uninstallation
- Troubleshooting
- Related Documentation
Read This First: Security Mode
Before any production deployment, set the security mode explicitly (Issue #2331):
OPENACE_SECURITY_MODE=production
production— strict checks.SECRET_KEYandOPENACE_ENCRYPTION_KEY(>= 32 characters each) and a strongDB_PASSWORDmust be set explicitly; weak or placeholder values refuse startup.pilot— trial environments. Missing secrets are auto-generated with strong warnings.development— local development. Missing secrets are auto-generated with general warnings.
The fastest correct path is the bootstrap script, which writes and validates .env for you (OPENACE_SECURITY_MODE=production, strong SECRET_KEY / OPENACE_ENCRYPTION_KEY / DB_PASSWORD):
./scripts/bootstrap-compose-env.sh
Notes:
- The single-user
docker-compose.ymldefaults todevelopmentso a one-click local start works; production must set the variable explicitly. - The multi-user overlay fails fast with
Run ./scripts/bootstrap-compose-env.sh firstif the required secrets are missing. - In production, also set
SSO_ALLOWED_REDIRECT_DOMAINS(comma-separated domain list); without it, SSO login from non-localhost origins is blocked (Issue #3224). - See ENV_REFERENCE.md for the authoritative environment variable table and KEY_MANAGEMENT.md for secret rotation impact.
Quick Start (Docker)
The Docker Compose stack runs the Open ACE container plus a PostgreSQL 15 database, with all persistent data in named volumes.
# 1. Clone the repository on the server
git clone https://github.com/open-ace/open-ace.git
cd open-ace
# 2. Generate and validate .env (SECRET_KEY, OPENACE_ENCRYPTION_KEY, DB_PASSWORD, ...)
./scripts/bootstrap-compose-env.sh
# 3. Start (pulls the pre-built ghcr.io/open-ace/open-ace:latest image by default)
docker compose up -d --wait
# 4. Verify
docker compose ps
docker compose logs -f open-ace
Then open http://localhost:19888 and log in with the default credentials:
Username: admin
Password: admin123
Important: change the default password immediately after first login.
Details:
- The image is
ghcr.io/open-ace/open-ace:latest(override withIMAGE_NAME, e.g. to pinghcr.io/open-ace/open-ace:v2.1.0or a locally builtopen-ace:dev). Developers can build locally withdocker build -t open-ace:dev --target production .. docker compose downdoes not delete data; all state lives in named volumes (see Data Persistence).- The container runs as the non-root
open-aceuser (uid 1000) by default. Only multi-user mode needs root (see Multi-User Workspaces). - The scheduler worker is an optional profile in single-user mode:
docker compose --profile scheduler up -d. - For HTTPS, put a reverse proxy in front following NGINX.md. For Kubernetes, see KUBERNETES.md.
Offline Installation
For servers without internet access, use the image export script from scripts/install-central/docker-method/:
# On a connected machine: build and export both images
./scripts/install-central/docker-method/export-image.sh --build --compress
# Copy open-ace-images.tar.gz (and install.sh) to the target server, then
./install.sh
The plain Docker equivalent also works:
docker pull ghcr.io/open-ace/open-ace:latest
docker save ghcr.io/open-ace/open-ace:latest | gzip > open-ace-images.tar.gz
gunzip -c open-ace-images.tar.gz | docker load
Pre-built images are published to GitHub Container Registry for every release (ghcr.io/open-ace/open-ace:latest, :vX.Y.Z, :X.Y.Z, :X.Y, :X) and are linux/amd64 only; Apple Silicon hosts run them under emulation. To build the image from your checkout instead (e.g. when ghcr.io is unreachable), run docker compose up -d --build --wait.
For mainland China networks where Docker Hub is unreachable, set BASE_REGISTRY=docker.m.daocloud.io in .env so both the build and the PostgreSQL image resolve from a mirror.
Local Trial (without Docker)
# Install dependencies
pip install -r requirements.txt
# Initialize configuration
python3 cli.py config init
# Apply database migrations (required before first start)
alembic upgrade head
# Start web server
python3 server.py
# Visit http://localhost:19888
Development and pilot modes support zero-config startup (Issue #2667): a missing OPENACE_ENCRYPTION_KEY is auto-generated on first start and persisted to ~/.open-ace/generated-secrets.env (reused across restarts, never rotated). Production mode still requires explicit secrets.
Configuration
Configuration has two layers:
- Environment variables (
.envfor Docker Compose) control deployment-level concerns: ports, database, security mode, multi-user switches. The authoritative table with defaults and rotation impact is ENV_REFERENCE.md. config.jsoncontrols application behavior (tools to collect, email, workspace). On bare metal it lives at~/.open-ace/config.json. In Docker it is auto-generated inside theconfig-datavolume at/home/open-ace/.open-ace/config.jsonon first start; you can also mount a custom file read-only at that path. See CONFIG_REFERENCE.md for the full reference.
Environment variables most deployments touch:
| Variable | Default | Purpose |
|---|---|---|
PORT | 19888 | Host port for the web UI (container always listens on 19888) |
SERVER_IP | auto-detected | Address browsers use to reach the server (Issue #1306); set explicitly when accessing from other machines, e.g. SERVER_IP=192.168.1.100 |
WORKSPACE_PORT_RANGE_START / WORKSPACE_PORT_RANGE_END | 3100 / 3200 | Published port range for per-user workspace WebUI instances |
WORKSPACE_ISOLATION_BACKEND | shared | Isolation backend (Issue #3446): plain = one OS account per user (needs the multi-user overlay), opensandbox = OpenSandbox pods; replaces WORKSPACE_MULTI_USER_MODE |
DB_USER / DB_NAME | ace / ace | PostgreSQL user and database name |
DB_PASSWORD | dev default | Strong password required in production |
IMAGE_NAME | ghcr.io/open-ace/open-ace:latest | Application image |
BASE_REGISTRY | docker.io | Base image registry override |
Port Configuration
- Bare metal: set
server.web_port/server.web_hostin~/.open-ace/config.json, then restart. - Docker: temporary change with
PORT=5001 docker compose up -d, or permanent viaecho "PORT=5001" >> .envfollowed bydocker compose down && docker compose up -d.
Firewall (for external access):
# Ubuntu/Debian
sudo ufw allow 19888/tcp
# CentOS/RHEL
sudo firewall-cmd --add-port=19888/tcp --permanent
sudo firewall-cmd --reload
Data Persistence
There is no ./data directory. All persistent data lives in four named volumes declared by docker-compose.yml:
| Volume | Mount point | Content |
|---|---|---|
config-data | /home/open-ace/.open-ace | config.json and generated-secrets.env |
postgres-data | /var/lib/postgresql/data | PostgreSQL database (users, sessions, all application data) |
workspace-data | /workspace | Per-user project directories |
agent-state | /var/lib/openace | Carried CLI transcripts, shared by the app and scheduler services (Issue #3237) |
Additionally, ./logs on the host is bind-mounted to /app/logs. Multi-user deployments add a fifth volume, home-data (mounted at /home), for user home directories (Issue #2729).
Inspect volumes:
docker volume ls | grep open-ace
# expect: agent-state, config-data, postgres-data, workspace-data
Back up the database with pg_dump (see DATABASE_BACKUP.md); the postgres-data volume is the source of truth.
Management Commands
cd /path/to/open-ace
# View status
docker compose ps
# View logs
docker compose logs -f
# View open-ace logs only
docker compose logs -f open-ace
# Restart services
docker compose restart
# Restart open-ace only
docker compose restart open-ace
# Stop services (volumes are preserved)
docker compose down
# Start services
docker compose up -d
Data Collection
Open ACE aggregates usage from local AI CLI tools via fetcher scripts in scripts/:
| Script | Source |
|---|---|
fetch_claude.py | ~/.claude/projects (Claude Code JSONL) |
fetch_qwen.py | ~/.qwen/projects (Qwen Code JSONL) |
fetch_codex.py | ~/.codex/sessions (Codex CLI JSONL) |
fetch_zcode.py | ~/.zcode/cli/db/db.sqlite (ZCode CLI SQLite) |
fetch_openclaw.py | ~/.openclaw/agents (OpenClaw) |
Manual collection:
python3 scripts/fetch_claude.py
python3 scripts/fetch_qwen.py
python3 scripts/fetch_codex.py
python3 scripts/fetch_zcode.py
python3 scripts/fetch_openclaw.py
# Collect for specific days
python3 scripts/fetch_claude.py --days 7
Scheduled collection (bare metal deployments):
crontab -e
30 0 * * * cd /path/to/open-ace && python3 scripts/fetch_claude.py >> logs/cron.log 2>&1
35 0 * * * cd /path/to/open-ace && python3 scripts/fetch_qwen.py >> logs/cron.log 2>&1
40 0 * * * cd /path/to/open-ace && python3 scripts/fetch_codex.py >> logs/cron.log 2>&1
45 0 * * * cd /path/to/open-ace && python3 scripts/fetch_zcode.py >> logs/cron.log 2>&1
50 0 * * * cd /path/to/open-ace && python3 scripts/fetch_openclaw.py >> logs/cron.log 2>&1
Deployment Scenarios
1. Single Machine (recommended for personal use)
All components on one machine: python3 server.py plus the cron jobs above.
2. Central Server + Remote Collectors
For distributed environments, deploy with scripts/manage.py. Its usage is a positional action plus an optional --remote switch:
python3 scripts/manage.py init # Initialize configuration
python3 scripts/manage.py deploy # Deploy to ~/open-ace/
python3 scripts/manage.py install # Install as a system service
python3 scripts/manage.py start # Start web server (deployment dir)
python3 scripts/manage.py stop # Stop web server
python3 scripts/manage.py restart # Restart web server
python3 scripts/manage.py status # Check service status
python3 scripts/manage.py show # Show current configuration
python3 scripts/manage.py --remote deploy # Deploy to remote machine
python3 scripts/manage.py --remote sync # Sync files to remote
python3 scripts/manage.py --remote status # Check remote status
--remote supports only deploy, sync, and status; sync is remote-only.
Alternatively, configure a remote machine manually:
scp -r open-ace user@remote:/path/to/
ssh user@remote "cd /path/to/open-ace && python3 scripts/fetch_openclaw.py"
System Services
Linux (systemd)
Create /etc/systemd/system/open-ace.service:
[Unit]
Description=Open ACE Web Server
After=network.target
[Service]
Type=simple
User=youruser
WorkingDirectory=/path/to/open-ace
ExecStart=/usr/bin/python3 server.py
Restart=always
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable open-ace
sudo systemctl start open-ace
macOS (launchd)
Create ~/Library/LaunchAgents/com.open-ace.web.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.open-ace.web</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/python3</string>
<string>/path/to/open-ace/server.py</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardInPath</key>
<string>/dev/null</string>
<key>StandardOutPath</key>
<string>/path/to/open-ace/server.log</string>
<key>StandardErrorPath</key>
<string>/path/to/open-ace/server-error.log</string>
</dict>
</plist>
Load the service:
launchctl load ~/Library/LaunchAgents/com.open-ace.web.plist
Multi-User Workspaces
With an OS-account isolation backend (workspace.isolation.backend plain, bwrap, local-gvisor or local-kata), Open ACE starts separate qwen-code-webui processes for each user with their system_account identity. The plain backend needs the container to run as root. See MULTI_USER_WORKSPACE.md for the startup methods, sudoers configuration, and version requirements.
To choose between the isolation modes (per-user OS account, confined, local gVisor/Kata container, OpenSandbox pod), configure one and verify what is in force, see the admin guide WORKSPACE_ISOLATION.md. The package installer sets up the sudoers rules and wrappers for you; the manual configuration in MULTI_USER_WORKSPACE.md is for installs without it.
Isolation and the Docker install. The Docker install supports the
noneand per-useros_userlevels, plussandboxedthrough OpenSandbox pods on Kubernetes. The confinedos_usermode and the local gVisor/Kata sandboxes need the package install on a Linux host. Choose your install method with WORKSPACE_ISOLATION in mind.
Upgrading
Upgrading to v2.1: workspace isolation moved to one
workspace.isolation {"level", "backend"}block (Issue #3446), and the server refuses to start on the old keys (multi_user_mode,required_isolation_level,os_user_confinement, ...). Package and Docker installs convert the config automatically; source installs and read-only mounted configs must runpython3 scripts/convert_workspace_isolation.py <path/to/config.json>first. Docker: useWORKSPACE_ISOLATION_BACKENDinstead ofWORKSPACE_MULTI_USER_MODE. See Workspace isolation. Runalembic upgrade head(v2.1 adds two indexes, built without blocking writes on PostgreSQL).
Upgrading from v1.x to v2.0: check these before restarting on v2.0.
- Python 3.10+ is required for source and package installs.
- Set
OPENACE_ENCRYPTION_KEYto your previousSECRET_KEYvalue before the first restart (see KEY_MANAGEMENT.md).- The Docker image runs as uid 1000; make mounted volumes writable by it, and use
docker-compose.multi-user.ymlfor multi-user workspace mode.- The database must already be on
baseline_2026_06_23or later; runalembic upgrade head.Full list: the
v2.0.0section of CHANGELOG.md.
Pull the new image and restart; database migrations run automatically on container startup, subject to the minimum upgrade baseline baseline_2026_06_23. Steps, migration failure handling, and rollback are covered in UPGRADING.md.
Uninstallation
# Stop and remove containers
docker compose down
# Remove images
docker rmi ghcr.io/open-ace/open-ace:latest postgres:15-alpine
# Remove data volumes (complete cleanup)
docker volume rm open-ace_agent-state open-ace_config-data open-ace_postgres-data open-ace_workspace-data
# Remove local configuration (optional)
rm -rf ~/.open-ace ./logs
Multi-user deployments also have the home-data volume (open-ace_home-data). Volume names carry the Compose project prefix (open-ace_ for a default clone); list yours with docker volume ls | grep open-ace.
Troubleshooting
Port Already in Use
# Find process using port 19888
lsof -i :19888
# Kill process
kill -9 <PID>
Or change the Open ACE port (see Port Configuration).
macOS AirPlay Legacy Note
Historically, macOS Monterey (12) and later enabled AirPlay Receiver by default, which listened on port 5000 and conflicted with services on that port. Open ACE listens on 19888, so this conflict no longer applies; the note is kept for history. If you do hit a port clash, disable AirPlay Receiver (System Settings -> General -> AirDrop & Handoff -> turn off "AirPlay Receiver") or change the Open ACE port.
Database Connection Failures
docker compose ps
docker compose logs postgres
docker compose logs -f open-ace
Ensure PostgreSQL is healthy (pg_isready healthcheck) and DB_PASSWORD in .env matches.
Permission Issues
# Fix permissions
chmod -R 755 ~/.open-ace/
code-server Verification
code-server powers the "Open VS Code" button in local workspace sessions:
# Verify in Docker container
docker run --rm <image> which code-server
docker run --rm <image> code-server --version
| Error Message | Cause | Solution |
|---|---|---|
code-server is not installed | Image version outdated or not built correctly | Rebuild Docker image |
code-server: command not found | PATH issue | Check if /usr/bin/code-server exists |
| Installation failed | Network issue | Check network connection, consider using proxy |
Related Documentation
- ENV_REFERENCE.md — authoritative environment variable table
- CONFIG_REFERENCE.md — config.json reference
- MULTI_USER_WORKSPACE.md — multi-user deployment
- UPGRADING.md — upgrade, migration failure handling, rollback
- KEY_MANAGEMENT.md — secrets and encryption key rotation
- NGINX.md — HTTPS reverse proxy
- KUBERNETES.md — Kubernetes deployment
- REMOTE_AGENT.md / REMOTE_WORKSPACE.md — remote integrations