Skip to main content

Deployment Guide

ACE = AI Computing Explorer

This guide covers deploying Open ACE in various scenarios.

Table of Contents

Quick Start

Local Deployment

# 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

Docker Deployment

Prerequisites

  • Docker and Docker Compose installed
  • Open ACE Docker image (open-ace:latest)
  • PostgreSQL image (postgres:15-alpine)

Non-root runtime (default)

The Open ACE image runs as the non-root open-ace user (uid 1000) by default. A USER 1000 directive in the production stage means docker run, docker-compose, and Kubernetes all execute the entrypoint as uid 1000 without relying solely on a manifest securityContext. The uid/gid 1000 is stable and matches the filesystem ownership baked into the image and the K8s runAsUser/runAsGroup: 1000.

Multi-user workspace mode (WORKSPACE_MULTI_USER_MODE=true or workspace.multi_user_mode: true in config) genuinely needs root — it creates system users (useradd), fixes ownership (chown), and switches identity (sudo -u <user>) across /home.


Multi-User Workspace Deployment

# Start multi-user mode with the one-click script
./scripts/start-multi-user.sh

The script automatically:

  • Detects the Docker Compose version (v1/v2)
  • Verifies that the configuration files exist
  • Starts the multi-user mode containers
  • Prints the access URL and status

Option 2: Docker Compose overlay

# Enable multi-user mode in one command
./scripts/bootstrap-compose-env.sh
docker compose -f docker-compose.yml -f docker-compose.multi-user.yml up -d --wait

docker-compose.multi-user.yml automatically configures:

  • Container runs as root (user: "0")
  • Explicit authorization (OPENACE_ALLOW_ROOT_MULTI_USER=1)
  • Configuration persistence (OPENACE_CONFIG_DIR=/home/open-ace/.open-ace)

Option 3: Manual configuration

docker run --user 0 -e WORKSPACE_MULTI_USER_MODE=true \
-e OPENACE_ALLOW_ROOT_MULTI_USER=1 \
-e OPENACE_CONFIG_DIR=/home/open-ace/.open-ace ...

You must set --user 0 (or manifest runAsUser: 0), OPENACE_ALLOW_ROOT_MULTI_USER=1, and OPENACE_CONFIG_DIR together. Otherwise, the entrypoint exits with a clear error rather than silently swallowing the useradd/chown permission failures that a naive non-root multi-user deployment would hit.


Multi-User Mode FAQ

Common errors and solutions

ErrorCauseSolution
multi-user workspace mode requires rootmulti-user mode enabled while running as non-rootuse ./scripts/start-multi-user.sh or the overlay file
OPENACE_ALLOW_ROOT_MULTI_USER=1 is not setrunning as root without explicit authorizationuse ./scripts/start-multi-user.sh or set the env var
Workspace failed to loadiframe load failure or timeoutcheck container logs, verify config, restart the container
docker-compose.multi-user.yml not foundoverlay file missingmake sure the repo was cloned correctly, or download the file from GitHub

Migrating from single-user to multi-user

If you already run a single-user deployment, migrate as follows:

  1. Stop the existing containers

    docker compose down

    Note: no data is lost — Docker volumes are preserved

  2. Check the data volumes

    docker volume ls | grep open-ace
    # expect: config-data, postgres-data, workspace-data
  3. Start in multi-user mode

    ./scripts/start-multi-user.sh

    or

    docker compose -f docker-compose.yml -f docker-compose.multi-user.yml up -d
  4. Verify data integrity

    • Log in and check that users and session data are intact
    • Check that configuration loads correctly
    • Test workspace functionality

Migration verification checklist

  • Database data preserved (users, sessions, configuration)
  • Docker volume data preserved (config-data, workspace-data)
  • Users can log in normally
  • Workspaces can be created and used normally
  • Existing AI sessions can be restored

Migrating from config.json

If you previously set "multi_user_mode": true in config.json:

  1. Recommended: Use docker-compose.multi-user.yml (see above)
  2. Or set "multi_user_mode": false in config.json and use environment variables

Note: Multi-user mode requires root and is suitable for controlled environments only. For production, ensure strong passwords and security keys are set.

Initial Deployment

Deployment is Docker Compose based:

# 1. Clone the repository on the server
git clone https://github.com/open-ace/open-ace.git
cd open-ace

# 2. Generate .env (SECRET_KEY, OPENACE_ENCRYPTION_KEY, UPLOAD_AUTH_KEY, ...)
./scripts/bootstrap-compose-env.sh

# 3. Start (pulls the pre-built openace/open-ace:latest image by default)
docker compose up -d --wait

# 4. Verify
docker compose ps
docker compose logs -f open-ace

For offline servers, pull the image on a connected machine with docker pull openace/open-ace:latest, transfer it via docker save openace/open-ace:latest | gzip > open-ace-images.tar.gz, load it with gunzip -c open-ace-images.tar.gz | docker load, then start the stack.

Deployment Configuration

Most settings are controlled through .env and docker-compose.yml in the repository root:

SettingEnvironment variableDefault
Web portPORT19888
ImageIMAGE_NAMEopenace/open-ace:latest
Database userDB_USERace
Database nameDB_NAMEace
Database passwordDB_PASSWORDdev-password-change-in-production (must change in production)

Note: Workspace runs in a separate container. When enabled, Open ACE will connect to the Workspace service at the specified URL. Make sure the Workspace container is running and the port is accessible.

Default Credentials

After deployment, use these credentials to login:

Username: admin
Password: admin123

Important: Change the default password immediately after first login!

Before starting the production stack, define these secrets in .env or your secret manager:

  • SECRET_KEY — Flask session secret, must be strong and unique
  • OPENACE_ENCRYPTION_KEY — dedicated encryption key for stored API keys / SMTP passwords
  • UPLOAD_AUTH_KEY — shared secret for upload endpoints

Directory Structure

open-ace/ # cloned repository
├── docker-compose.yml # Docker Compose configuration
├── .env # Environment variables (sensitive!)
└── config/ # Configuration files (mounted into the container)
└── config.json # Main configuration

Note: Data is stored in the PostgreSQL container's volume (postgres-data), not in the host filesystem.

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
docker compose down

# Start services
docker compose up -d

Updating Open ACE Image

When a new version of Open ACE is released, you only need to update the Docker image:

cd /path/to/open-ace

# 1. Pull the new image
docker compose pull

# 2. Restart open-ace container
docker compose up -d open-ace

# 3. Verify startup
docker compose logs -f open-ace

Method 2: Complete Rebuild

cd /path/to/open-ace

# 1. Pull the new image
docker compose pull

# 2. Stop and remove old container
docker compose stop open-ace
docker compose rm -f open-ace

# 3. Start new container
docker compose up -d open-ace

# 4. Verify startup
docker compose logs -f open-ace

Method 3: Using Version Tags

# 1. Pin a specific version (in .env)
echo "IMAGE_NAME=openace/open-ace:v1.2.0" >> .env

# 2. Pull and recreate the container
docker compose pull
docker compose up -d open-ace

Note:

  • Data is stored in ./data directory and PostgreSQL volume - it will NOT be lost
  • Configuration in ./config is preserved
  • PostgreSQL container continues running - only open-ace container is updated

Database Migrations

If the new version includes database schema changes:

cd /path/to/open-ace

# Run migrations
docker compose run --rm open-ace alembic upgrade head

# Restart application
docker compose restart open-ace

Uninstallation

# Stop and remove containers
docker compose down

# Remove images
docker rmi openace/open-ace:latest postgres:15-alpine

# Remove data volumes (complete cleanup)
docker volume rm open-ace_postgres-data open-ace_config-data open-ace_workspace-data

# Remove local configuration (optional)
rm -rf ~/.open-ace ./logs

Configuration

Configuration File

Configuration is stored in ~/.open-ace/config.json:

{
"host_name": "my-machine",
"tools": {
"claude": {
"enabled": true,
"log_path": "~/.claude/projects"
},
"qwen": {
"enabled": true,
"log_path": "~/.qwen/projects"
},
"openclaw": {
"enabled": true,
"log_path": "~/.openclaw/agents"
}
},
"email": {
"smtp_host": "smtp.example.com",
"smtp_port": 587,
"sender": "noreply@example.com"
}
}

Environment Variables

VariableDescription
OPENCLAW_TOKENOpenClaw API token
SMTP_PASSWORDEmail SMTP password
OPENACE_CORS_ALLOWED_ORIGINSComma-separated explicit API CORS allowlist for non-loopback WebUI origins
OPENACE_WS_MAX_MESSAGE_BYTESMaximum inbound browser WebSocket message size for terminal / VSCode raw bridges (default: 8388608)

Outbound URL Security

Administrator-configured SSO/OIDC endpoint URLs are validated before Open ACE sends test, token, userinfo, or JWKS requests. By default, only public http and https destinations are allowed. Loopback, localhost, private networks, link-local ranges, metadata service hosts, URL credentials, and non-public DNS results are blocked to reduce SSRF risk.

Port Configuration

Open ACE listens on port 19888 by default (Issue #1372: AI + ace mnemonic port). To change the port, use the appropriate method based on your deployment type.

macOS Port Conflict (Legacy Note)

Historically, macOS Monterey (12) and later versions enabled AirPlay Receiver by default, which listened on port 5000 and conflicted with Open ACE. Since we now use port 19888, this conflict is no longer an issue.

Solutions:

  1. Disable AirPlay Receiver: System Settings → General → AirDrop & Handoff → Turn off "AirPlay Receiver"
  2. Or change Open ACE port (see methods below)

Binary Installation

Modify the configuration file ~/.open-ace/config.json:

{
"server": {
"web_port": 5001,
"web_host": "0.0.0.0"
}
}

Restart the service after modification.

Docker Installation

Temporary change (command line):

PORT=5001 docker compose up -d

Permanent change (.env file):

# Create/edit .env file in project root
echo "PORT=5001" >> .env

# Restart container
docker compose down
docker compose up -d

Verify port mapping:

docker ps
# Should show 0.0.0.0:19888->19888/tcp

Firewall Settings (for external access)

# Ubuntu/Debian
sudo ufw allow 19888/tcp

# CentOS/RHEL
sudo firewall-cmd --add-port=19888/tcp --permanent
sudo firewall-cmd --reload

Summary

MethodConfiguration LocationHow to Change
Binary~/.open-ace/config.jsonModify server.web_port
DockerEnvironment variable PORT.env file or command line

Deployment Scenarios

All components run on one machine:

# Start web server
python3 server.py

# Set up cron for data collection
crontab -e

Add to crontab:

# Collect data daily at 00:30
30 0 * * * cd /path/to/open-ace && python3 scripts/fetch_claude.py && python3 scripts/fetch_qwen.py >> logs/cron.log 2>&1

2. Central Server + Remote Collectors

For distributed environments:

Central Server

# Deploy
python3 scripts/manage.py local deploy

# Start web service
python3 scripts/manage.py local start

Remote Machine

# Deploy to remote
python3 scripts/manage.py remote deploy

# Or manually configure
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 service file /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

Data Collection

Manual Collection

# Collect from all tools
python3 scripts/fetch_claude.py
python3 scripts/fetch_qwen.py
python3 scripts/fetch_openclaw.py

# Collect for specific days
python3 scripts/fetch_claude.py --days 7

Scheduled Collection

Using cron:

# Edit crontab
crontab -e

# Add scheduled tasks
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_openclaw.py >> logs/cron.log 2>&1

Management Commands

# Using manage.py
python3 scripts/manage.py local start # Start local service
python3 scripts/manage.py local stop # Stop local service
python3 scripts/manage.py local status # Check status
python3 scripts/manage.py remote deploy # Deploy to remote
python3 scripts/manage.py remote sync # Sync files to remote

Upgrading

# Backup data
cp ~/.open-ace/usage.db ~/.open-ace/usage.db.backup

# Pull latest code
git pull

# Run database migrations if needed
alembic upgrade head

# Restart service
python3 scripts/manage.py local stop
python3 scripts/manage.py local start

Troubleshooting

Port Already in Use

If startup fails due to port conflict, you can:

  1. Change Open ACE port - See Port Configuration
  2. Kill the conflicting process:
# Find process using port 19888
lsof -i :19888

# Kill process
kill -9 <PID>

Database Locked

# Check for running processes
ps aux | grep python

# Stop all services before maintenance

Permission Issues

# Fix permissions
chmod -R 755 ~/.open-ace/

code-server Installation Verification

Purpose: code-server is used for the "Open VS Code" button in local workspace sessions. When users click the "Open VS Code" button in a local workspace, the system launches code-server to provide a web-based VS Code editor.

Verify Installation:

# Verify in Docker container
docker run --rm <image> which code-server
docker run --rm <image> code-server --version

Common Issues:

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

Fix History:

Issue/PRDateFix Content
#2245 / #22502026-08-05First added code-server installation
#23582026-08-06Fixed verification path (/usr/local/bin → /usr/bin)
#24982026-08-11Removed invalid --prefix parameter

Installation Method:

  • Uses official install script: https://code-server.dev/install.sh
  • Debian environment uses deb package, installs to /usr/bin/code-server
  • Timeout settings: 15s connection, 300s execution

Security Considerations

  1. Security Mode: Must explicitly set OPENACE_SECURITY_MODE (Issue #2185)

    • production: Enforce security checks. Secrets must be explicitly set. Weak passwords forbidden.
    • pilot: Allow auto-generated secrets with strong warnings. Suitable for trial environments.
    • development: Allow auto-generated secrets with general warnings. Suitable for development.
    • Important: The system no longer silently falls back to a default mode. Explicit configuration required.
  2. Authentication: Enable user authentication in production

  3. HTTPS: Use reverse proxy (nginx/Apache) with SSL

  4. Firewall: Restrict access to port 19888

  5. Secrets: Use environment variables or a secret manager for sensitive data

  6. Dedicated encryption key: Set OPENACE_ENCRYPTION_KEY explicitly; encrypted secret storage no longer derives from SECRET_KEY

  7. No placeholder secrets: Do not use the following placeholder values in production:

    • change-me-in-production
    • replace-with-random-* (k8s manifest placeholders)
    • Development placeholders like dev-secret-key, dev-smtp-password-key, default-secret-key

    Using these placeholders will cause the application to refuse startup in production (SECRET_KEY, OPENACE_ENCRYPTION_KEY) or disable features (UPLOAD_AUTH_KEY).

  8. SSO Callback Whitelist (Issue #3224): Production environments must configure SSO_ALLOWED_REDIRECT_DOMAINS

    • Environment variable: SSO_ALLOWED_REDIRECT_DOMAINS
    • Format: Comma-separated domain list, e.g., example.com,www.example.com
    • Default behavior: Only localhost allowed when not configured (for development)
    • ⚠️ Production must configure: SSO login will fail when accessed from non-localhost without whitelist
    • Security risk: When whitelist is not configured, SSO login succeeds but redirect is blocked; session token is NOT exposed in JSON response (fixed)

Upgrade Note: Encrypted Secrets

Recent security hardening separates Flask session signing from stored-secret encryption. Before upgrading an existing deployment that already has encrypted SSO client secrets, SMTP passwords, or API keys, set OPENACE_ENCRYPTION_KEY to the same value previously used for SECRET_KEY. After the service starts and can read existing secrets, rotate OPENACE_ENCRYPTION_KEY during a planned maintenance window if you need a dedicated new key.

Docker Compose now requires SECRET_KEY, OPENACE_ENCRYPTION_KEY, and UPLOAD_AUTH_KEY to be set explicitly. Update your .env or secret manager before restarting the stack.

Multi-User Workspace Deployment

When enabling workspace.multi_user_mode, Open ACE starts separate qwen-code-webui processes for each user with their system_account identity. This requires additional deployment configuration.

Prerequisites

  1. qwen-code-webui installed on the server
  2. sudo configured for user switching
  3. User accounts exist for each system_account

sudo Configuration (Required)

Create sudoers file to allow Open ACE service account to run webui as other users:

# Create sudoers file
sudo visudo -f /etc/sudoers.d/open-ace-webui

Add the following content:

# Allow open-ace service account to run qwen-code-webui as any user
# Replace 'open-ace' with your actual service account name
# Note: Python layer validates target user is in database mapping at WebUI startup

open-ace ALL=(ALL) NOPASSWD: /usr/local/bin/qwen-code-webui *
open-ace ALL=(ALL) NOPASSWD: /usr/bin/qwen-code-webui *
open-ace ALL=(ALL) NOPASSWD: /opt/qwen-code-webui/bin/qwen-code-webui *

# 【Issue #2181 Security Hardening】Low-risk utility commands
# Removed cat/chown/useradd/rm wildcards, replaced with secure wrappers
open-ace ALL=(root) NOPASSWD: /usr/bin/test *, /usr/bin/ls *, /usr/bin/stat *, /usr/bin/mkdir *, /usr/bin/id *, /usr/bin/find *

# 【Issue #2181】Secure wrapper rules
# These wrappers validate paths, users, and permissions internally
open-ace ALL=(root) NOPASSWD: /usr/local/bin/openace-chown *
open-ace ALL=(root) NOPASSWD: /usr/local/bin/openace-useradd *
open-ace ALL=(root) NOPASSWD: /usr/local/bin/openace-cat *
open-ace ALL=(root) NOPASSWD: /usr/local/bin/openace-mkdir *
open-ace ALL=(root) NOPASSWD: /usr/local/bin/openace-rm *

# 【Issue #2181】Cross-user Agent launch wrapper
# All AI CLI must be launched through this wrapper
open-ace ALL=(root) NOPASSWD: /usr/local/bin/openace-run-as --isolated *

# 【Issue #2181】Environment variable preservation (non-sensitive only)
# Agent processes use env -i via openace-run-as --isolated, not inheriting env_keep
# env_keep is mainly for WebUI startup
Defaults env_keep += "OPENACE_PROXY_TOKEN OPENACE_PROXY_URL OPENACE_MODEL OPENACE_LOG_DIR PATH"
Defaults env_keep += "GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL"
Defaults env_keep += "SESSION_TIMEOUT_MS KEEPALIVE_INTERVAL_MS"

Security notes:

  • Use full paths to prevent path manipulation attacks
  • The NOPASSWD flag is required for non-interactive service operation
  • Limit to specific executable paths, not generic sudo access
  • Issue #2181 Hardening: cat/chown/useradd/rm wildcards removed, replaced with secure wrappers
  • Issue #2181 Hardening: env_keep no longer contains sensitive variables (API Keys, GH_TOKEN, etc.)
  • Agent processes launched via openace-run-as --isolated use env -i for complete environment isolation

qwen-code-webui Installation

Install qwen-code-webui in one of these locations:

# Method 1: npm global install (recommended)
npm install -g @ivycomputing/qwen-code-webui

# Verify installation
which qwen-code-webui
# Should output: /usr/local/bin/qwen-code-webui

# Method 2: Manual install
git clone https://github.com/ivycomputing/qwen-code-webui.git
cd qwen-code-webui
npm install && npm run build
ln -s $(pwd)/bin/qwen-code-webui /usr/local/bin/qwen-code-webui

User Account Requirements

Each user with a system_account must have:

  1. Linux account exists:

    # Check if user exists
    id <system_account>

    # Create if needed
    sudo useradd -m <system_account>
  2. qwen directory accessible:

    # Ensure user has .qwen directory
    sudo mkdir -p /home/<system_account>/.qwen/projects
    sudo chown -R <system_account>:<system_account> /home/<system_account>/.qwen
  3. Project directories accessible (if applicable)

Port Range Configuration

Choose a port range that doesn't conflict with other services:

{
"workspace": {
"port_range_start": 3100,
"port_range_end": 3200
}
}

Recommendations:

  • Use ports above 3000 (avoid common service ports)
  • Allocate enough ports for expected concurrent users (e.g., 100 ports for up to 100 users)
  • Verify ports are not used: sudo netstat -tlnp | grep 3100-3200

systemd Service Configuration

When running Open ACE as a systemd service, ensure proper permissions:

[Unit]
Description=Open ACE Web Server
After=network.target

[Service]
Type=simple
User=open-ace
Group=open-ace
WorkingDirectory=/home/open-ace/open-ace
ExecStart=/usr/bin/python3 server.py
Restart=always

# Required for multi-user mode
# Allow sudo execution
AmbientCapabilities=CAP_SETUID CAP_SETGID

[Install]
WantedBy=multi-user.target

Troubleshooting Multi-User Mode

IssueCauseSolution
"sudo: no tty present"sudo requires passwordAdd NOPASSWD to sudoers
"qwen-code-webui not found"Executable not installedInstall webui in PATH
"Permission denied"User lacks permissionsCheck sudoers configuration
Port allocation failedAll ports in useIncrease port range or reduce max_instances
Process won't startUser account missingCreate system_account user

Checking Multi-User Status

# View running instances
curl http://localhost:19888/api/workspace/instances

# Check logs
tail -f /home/open-ace/open-ace/logs/open-ace.log | grep WebUIManager

Windows Compatibility

Windows does NOT support multi-user mode. On Windows systems, the configuration is automatically downgraded to single-user mode (direct execution without user switching). This is a platform limitation due to Windows not having equivalent sudo -u functionality.