Deployment
Guides for deploying Prometheal in production.
Production deploy (recommended)
One-command production deployment with auto-TLS, Redis, and secure defaults.
Prerequisites
- Docker and Docker Compose
- A server with 2+ GB RAM
- A domain name (for TLS) — or use localhost for testing
Deploy
git clone https://github.com/ia03/prometheal.git
cd prometheal
./deploy.shThe script will:
- Ask for your domain (or press Enter for localhost)
- Generate secure random secrets (JWT, encryption key, Postgres password)
- Build the sandbox image
- Start the full stack: App + PostgreSQL + Redis + Caddy
- Wait for the server to be healthy
Once ready, open the URL to complete the setup wizard.
What's included
| Service | Purpose |
|---|---|
| App | Prometheal (Next.js, standalone build) |
| PostgreSQL 16 | Database (secure random password) |
| Redis 7 | Distributed state (rate limits, session cache, locks) |
| Caddy 2 | Reverse proxy with automatic TLS certificate provisioning |
Localhost mode
If you skip the domain prompt (just press Enter), the stack runs at http://localhost on port 80. Caddy serves as a plain HTTP reverse proxy — no TLS. This is useful for local testing.
Domain mode
When you provide a domain (e.g., prometheal.example.com), Caddy automatically provisions a TLS certificate via Let's Encrypt. Make sure:
- DNS for the domain points to your server
- Ports 80 and 443 are open (Caddy needs both for ACME challenges)
Install gVisor (recommended)
gVisor adds kernel-level sandboxing on top of Docker:
sudo apt-get install -y runsc
sudo runsc install
sudo systemctl restart dockerManaging the stack
docker compose -f docker-compose.prod.yml logs -f # View logs
docker compose -f docker-compose.prod.yml down # Stop
docker compose -f docker-compose.prod.yml up -d # Restart
docker compose -f docker-compose.prod.yml ps # StatusRe-running deploy.sh
The script is idempotent. If .env.production already exists, it keeps the existing config. If the sandbox image already exists, it skips the build. Delete .env.production to regenerate secrets.
Manual single instance (Docker Compose)
If you prefer to configure things yourself instead of using deploy.sh.
Prerequisites
- Docker and Docker Compose
- A server with 2+ GB RAM
- A domain name (recommended) with TLS termination (nginx, Caddy, or cloud load balancer)
1. Clone and configure
git clone https://github.com/ia03/prometheal.git
cd prometheal
cp .env.example .envGenerate secrets:
# JWT secret
echo "JWT_SECRET=$(openssl rand -base64 32)" >> .env
# Encryption key (64 hex chars)
echo "ENCRYPTION_KEY=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")" >> .envEdit .env:
DATABASE_URL=postgresql://prometheal:your-strong-password@postgres:5432/prometheal
NEXT_PUBLIC_APP_URL=https://prometheal.yourdomain.com
SANDBOX_PROVIDER=docker2. Build the sandbox image
docker build -t prometheal-sandbox:latest -f sandbox/Dockerfile sandbox/3. Install gVisor (recommended)
sudo apt-get install -y runsc
sudo runsc install
sudo systemctl restart docker4. Start
docker compose up -d5. Set up a reverse proxy
Example with Caddy (auto-TLS):
prometheal.yourdomain.com {
reverse_proxy localhost:3000
}Example with nginx:
server {
listen 443 ssl;
server_name prometheal.yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Required for SSE streaming (chat responses)
proxy_buffering off;
proxy_cache off;
}
}Important: Disable proxy buffering for the chat endpoints — Prometheal streams responses via server-sent events.
Multi-instance deployment
For high availability or handling more concurrent agents. Requires Redis for distributed state.
Architecture
┌─────────┐
│ Nginx │
│ (LB) │
└────┬────┘
┌────┴────┐
┌─────┤ ├─────┐
▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐
│ App 1│ │ App 2│ │ App N│
└──┬───┘ └──┬───┘ └──┬───┘
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐
│PostgreSQL│ │ Redis │
└─────────┘ └─────────┘Configuration
Use docker-compose.multi.yml:
docker compose -f docker-compose.multi.yml up -dKey differences from single-instance:
REDIS_URLmust be set — enables distributed rate limiting, session cache, MCP tool cache, and sandbox locks- Nginx load balances across app instances
- All instances share the same PostgreSQL and Redis
Scaling
docker compose -f docker-compose.multi.yml up -d --scale app=3Sticky sessions
The nginx config uses IP hash for session affinity. This isn't strictly required (sessions are stateless JWTs), but it helps with:
- Tool confirmation flows (the pending confirmation is stored in-memory on the instance that started it)
- MCP client connections (stdio processes are per-instance)
E2B Cloud deployment
If you don't want to manage sandbox infrastructure:
SANDBOX_PROVIDER=e2b
E2B_API_KEY=your-keySandboxes run as Firecracker microVMs on E2B's infrastructure. You still need to host the Prometheal server, but sandbox containers are managed by E2B.
Database management
Migrations
Migrations run automatically on startup via docker-entrypoint.sh:
node_modules/prisma/build/index.js migrate deployFor manual migration:
npx prisma migrate deploy # production (applies pending migrations)
npx prisma migrate dev # development (creates + applies migrations)Backups
Use the included backup script for one-command backups:
./backup.sh # Back up to ./backups/
./backup.sh /mnt/backups # Custom backup directory
COMPOSE_FILE=docker-compose.prod.yml ./backup.sh # Production stackThis backs up:
- PostgreSQL database — full
pg_dumpin custom format - Uploads — agent documents and library files
- Encryption key — extracted from
.env/.env.production(store this in a secure vault) - Workspace volume list — for manual volume backup if needed
Restore:
# Database
docker compose exec -T postgres pg_restore -U prometheal -d prometheal --clean < backups/<timestamp>/database.dump
# Uploads
tar -xzf backups/<timestamp>/uploads.tar.gzScheduled backups (cron):
# Daily at 2 AM, keep 30 days
0 2 * * * cd /path/to/prometheal && COMPOSE_FILE=docker-compose.prod.yml ./backup.sh /mnt/backups && find /mnt/backups -maxdepth 1 -mtime +30 -exec rm -rf {} +Critical
Always back up your ENCRYPTION_KEY separately in a secure vault (AWS Secrets Manager, HashiCorp Vault, 1Password). Without it, encrypted credentials in the database cannot be recovered.
Sandbox volumes
Agent workspaces are stored in Docker named volumes (prometheal-workspace-{agentId}). These persist across sandbox restarts. To back up a specific workspace:
# List volumes
docker volume ls | grep prometheal-workspace
# Back up a volume
docker run --rm -v prometheal-workspace-<agentId>:/data -v $(pwd):/backup alpine tar czf /backup/workspace.tar.gz /dataHealth checks
| Endpoint | What it checks |
|---|---|
GET /api/health | Database connection (public, used by Docker healthcheck) |
GET /api/settings/system | Database connection, sandbox config, agent counts, uptime (admin only) |
GET /api/llm-proxy/health | LLM proxy availability |
GET /api/sandbox/health | Sandbox provider health |
Use these for monitoring and load balancer health checks.
Environment variable reference
See configuration.md for the complete list of environment variables.
Updating
git pull
docker compose build
docker compose up -dMigrations run automatically on startup. Always back up your database before updating.
Troubleshooting
Container won't start
docker compose logs appCommon issues:
DATABASE_URLis wrong or PostgreSQL isn't runningENCRYPTION_KEYis empty or not 64 hex chars- Port 3000 is already in use
Sandbox won't create
docker compose logs app | grep sandboxCommon issues:
- Docker socket not accessible (if Prometheal runs in Docker, mount
/var/run/docker.sock) - Sandbox image not built (
docker build -t prometheal-sandbox:latest -f sandbox/Dockerfile sandbox/) - E2B API key invalid or expired
Chat responses are slow or cut off
- Check if proxy buffering is disabled in your reverse proxy
- Ensure the LLM API key is valid (Settings > General)
- Check spending limits (Settings > Usage)
MCP integration not connecting
# Test the MCP server manually
npx -y @modelcontextprotocol/server-github- Check credentials are correct in Settings > Integrations
- Check the integration status (ACTIVE/INACTIVE/ERROR)
- Look for errors in the server logs