Multi-Instance Development Environment
Purpose: Manage multiple parallel instances of the Orient for simultaneous testing of different feature branches using isolated resources.
When to use this skill: When you need to run multiple bot instances simultaneously (main repo + worktrees), troubleshoot port conflicts, verify instance isolation, or understand the multi-instance architecture.
Tags: #development #worktrees #docker #ports #isolation #testing
Overview
The multi-instance development system allows running multiple bot instances in parallel, each with:
- Unique instance ID (0-9): Auto-detected from worktree path
- Isolated ports: Formula-based offset calculation
- Separate databases: Instance-specific SQLite database files
- Isolated storage: Separate MinIO containers and S3 buckets
- Independent networks: Instance-specific Docker networks
Key Benefits
- Test multiple features branches simultaneously
- No resource conflicts between instances
- WhatsApp session safety (disabled in worktrees)
- 100% backward compatible with main repo
Architecture
Instance ID Assignment
Instance 0 (Main Repository)
- Path:
/Users/*/code/*/orienter(noclaude-worktreesin path) - Ports: Original values (80, 4098, etc.)
- WhatsApp: Enabled by default
- Use case: Primary development environment
Instance 1-9 (Worktrees)
- Path:
/Users/*/claude-worktrees/orienter/feature-name-* - Instance ID: Hash of worktree name modulo 9 + 1
- Ports: Offset by
instance_id * 1000 - WhatsApp: Disabled by default
- Use case: Parallel feature testing
Manual Override
# Force specific instance ID
export AI_INSTANCE_ID=3
./run.sh dev
Port Offset Formula
new_port = base_port + (instance_id * 1000)
Port Allocation Table:
| Service | Base | Instance 0 | Instance 1 | Instance 2 | Instance 3 | | ------------- | ---- | ---------- | ---------- | ---------- | ---------- | | Nginx | 80 | 80 | 1080 | 2080 | 3080 | | Dashboard | 4098 | 4098 | 5098 | 6098 | 7098 | | OpenCode | 4099 | 4099 | 5099 | 6099 | 7099 | | Vite | 5173 | 5173 | 6173 | 7173 | 8173 | | MinIO API | 9000 | 9000 | 10000 | 11000 | 12000 | | MinIO Console | 9001 | 9001 | 10001 | 11001 | 12001 |
Note: WhatsApp is integrated into Dashboard (same port). Database is SQLite (file-based, no port).
Resource Isolation
Each instance has:
- Docker containers:
orienter-{service}-{instance_id}(nginx, minio) - Docker volumes:
minio-data-{instance_id} - Docker network:
orienter-network-{instance_id} - SQLite database:
.dev-data/instance-{instance_id}/orient.db - S3 bucket:
orienter-data-{instance_id} - Local directories:
.dev-data/instance-{instance_id},logs/instance-{instance_id}
Common Use Cases
1. Running Main + Worktree Simultaneously
Scenario: Test a feature branch while keeping main repo running.
# Terminal 1: Start main repo (Instance 0)
cd ~/code/genoox/orienter
./run.sh dev
# Access at: http://localhost:80
# Terminal 2: Start worktree (Instance 1)
cd ~/claude-worktrees/orienter/feature-auth-refactor-*
./run.sh dev
# Access at: http://localhost:1080
# Terminal 3: List all running instances
./run.sh instances
Expected Result:
- Both instances running without conflicts
- Separate dashboards accessible on different ports
- Independent databases and storage
2. Testing Multiple Feature Branches
Scenario: Compare behavior across 3 different implementations.
# Terminal 1: Feature A
cd ~/claude-worktrees/orienter/feature-a-*
./run.sh dev # Instance 1: http://localhost:1080
# Terminal 2: Feature B
cd ~/claude-worktrees/orienter/feature-b-*
./run.sh dev # Instance 2: http://localhost:2080
# Terminal 3: Feature C
cd ~/claude-worktrees/orienter/feature-c-*
./run.sh dev # Instance 3: http://localhost:3080
3. Enabling WhatsApp in Worktree (Advanced)
Warning: Only enable if main repo WhatsApp is stopped.
# Stop main repo WhatsApp first
cd ~/code/genoox/orienter
./run.sh dev stop
# Enable WhatsApp in worktree
cd ~/claude-worktrees/orienter/test-feature-*
./run.sh dev --enable-whatsapp
# WhatsApp now runs on same port as Dashboard (Instance 1)
Why disabled by default?
- WhatsApp sessions are global per phone number
- Running two instances causes session conflicts
- Only one instance should connect to WhatsApp at a time
How It Works
1. Instance Detection (scripts/instance-env.sh)
detect_instance_id() {
# Priority 1: Explicit override
if [ -n "$AI_INSTANCE_ID" ]; then echo "$AI_INSTANCE_ID"; return; fi
# Priority 2: Detect from worktree path
if [[ "$(pwd)" == *"/claude-worktrees/"* ]]; then
local name=$(pwd | grep -oP 'claude-worktrees/[^/]+/\K[^/]+' | head -1)
echo $(($(echo -n "$name" | cksum | cut -d' ' -f1) % 9 + 1))
return
fi
# Priority 3: Default to instance 0
echo "0"
}
2. Port Calculation
calculate_port() {
local base_port=$1
local instance_id=$2
echo $(( base_port + (instance_id * 1000) ))
}
3. Environment Configuration
The system exports environment variables:
- Ports:
NGINX_PORT,DASHBOARD_PORT,OPENCODE_PORT,VITE_PORT, etc. - Docker:
COMPOSE_PROJECT_NAME, container names with instance ID - Database:
SQLITE_DB_PATHincludes instance ID - Storage:
S3_BUCKETincludes instance ID - Directories:
DATA_DIR,LOG_DIR,PID_DIRare instance-specific
4. Docker Compose Templatization
services:
nginx:
container_name: orienter-nginx-${AI_INSTANCE_ID:-0}
ports:
- '${NGINX_PORT:-80}:80'
networks:
- orienter-network
minio:
container_name: orienter-minio-${AI_INSTANCE_ID:-0}
ports:
- '${MINIO_API_PORT:-9000}:9000'
- '${MINIO_CONSOLE_PORT:-9001}:9001'
volumes:
- minio-data:/data
5. Nginx Configuration Generation
Dynamic generation with envsubst:
# In scripts/dev.sh
envsubst '$VITE_PORT,$DASHBOARD_PORT,$OPENCODE_PORT' \
< docker/nginx-local.template.conf \
> docker/nginx-local.conf
Troubleshooting
Port Conflicts
Symptom: Error: "Address already in use" or "port is already allocated"
Diagnosis:
# Check what's using a port
lsof -ti :1080
# List all running instances
./run.sh instances
# Check for orphaned processes
docker ps -a --filter "name=orienter-"
Solutions:
- Stop conflicting instance:
cd ~/claude-worktrees/orienter/other-feature
./run.sh dev stop
- Force kill port:
lsof -ti :1080 | xargs kill -9
- Clean up Docker containers:
docker ps -a --filter "name=orienter-" --format "{{.Names}}" | xargs docker rm -f
- Manual instance ID override:
export AI_INSTANCE_ID=7 # Use different instance
./run.sh dev
Database Issues
Symptom: SQLite database errors or missing tables
Diagnosis:
# Check if database file exists
ls -la .dev-data/instance-*/orient.db
# Check database size
du -h .dev-data/instance-*/orient.db
Solutions:
- Create database directory:
source scripts/instance-env.sh
mkdir -p "$(dirname "$SQLITE_DB_PATH")"
- Reset database:
rm .dev-data/instance-$AI_INSTANCE_ID/orient.db
./run.sh dev # Will recreate on startup
Container Isolation Issues
Symptom: Instances interfering with each other, or wrong data showing up
Diagnosis:
# List all containers with their networks
docker ps --format "table {{.Names}}\t{{.Networks}}\t{{.Ports}}"
# List volumes
docker volume ls | grep orienter
Solutions:
- Verify volume isolation:
docker volume ls | grep minio-data
# Should show: minio-data-0, minio-data-1, etc.
- Clean slate (nuclear option):
# Stop all instances
./run.sh dev stop
# Remove all containers and volumes
docker compose -f docker/docker-compose.infra.yml down -v
# Restart
./run.sh dev
WhatsApp Session Conflicts
Symptom: WhatsApp constantly disconnecting, or multiple QR codes appearing
Diagnosis:
# Check if multiple instances have WhatsApp enabled
ps aux | grep dashboard
# Check WhatsApp enabled status
source scripts/instance-env.sh
echo "WhatsApp enabled: $WHATSAPP_ENABLED"
Solutions:
- Ensure only one WhatsApp instance:
# Stop all instances
./run.sh dev stop
cd ~/code/genoox/orienter
./run.sh dev stop
# Start only main repo
cd ~/code/genoox/orienter
./run.sh dev # WhatsApp enabled by default
- If you need WhatsApp in worktree:
# Stop main repo first
cd ~/code/genoox/orienter
./run.sh dev stop
# Start worktree with WhatsApp
cd ~/claude-worktrees/orienter/feature-*
./run.sh dev --enable-whatsapp
Instance Detection Not Working
Symptom: Worktree shows as Instance 0, or ports aren't offset
Diagnosis:
# Check current path
pwd
# Should contain "/claude-worktrees/"
# Test detection
source scripts/instance-env.sh
echo "Detected instance: $AI_INSTANCE_ID"
Solutions:
- Verify worktree path:
pwd | grep claude-worktrees
# Should return the path if in worktree
- Manual override:
export AI_INSTANCE_ID=2
./run.sh dev
Best Practices
Development Workflow
-
Main Repo = Production-like
- Keep main repo as Instance 0
- Use for integration testing
- Run with WhatsApp enabled
-
Worktrees = Feature Testing
- Create worktrees for features
- Each gets unique instance ID
- WhatsApp disabled by default
-
Resource Management
- Monitor RAM usage (~500MB per instance)
- Don't exceed 9 instances simultaneously
- Stop unused instances:
./run.sh dev stop
Testing Strategies
- Parallel Feature Comparison
# Test 3 implementations side-by-side
./run.sh instances # Check all running
open http://localhost:1080 # Feature A
open http://localhost:2080 # Feature B
open http://localhost:3080 # Feature C
- Database Migration Testing
# Each instance has separate SQLite DB
# Test migrations independently:
cd ~/claude-worktrees/orienter/migration-test
./run.sh dev
# Run migrations (affects only Instance 1's DB)
Cleanup Procedures
Daily cleanup:
# Stop all instances
./run.sh dev stop
# Check for orphans
./run.sh instances
docker ps | grep orienter
Weekly cleanup:
# Remove old volumes (optional)
docker volume ls | grep orienter
docker volume prune -f
# Clear logs
rm -rf logs/instance-*/*.log
# Clear old database files
rm -rf .dev-data/instance-*
Monitoring & Debugging
Check Instance Status
# List all running instances with ports
./run.sh instances
# Check specific instance
docker ps --filter "name=orienter-.*-1"
# View logs
tail -f logs/instance-1/dashboard-dev.log
Verify Isolation
# Database isolation (SQLite - file-based)
ls -la .dev-data/instance-0/orient.db
ls -la .dev-data/instance-1/orient.db
# Should show different files
# Storage isolation
docker exec orienter-minio-0 mc ls local/
docker exec orienter-minio-1 mc ls local/
# Should show different buckets
Performance Monitoring
# Check resource usage
docker stats --filter "name=orienter-"
# Check port availability
for port in 1080 5098 5099 6173 10000 10001; do
lsof -ti :$port >/dev/null 2>&1 && echo "Port $port: IN USE" || echo "Port $port: FREE"
done
Configuration Reference
Environment Variables
Set in scripts/instance-env.sh, exported to all services:
Instance Identification:
AI_INSTANCE_ID- Instance number (0-9)COMPOSE_PROJECT_NAME- Docker Compose project name
Service Ports:
NGINX_PORT- Nginx reverse proxy portDASHBOARD_PORT- Dashboard API port (includes WhatsApp)OPENCODE_PORT- OpenCode server portVITE_PORT- Vite dev server port
Infrastructure Ports:
MINIO_API_PORT- MinIO S3 API portMINIO_CONSOLE_PORT- MinIO web console port
Database Configuration:
SQLITE_DB_PATH- SQLite database file path (instance-specific)
Storage Configuration:
S3_BUCKET- MinIO bucket name (includes instance ID)AWS_ENDPOINT_URL- MinIO endpoint URL
Directories:
DATA_DIR- Instance data directoryLOG_DIR- Instance logs directoryPID_DIR- Process ID files directory
Feature Flags:
WHATSAPP_ENABLED- WhatsApp integration enabled (true/false)
Command Reference
Instance Management:
./run.sh instances # List all running instances
./run.sh dev # Start instance (auto-detect ID)
./run.sh dev stop # Stop current instance
./run.sh dev status # Show instance status
./run.sh dev logs # View instance logs
./run.sh dev --enable-whatsapp # Enable WhatsApp in worktree
Testing:
./scripts/run-all-tests.sh # Run all multi-instance tests
./scripts/test-instance-env.sh # Unit tests
./scripts/test-config-templates.sh # Template tests
./scripts/test-integration.sh # Integration tests
Debugging:
source scripts/instance-env.sh # Load instance environment
display_instance_info # Show instance configuration
docker ps --filter "name=orienter-" # List containers
docker logs orienter-nginx-1 # View container logs
Advanced Topics
Custom Instance ID Assignment
For complex scenarios, you can manually assign instance IDs:
# In worktree .env file
echo "AI_INSTANCE_ID=5" >> .env
# Or via environment
export AI_INSTANCE_ID=5
./run.sh dev
Port Collision Resolution
If two worktrees get the same hash (rare), manually override:
# Worktree 1
cd ~/claude-worktrees/orienter/feature-a
export AI_INSTANCE_ID=1
./run.sh dev
# Worktree 2 (collision detected)
cd ~/claude-worktrees/orienter/feature-b
export AI_INSTANCE_ID=2
./run.sh dev
Production Considerations
This feature is development-only:
- Production deploys still use single instance (instance 0)
- Multi-instance is NOT for production horizontal scaling
- Use for local testing only
Related Skills
worktree-operations- Creating and managing git worktreesdocker-development- Local Docker development workflowtesting-strategy- Testing in the monorepoproduction-debugging- Production environment debugging
Implementation Files
Core:
scripts/instance-env.sh- Instance detection and configurationscripts/dev.sh- Development script integrationdocker/docker-compose.infra.yml- Templatized infrastructure (nginx, minio)docker/nginx-local.template.conf- Dynamic Nginx config
Testing:
tests/scripts/instance-env.test.ts- TypeScript unit testsscripts/test-*.sh- Bash integration testsscripts/run-all-tests.sh- Test runner
Migration History
2026-01-26: SQLite Migration (v2.0.0)
Breaking Changes:
- Database: Migrated from PostgreSQL to SQLite (file-based)
- WhatsApp: Integrated into Dashboard (single
DASHBOARD_PORTinstead of separateWHATSAPP_PORT) - Docker: Removed PostgreSQL container from all compose files
Removed Environment Variables:
POSTGRES_PORT- No longer needed (SQLite is file-based)POSTGRES_DB- No longer neededPOSTGRES_USER- No longer neededPOSTGRES_PASSWORD- No longer neededDATABASE_URL- Replaced withSQLITE_DB_PATHWHATSAPP_PORT- WhatsApp now usesDASHBOARD_PORT
New Environment Variables:
SQLITE_DB_PATH- Path to SQLite database fileDATABASE_TYPE=sqlite- Database type indicator
Migration Steps:
- Update
.envto remove PostgreSQL variables - Run
./run.sh dev stopto stop any running instances - Remove old PostgreSQL data:
docker volume rmany postgres volumes - Start fresh with
./run.sh dev
Last Updated: 2026-01-26 Feature Version: 2.0.0 Architecture: SQLite database, unified Dashboard+WhatsApp server Minimum Requirements: Docker, Bash 4.0+, ~500MB RAM per instance
微信扫一扫