- Replace all dynamous-community/remote-coding-agent references with coleam00/Archon - Replace all ghcr.io/dynamous-community/remote-coding-agent with ghcr.io/coleam00/archon - Change license from proprietary Dynamous to MIT - Fix cd directory name in docs (remote-coding-agent → Archon) - Remove hardcoded local paths from skills and docs - Add Windows x64 binary to release pipeline (cross-compiled from Linux) - Add --minify --bytecode flags to binary compilation - Create PowerShell install script (scripts/install.ps1) - Fix isBinaryBuild() detection for Bun 1.3.5+ (use import.meta.dir virtual FS check) - Scaffold Astro Starlight docs site at website/ (Astro 6 + Starlight 0.38) - Add deploy-docs.yml workflow for GitHub Pages - Update test.yml branch triggers (develop → dev) - Add install section with curl/PowerShell/Homebrew/Docker to README - Add badges and archon.diy docs link to README - Create SECURITY.md with vulnerability disclosure policy - Update CONTRIBUTING.md for public audience - Add website/ and eslint ignores for Astro-generated files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
16 KiB
Docker Guide
Deploy Archon on a server with Docker. Includes automatic HTTPS, PostgreSQL, and the Web UI.
Cloud-Init (Fastest Setup)
The fastest way to deploy. Paste the cloud-init config into your VPS provider's User Data field when creating a server — it installs everything automatically.
File: deploy/cloud-init.yml
How to use
- Create a VPS (Ubuntu 22.04+ recommended) at DigitalOcean, AWS, Linode, Hetzner, etc.
- Paste the contents of
deploy/cloud-init.ymlinto the "User Data" / "Cloud-Init" field - Add your SSH key via the provider's UI
- Create the server and wait ~5-8 minutes for setup to complete
What it installs
- Docker + Docker Compose
- UFW firewall (ports 22, 80, 443)
- Clones the repo to
/opt/archon - Copies
.env.example→.envandCaddyfile.example→Caddyfile - Pre-pulls PostgreSQL and Caddy images
- Builds the Archon Docker image
After boot
SSH into the server and finish configuration:
# Check setup completed
cat /opt/archon/SETUP_COMPLETE
# Edit credentials and domain
nano /opt/archon/.env
# Set at minimum:
# CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-...
# DOMAIN=archon.example.com
# DATABASE_URL=postgresql://postgres:postgres@postgres:5432/remote_coding_agent
# (Optional) Set up basic auth to protect Web UI:
# docker run caddy caddy hash-password --plaintext 'YOUR_PASSWORD'
# Add to .env: CADDY_BASIC_AUTH=basicauth @protected { admin $$2a$$14$$<hash> }
# Start
cd /opt/archon
docker compose --profile with-db --profile cloud up -d
Don't forget DNS: Before starting, point your domain's A record to the server's IP.
Provider-specific notes
| Provider | Where to paste cloud-init |
|---|---|
| DigitalOcean | Create Droplet → Advanced Options → User Data |
| AWS EC2 | Launch Instance → Advanced Details → User Data |
| Linode | Create Linode → Add Tags → Metadata (User Data) |
| Hetzner | Create Server → Cloud config → User Data |
| Vultr | Deploy → Additional Features → Cloud-Init User-Data |
Manual Server Setup
Step-by-step alternative if you prefer not to use cloud-init, or need more control.
1. Install Docker
# On Ubuntu/Debian
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Log out and back in for group change to take effect
exit
# ssh back in
# Verify
docker --version
docker compose version
2. Clone the repo
git clone https://github.com/coleam00/Archon.git
cd Archon
3. Configure environment
cp .env.example .env
cp Caddyfile.example Caddyfile
nano .env
Set these values in .env:
# AI Assistant — at least one is required
# Option A: Claude OAuth token (run `claude setup-token` on your local machine to get one)
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-xxxxx
# Option B: Claude API key (from console.anthropic.com/settings/keys)
# CLAUDE_API_KEY=sk-ant-xxxxx
# Domain — your domain or subdomain pointing to this server
DOMAIN=archon.example.com
# Database — connect to the Docker PostgreSQL container
# Without this, the app uses SQLite (fine for getting started, but PostgreSQL recommended)
DATABASE_URL=postgresql://postgres:postgres@postgres:5432/remote_coding_agent
# Basic Auth (optional) — protects Web UI when exposed to the internet
# Skip if using IP-based firewall rules instead.
# Generate hash: docker run caddy caddy hash-password --plaintext 'YOUR_PASSWORD'
# CADDY_BASIC_AUTH=basicauth @protected { admin $$2a$$14$$... }
# Platform tokens (set the ones you use)
# TELEGRAM_BOT_TOKEN=123456789:ABCdef...
# SLACK_BOT_TOKEN=xoxb-...
# SLACK_APP_TOKEN=xapp-...
# GH_TOKEN=ghp_...
# GITHUB_TOKEN=ghp_...
Docker does not support
CLAUDE_USE_GLOBAL_AUTH=true— there is no localclaudeCLI inside the container. You must provide eitherCLAUDE_CODE_OAUTH_TOKENorCLAUDE_API_KEYexplicitly.If you use
--profile with-dbwithout settingDATABASE_URL, the app will fall back to SQLite and log a warning. The PostgreSQL container runs but is unused.
4. Point your domain to the server
Create a DNS A record at your domain registrar:
| Type | Name | Value |
|---|---|---|
| A | archon (or @ for root domain) |
Your server's public IP |
Wait for DNS propagation (usually 5-60 minutes). Verify with dig archon.example.com.
5. Open firewall ports
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443
sudo ufw --force enable
6. Start
docker compose --profile with-db --profile cloud up -d
This starts three containers:
- app — Archon server + Web UI
- postgres — PostgreSQL 17 database (auto-initialized)
- caddy — Reverse proxy with automatic HTTPS (Let's Encrypt)
7. Verify
# Check all containers are running
docker compose --profile with-db --profile cloud ps
# Watch logs
docker compose logs -f app
docker compose logs -f caddy
# Test HTTPS (from your local machine)
curl https://archon.example.com/api/health
Open https://archon.example.com in your browser — you should see the Archon Web UI.
Profiles
Archon uses Docker Compose profiles to optionally add PostgreSQL and/or HTTPS. Mix and match:
| Command | What runs |
|---|---|
docker compose up -d |
App with SQLite |
docker compose --profile with-db up -d |
App + PostgreSQL |
docker compose --profile cloud up -d |
App + Caddy (HTTPS) |
docker compose --profile with-db --profile cloud up -d |
App + PostgreSQL + Caddy |
No profile (SQLite)
Zero-config default. No database container needed — SQLite file is stored in the archon_data volume.
--profile with-db (PostgreSQL)
Starts a PostgreSQL 17 container. Set the connection URL in .env:
DATABASE_URL=postgresql://postgres:postgres@postgres:5432/remote_coding_agent
The schema is auto-initialized on first startup. PostgreSQL is exposed on ${POSTGRES_PORT:-5432} for external tools.
--profile cloud (Caddy HTTPS)
Adds a Caddy reverse proxy with automatic TLS certificates from Let's Encrypt.
Requires before starting:
Caddyfilecreated:cp Caddyfile.example CaddyfileDOMAINset in.env- DNS A record pointing to your server's IP
- Ports 80 and 443 open
Caddy handles HTTPS certificates, HTTP→HTTPS redirect, HTTP/3, and SSE streaming.
Authentication (Optional Basic Auth)
Caddy can enforce HTTP Basic Auth on all routes except webhooks (/webhooks/*) and the health check (/api/health). This is optional — skip it if you use IP-based firewall rules or other network-level access control.
To enable:
-
Generate a bcrypt password hash:
docker run caddy caddy hash-password --plaintext 'YOUR_PASSWORD' -
Set
CADDY_BASIC_AUTHin.env(use$$to escape$in bcrypt hashes):CADDY_BASIC_AUTH=basicauth @protected { admin $$2a$$14$$abc123... } -
Restart:
docker compose --profile cloud restart caddy
Your browser will prompt for username/password when accessing the Archon URL. Webhook endpoints bypass auth since they use HMAC signature verification.
To disable, leave CADDY_BASIC_AUTH empty or unset — the Caddyfile expands it to nothing.
Important: Always use the
docker run caddy caddy hash-passwordcommand to generate hashes — never put plaintext passwords in.env.
Form-Based Authentication (HTML Login Page)
An alternative to basic auth that serves a styled HTML login form instead of the browser's credential popup. Uses a lightweight auth-service sidecar and Caddy's forward_auth directive.
When to use form auth vs basic auth:
- Form auth: Styled dark-mode login page, 24h session cookie, logout support. Requires an extra container.
- Basic auth: Zero extra containers, simpler setup. Browser shows a native credential dialog.
Setup:
-
Generate a bcrypt password hash:
docker compose --profile auth run --rm auth-service \ node -e "require('bcryptjs').hash('YOUR_PASSWORD', 12).then(h => console.log(h))"First run builds the auth-service image. Save the output hash (starts with
$2b$12$...). -
Generate a random cookie signing secret:
docker run --rm node:22-alpine \ node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" -
Set the following in
.env:AUTH_USERNAME=admin AUTH_PASSWORD_HASH=$2b$12$REPLACE_WITH_YOUR_HASH COOKIE_SECRET=REPLACE_WITH_64_HEX_CHARS -
Update
Caddyfile(copy fromCaddyfile.exampleif not done yet):- Uncomment the "Option A" form auth block (the
handle /login,handle /logout, andhandle { forward_auth ... }blocks) - Comment out the "No auth" default
handleblock (the lasthandle { ... }block near the bottom of the site block)
- Uncomment the "Option A" form auth block (the
-
Start with both
cloudandauthprofiles:docker compose --profile with-db --profile cloud --profile auth up -d -
Visit your domain — you should be redirected to
/login.
Logout: Navigate to /logout to clear the session cookie and return to the login form.
Session duration: Defaults to 24 hours (COOKIE_MAX_AGE=86400). Override in .env:
COOKIE_MAX_AGE=3600 # 1 hour
Note: Do not use form auth and basic auth simultaneously. Choose one method and leave the other disabled (either empty
CADDY_BASIC_AUTHor remove the basic auth@protectedblock from your Caddyfile).
Configuration
AI Credentials (required)
Docker containers cannot use CLAUDE_USE_GLOBAL_AUTH=true — there is no local claude CLI inside the container. You must set credentials explicitly in .env:
Claude (choose one):
# OAuth token — run `claude setup-token` on your local machine, copy the token
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-xxxxx
# Or API key — from console.anthropic.com/settings/keys
CLAUDE_API_KEY=sk-ant-xxxxx
Codex (alternative):
CODEX_ID_TOKEN=eyJhbGc...
CODEX_ACCESS_TOKEN=eyJhbGc...
CODEX_REFRESH_TOKEN=rt_...
CODEX_ACCOUNT_ID=6a6a7ba6-...
Platform Tokens (optional)
TELEGRAM_BOT_TOKEN=123456789:ABCdef...
SLACK_BOT_TOKEN=xoxb-...
SLACK_APP_TOKEN=xapp-...
DISCORD_BOT_TOKEN=...
GH_TOKEN=ghp_...
GITHUB_TOKEN=ghp_...
WEBHOOK_SECRET=...
Server Settings (optional)
PORT=3000 # Default: 3000
DOMAIN=archon.example.com # Required for --profile cloud
LOG_LEVEL=info # fatal|error|warn|info|debug|trace
MAX_CONCURRENT_CONVERSATIONS=10
See .env.example for the full list with documentation.
Data Directory
The container stores all data at /.archon/ (workspaces, worktrees, artifacts, logs, SQLite DB).
By default this is a Docker-managed volume. To store data at a specific location on the host, set ARCHON_DATA in .env:
# Store Archon data at a specific host path
ARCHON_DATA=/opt/archon-data
The directory is created automatically. Make sure the path is writable by UID 1001 (the container user):
mkdir -p /opt/archon-data
sudo chown -R 1001:1001 /opt/archon-data
If ARCHON_DATA is not set, Docker manages the volume automatically (archon_data) — data persists across restarts and rebuilds but lives inside Docker's storage.
GitHub CLI Authentication
GH_TOKEN from .env is picked up automatically. Alternatively:
docker compose exec app gh auth login
GitHub Webhooks
After the server is reachable via HTTPS:
- Go to
https://github.com/<owner>/<repo>/settings/hooks - Add webhook:
- Payload URL:
https://archon.example.com/webhooks/github - Content type:
application/json - Secret: Your
WEBHOOK_SECRETfrom.env - Events: Issues, Issue comments, Pull requests
- Payload URL:
Pre-built Image
For users who don't need to build from source:
mkdir archon && cd archon
curl -O https://raw.githubusercontent.com/coleam00/Archon/main/deploy/docker-compose.yml
curl -O https://raw.githubusercontent.com/coleam00/Archon/main/.env.example
cp .env.example .env
# Edit .env — set AI credentials, DOMAIN, etc.
docker compose up -d
Uses ghcr.io/coleam00/archon:latest. To add PostgreSQL, uncomment the postgres service in the compose file and set DATABASE_URL in .env.
Building the Image
The Dockerfile uses three stages:
- deps — Installs all dependencies (including devDependencies for the web build)
- web-build — Builds the React web UI with Vite
- production — Production image with only production dependencies + pre-built web assets
docker build -t archon .
docker run --env-file .env -p 3000:3000 archon
What's in the image:
- Runtime: Bun 1.2 (runs TypeScript directly, no compile step)
- System deps: git, curl, gh (GitHub CLI), postgresql-client, Chromium
- Browser tooling: agent-browser (Vercel Labs) — enables E2E testing workflows via CDP. Uses system Chromium (
AGENT_BROWSER_EXECUTABLE_PATH=/usr/bin/chromium) - App: All 9 workspace packages (source), pre-built web UI
- User: Non-root
appuser(UID 1001) — required by Claude Code SDK - Archon dirs:
/.archon/workspaces,/.archon/worktrees
The multi-stage build keeps the image lean — no devDependencies, test files, docs, or .git/.
Maintenance
View Logs
docker compose logs -f # All services
docker compose logs -f app # App only
docker compose logs --tail=100 app # Last 100 lines
Update
git pull
docker compose --profile with-db --profile cloud up -d --build
Restart
docker compose restart # All
docker compose restart app # App only
Stop
docker compose down # Stop containers (data preserved)
docker compose down -v # Stop + delete volumes (destructive!)
Database Migrations (PostgreSQL)
Migrations run automatically on first startup. For subsequent migrations:
docker compose exec postgres psql -U postgres -d remote_coding_agent -f /migrations/019_workflow_resume_path.sql
Clean Up Docker Resources
docker system prune -a # Remove unused images/containers
docker volume prune # Remove unused volumes (caution!)
docker system df # Check disk usage
Troubleshooting
App won't start: "no_ai_credentials"
No AI assistant configured. Docker does not support CLAUDE_USE_GLOBAL_AUTH=true. Set one of these in .env:
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-...(runclaude setup-tokenlocally to get one)CLAUDE_API_KEY=sk-ant-...(from console.anthropic.com)- Or Codex credentials (
CODEX_ID_TOKEN,CODEX_ACCESS_TOKEN, etc.)
Caddy fails to start: "not a directory"
error mounting "Caddyfile": not a directory
The Caddyfile doesn't exist — Docker created a directory in its place. Fix:
rm -rf Caddyfile
cp Caddyfile.example Caddyfile
docker compose --profile cloud up -d
Caddy not getting SSL certificate
# Check DNS propagation
dig archon.example.com
# Should return your server IP
# Check Caddy logs
docker compose logs caddy
# Check firewall
sudo ufw status
# Ports 80 and 443 must be open
Common causes: DNS not propagated (wait 5-60min), firewall blocking 80/443, domain typo in .env.
Health check failing
The health endpoint is at /api/health (not /health):
curl http://localhost:3000/api/health
PostgreSQL connection refused
When using --profile with-db, ensure:
DATABASE_URLusespostgresas hostname (Docker service name), notlocalhost:DATABASE_URL=postgresql://postgres:postgres@postgres:5432/remote_coding_agent- The postgres container is healthy:
docker compose ps postgres - Migrations ran: check
docker compose logs postgresfor init script output
Permission errors in /.archon/
The container runs as appuser (UID 1001). If using bind mounts instead of Docker volumes:
sudo chown -R 1001:1001 /path/to/archon-data
Port conflicts
Default port is 3000. Change in .env:
PORT=3001
Container keeps restarting
docker compose ps
docker compose logs --tail=50 app
Common causes: missing .env file, invalid credentials, database unreachable.