Self-Hosted
Komand is designed to be self-hosted on your own infrastructure using Docker. This gives you full control over your data, deployment environment, and network configuration.
Prerequisites
Section titled “Prerequisites”- Docker and Docker Compose v2+
- 4 GB RAM minimum (8 GB recommended for production)
- PostgreSQL 17 (included in Docker Compose, or bring your own)
Quick Start
Section titled “Quick Start”1. Clone the Repository
Section titled “1. Clone the Repository”git clone https://github.com/komand-ai/komand-server.gitcd komand-server2. Configure Environment
Section titled “2. Configure Environment”Create a .env file in the docker/ directory:
# DatabasePOSTGRES_DB=komandPOSTGRES_USER=komandPOSTGRES_PASSWORD=your-secure-password
# EnvironmentDOTNET_ENVIRONMENT=ProductionASPNETCORE_ENVIRONMENT=Production
# Connection string (must match POSTGRES_* values)ORLEANS_CONNECTION_STRING=Host=postgres;Database=komand;Username=komand;Password=your-secure-password
# LoggingSEQ_URL=http://seq:80
# CORS (your domain)Cors__AllowedOrigins=https://komand.your-domain.com
# JWT (MUST change in production — minimum 32 characters)Jwt__SecretKey=replace-this-with-a-secure-random-string-min-32-chars3. Start the Stack
Section titled “3. Start the Stack”docker compose -f docker/docker-compose.yml up -d4. Verify
Section titled “4. Verify”# Check all services are healthydocker compose -f docker/docker-compose.yml ps
# Hit the health endpointcurl http://localhost:5000/healthThe dashboard is available at http://localhost:5000.
Services
Section titled “Services”The Docker Compose stack runs four services with enforced resource limits:
| Service | Image | Ports | Memory | CPU | Health Check |
|---|---|---|---|---|---|
| postgres | postgres:17-alpine | 5432 | 512 MB | 1.0 | pg_isready |
| seq | datalust/seq:latest | 5341 (UI), 5342 (ingest) | 256 MB | 0.5 | — |
| silo | Custom (Dockerfile.silo) | 11111, 30000 | 1 GB | 2.0 | TCP socket 11111 |
| gateway | Custom (Dockerfile.gateway) | 5000 | 512 MB | 1.0 | HTTP /health |
Startup Order
Section titled “Startup Order”Services start in dependency order with health check gates:
postgres (healthy) → silo (healthy) → gateway seq (parallel)The silo waits for PostgreSQL to be ready before starting. The gateway waits for the silo to be healthy before starting. Seq starts independently.
Volumes
Section titled “Volumes”| Volume | Purpose |
|---|---|
postgres_data | PostgreSQL data persistence |
seq_data | Structured log data |
Database Initialisation
Section titled “Database Initialisation”On first startup, PostgreSQL runs the init script docker/init-db/01-orleans-schema.sql which creates:
Orleans tables:
OrleansMembershipTable— silo cluster membershipOrleansMembershipVersionTable— cluster version trackingOrleansStorage— grain state persistence (indexed by grain ID hash)OrleansRemindersTable— durable reminders for CronGrainOrleansQuery— stored procedures for Orleans operations
Komand tables:
komand_audit_log— audit trail with indexes on timestamp, agent_id, actor_id
Orleans manages its own schema — you don’t need to run migrations manually.
Container Security
Section titled “Container Security”Both application containers (silo and gateway) run with security hardening:
- Non-root user — runs as
komanduser inside the container - Resource limits — CPU and memory caps prevent resource exhaustion
- Multi-stage builds — production images contain only the .NET runtime, not the SDK
- Health checks — unhealthy containers are flagged for restart
- No host network — containers use an isolated Docker network
Build Process
Section titled “Build Process”The gateway Dockerfile uses a three-stage build:
Stage 1: Node.js 22 → Build React frontend (npm ci + npm run build)Stage 2: .NET SDK 10.0-preview → Build Gateway + copy frontend dist to wwwroot/Stage 3: ASP.NET Core 10.0-preview runtime → Production image with curl for health checksThe silo Dockerfile uses a two-stage build:
Stage 1: .NET SDK 10.0-preview → Build SiloStage 2: ASP.NET Core 10.0-preview runtime → Production imageProduction Hardening
Section titled “Production Hardening”TLS Termination
Section titled “TLS Termination”Komand runs HTTP internally. Use a reverse proxy for TLS termination:
nginx:
server { listen 443 ssl http2; server_name komand.your-domain.com;
ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem;
location / { proxy_pass http://localhost:5000; 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;
# SignalR WebSocket support proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }}Caddy (automatic TLS):
komand.your-domain.com { reverse_proxy localhost:5000}JWT Secret Key
Section titled “JWT Secret Key”The default development key must be replaced in production. Generate a secure key:
openssl rand -base64 48Set it as Jwt__SecretKey in your .env file. The key must be at least 32 characters.
Firewall
Section titled “Firewall”Only expose the reverse proxy port (443). All other ports should be internal-only:
| Port | Access |
|---|---|
| 443 | Public (via reverse proxy) |
| 5000 | Internal only (Gateway) |
| 11111 | Internal only (Orleans silo-to-silo) |
| 30000 | Internal only (Orleans client-to-silo) |
| 5432 | Internal only (PostgreSQL) |
| 5341/5342 | Internal only (Seq) |
In production, CORS defaults to no allowed origins if not configured. You must explicitly set:
Cors__AllowedOrigins=https://komand.your-domain.comResource Limits
Section titled “Resource Limits”Adjust the Docker Compose resource limits based on your workload:
| Service | Small (< 10 agents) | Medium (10–100 agents) | Large (100+ agents) |
|---|---|---|---|
| Silo | 1 GB / 2 CPU | 2 GB / 4 CPU | 4 GB / 8 CPU |
| Gateway | 512 MB / 1 CPU | 1 GB / 2 CPU | 2 GB / 4 CPU |
| PostgreSQL | 512 MB / 1 CPU | 1 GB / 2 CPU | 2 GB / 4 CPU |
Backups
Section titled “Backups”PostgreSQL
Section titled “PostgreSQL”Back up the database regularly:
# One-time backupdocker exec komand-postgres pg_dump -U komand komand > backup-$(date +%Y%m%d).sql
# Restore from backupdocker exec -i komand-postgres psql -U komand komand < backup-20260223.sqlAutomated Backups
Section titled “Automated Backups”Add a cron job for daily backups:
0 2 * * * docker exec komand-postgres pg_dump -U komand komand | gzip > /backups/komand-$(date +\%Y\%m\%d).sql.gzWhat to Back Up
Section titled “What to Back Up”| Data | Location | Method |
|---|---|---|
| Grain state (agents, sessions, skills) | PostgreSQL | pg_dump |
| Audit logs | PostgreSQL | pg_dump |
| Structured logs | Seq volume | Volume backup |
| Configuration | .env + appsettings.json | File backup |
External PostgreSQL
Section titled “External PostgreSQL”To use an existing PostgreSQL instance instead of the containerised one:
- Remove the
postgresservice fromdocker-compose.yml - Remove the
postgres_datavolume - Update
ORLEANS_CONNECTION_STRINGto point to your database - Import the schema:
psql -f docker/init-db/01-orleans-schema.sql - Ensure the database user has full schema permissions
ORLEANS_CONNECTION_STRING=Host=your-db.example.com;Port=5432;Database=komand;Username=komand;Password=your-password;SslMode=RequireMonitoring
Section titled “Monitoring”Health Endpoint
Section titled “Health Endpoint”The /health endpoint checks:
- Orleans silo — pings the
SkillRegistryGrainwith a 5-second timeout - PostgreSQL — connection health check (if connection string is configured)
Returns HTTP 200 (healthy) or HTTP 503 (unhealthy).
Seq Dashboard
Section titled “Seq Dashboard”Seq provides a web UI at port 5341 for searching and analysing structured logs. All log entries include:
- Correlation ID (
X-Request-Id) - Application name (
Komand.SiloorKomand.Gateway) - Grain activation/deactivation events
- API request durations
Metrics to Monitor
Section titled “Metrics to Monitor”| Metric | Alert Threshold | Source |
|---|---|---|
/health status | Non-200 for > 30s | Health endpoint |
| Container restarts | > 3 in 10 minutes | Docker |
| PostgreSQL connections | > 80% pool | PostgreSQL |
| Memory usage | > 85% of limit | Docker stats |
| Grain call timeouts | > 5% error rate | Seq logs |
Updates
Section titled “Updates”cd komand-servergit pulldocker compose -f docker/docker-compose.yml builddocker compose -f docker/docker-compose.yml up -dOrleans handles grain state migration across versions automatically. No manual migration steps are required.
Zero-Downtime Updates
Section titled “Zero-Downtime Updates”For zero-downtime deployments, use rolling updates with a multi-silo configuration. Orleans supports adding and removing silos from a running cluster without service interruption.