Deployment Guide
ACE = AI Computing Explorer
This guide covers deploying Open ACE in various scenarios.
Table of Contents
- Quick Start
- Docker Deployment
- Configuration
- Deployment Scenarios
- System Services
- Data Collection
- Upgrading
- Troubleshooting
- Security Considerations
- Multi-User Workspace Deployment
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
Option 1: One-click startup script (recommended)
# 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
| Error | Cause | Solution |
|---|---|---|
multi-user workspace mode requires root | multi-user mode enabled while running as non-root | use ./scripts/start-multi-user.sh or the overlay file |
OPENACE_ALLOW_ROOT_MULTI_USER=1 is not set | running as root without explicit authorization | use ./scripts/start-multi-user.sh or set the env var |
Workspace failed to load | iframe load failure or timeout | check container logs, verify config, restart the container |
docker-compose.multi-user.yml not found | overlay file missing | make 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:
-
Stop the existing containers
docker compose downNote: no data is lost — Docker volumes are preserved
-
Check the data volumes
docker volume ls | grep open-ace# expect: config-data, postgres-data, workspace-data -
Start in multi-user mode
./scripts/start-multi-user.shor
docker compose -f docker-compose.yml -f docker-compose.multi-user.yml up -d -
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:
- Recommended: Use docker-compose.multi-user.yml (see above)
- Or set
"multi_user_mode": falsein 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:
| Setting | Environment variable | Default |
|---|---|---|
| Web port | PORT | 19888 |
| Image | IMAGE_NAME | openace/open-ace:latest |
| Database user | DB_USER | ace |
| Database name | DB_NAME | ace |
| Database password | DB_PASSWORD | dev-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 uniqueOPENACE_ENCRYPTION_KEY— dedicated encryption key for stored API keys / SMTP passwordsUPLOAD_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:
Method 1: Simple Restart (Recommended)
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
./datadirectory and PostgreSQL volume - it will NOT be lost - Configuration in
./configis 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
| Variable | Description |
|---|---|
OPENCLAW_TOKEN | OpenClaw API token |
SMTP_PASSWORD | Email SMTP password |
OPENACE_CORS_ALLOWED_ORIGINS | Comma-separated explicit API CORS allowlist for non-loopback WebUI origins |
OPENACE_WS_MAX_MESSAGE_BYTES | Maximum 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:
- Disable AirPlay Receiver: System Settings → General → AirDrop & Handoff → Turn off "AirPlay Receiver"
- 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
| Method | Configuration Location | How to Change |
|---|---|---|
| Binary | ~/.open-ace/config.json | Modify server.web_port |
| Docker | Environment variable PORT | .env file or command line |
Deployment Scenarios
1. Single Machine (Recommended for Personal Use)
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:
- Change Open ACE port - See Port Configuration
- 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 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 |
Fix History:
| Issue/PR | Date | Fix Content |
|---|---|---|
| #2245 / #2250 | 2026-08-05 | First added code-server installation |
| #2358 | 2026-08-06 | Fixed verification path (/usr/local/bin → /usr/bin) |
| #2498 | 2026-08-11 | Removed 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
-
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.
-
Authentication: Enable user authentication in production
-
HTTPS: Use reverse proxy (nginx/Apache) with SSL
-
Firewall: Restrict access to port 19888
-
Secrets: Use environment variables or a secret manager for sensitive data
-
Dedicated encryption key: Set
OPENACE_ENCRYPTION_KEYexplicitly; encrypted secret storage no longer derives fromSECRET_KEY -
No placeholder secrets: Do not use the following placeholder values in production:
change-me-in-productionreplace-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). -
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
localhostallowed 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)
- Environment variable:
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
- qwen-code-webui installed on the server
- sudo configured for user switching
- 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
NOPASSWDflag is required for non-interactive service operation - Limit to specific executable paths, not generic
sudoaccess - 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 --isolateduseenv -ifor 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:
-
Linux account exists:
# Check if user existsid <system_account># Create if neededsudo useradd -m <system_account> -
qwen directory accessible:
# Ensure user has .qwen directorysudo mkdir -p /home/<system_account>/.qwen/projectssudo chown -R <system_account>:<system_account> /home/<system_account>/.qwen -
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
| Issue | Cause | Solution |
|---|---|---|
| "sudo: no tty present" | sudo requires password | Add NOPASSWD to sudoers |
| "qwen-code-webui not found" | Executable not installed | Install webui in PATH |
| "Permission denied" | User lacks permissions | Check sudoers configuration |
| Port allocation failed | All ports in use | Increase port range or reduce max_instances |
| Process won't start | User account missing | Create 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.