Skip to main content

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​

Before any production deployment, set the security mode explicitly (Issue #2331):

OPENACE_SECURITY_MODE=production
  • production — strict checks. SECRET_KEY and OPENACE_ENCRYPTION_KEY (>= 32 characters each) and a strong DB_PASSWORD must 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.yml defaults to development so 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 first if 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 with IMAGE_NAME, e.g. to pin ghcr.io/open-ace/open-ace:v2.1.0 or a locally built open-ace:dev). Developers can build locally with docker build -t open-ace:dev --target production ..
  • docker compose down does not delete data; all state lives in named volumes (see Data Persistence).
  • The container runs as the non-root open-ace user (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:

  1. Environment variables (.env for 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.
  2. config.json controls application behavior (tools to collect, email, workspace). On bare metal it lives at ~/.open-ace/config.json. In Docker it is auto-generated inside the config-data volume at /home/open-ace/.open-ace/config.json on 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:

VariableDefaultPurpose
PORT19888Host port for the web UI (container always listens on 19888)
SERVER_IPauto-detectedAddress 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_END3100 / 3200Published port range for per-user workspace WebUI instances
WORKSPACE_ISOLATION_BACKENDsharedIsolation 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_NAMEace / acePostgreSQL user and database name
DB_PASSWORDdev defaultStrong password required in production
IMAGE_NAMEghcr.io/open-ace/open-ace:latestApplication image
BASE_REGISTRYdocker.ioBase image registry override

Port Configuration​

  • Bare metal: set server.web_port / server.web_host in ~/.open-ace/config.json, then restart.
  • Docker: temporary change with PORT=5001 docker compose up -d, or permanent via echo "PORT=5001" >> .env followed by docker 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:

VolumeMount pointContent
config-data/home/open-ace/.open-aceconfig.json and generated-secrets.env
postgres-data/var/lib/postgresql/dataPostgreSQL database (users, sessions, all application data)
workspace-data/workspacePer-user project directories
agent-state/var/lib/openaceCarried 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/:

ScriptSource
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​

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 none and per-user os_user levels, plus sandboxed through OpenSandbox pods on Kubernetes. The confined os_user mode 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 run python3 scripts/convert_workspace_isolation.py <path/to/config.json> first. Docker: use WORKSPACE_ISOLATION_BACKEND instead of WORKSPACE_MULTI_USER_MODE. See Workspace isolation. Run alembic 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.

  1. Python 3.10+ is required for source and package installs.
  2. Set OPENACE_ENCRYPTION_KEY to your previous SECRET_KEY value before the first restart (see KEY_MANAGEMENT.md).
  3. The Docker image runs as uid 1000; make mounted volumes writable by it, and use docker-compose.multi-user.yml for multi-user workspace mode.
  4. The database must already be on baseline_2026_06_23 or later; run alembic upgrade head.

Full list: the v2.0.0 section 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 MessageCauseSolution
code-server is not installedImage version outdated or not built correctlyRebuild Docker image
code-server: command not foundPATH issueCheck if /usr/bin/code-server exists
Installation failedNetwork issueCheck network connection, consider using proxy