Docker
Run Chvor in a Docker container with persistent storage, environment configuration, and Docker Compose.
5 min read
Docker
Docker is the simplest way to run Chvor in production. The official image is published to GitHub Container Registry and includes the server, client, and all runtime dependencies.
ghcr.io/luka-zivkovic/chvor:latest
Quick Start
Run Chvor with a single command:
docker run -d \
--name chvor \
-p 3001:3001 \
-v chvor-data:/root/.chvor \
-e ANTHROPIC_API_KEY=sk-ant-... \
ghcr.io/luka-zivkovic/chvor:latest
Open http://localhost:3001 in your browser. You should see the Brain Canvas.
Docker Compose (Recommended)
For production use, Docker Compose makes it easy to manage configuration, volumes, and restarts.
Create a docker-compose.yml:
services:
chvor:
image: ghcr.io/luka-zivkovic/chvor:latest
container_name: chvor
ports:
- "3001:3001"
volumes:
- chvor-data:/root/.chvor
env_file:
- .env
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/health"]
interval: 30s
timeout: 10s
retries: 3
volumes:
chvor-data:
Create a .env file next to your compose file:
NODE_ENV=production
ANTHROPIC_API_KEY=sk-ant-...
CHVOR_TOKEN=your-secret-token
# Optional channels
# TELEGRAM_BOT_TOKEN=...
# DISCORD_BOT_TOKEN=...
# SLACK_BOT_TOKEN=...
# SLACK_APP_TOKEN=...
Start it:
docker compose up -d
View logs:
docker compose logs -f chvor
Volumes and Persistence
Chvor stores all data in /root/.chvor inside the container. You must mount a volume to this path, or you will lose data when the container is recreated.
Named volume (recommended)
volumes:
- chvor-data:/root/.chvor
Docker manages the volume. Data persists across container restarts and image updates.
Bind mount
If you want direct access to the data from the host:
volumes:
- /home/user/chvor-data:/root/.chvor
Make sure the host directory exists and has appropriate permissions:
mkdir -p /home/user/chvor-data
What is stored in the volume
| Path | Contents |
|---|---|
/root/.chvor/chvor.db | SQLite database (conversations, memory) |
/root/.chvor/skills/ | Custom skill YAML files |
/root/.chvor/config.json | MCP tool configuration |
/root/.chvor/uploads/ | User-uploaded files |
/root/.chvor/logs/ | Server logs |
Environment Variables
Pass environment variables using any Docker method:
Inline with -e
docker run -d \
-e ANTHROPIC_API_KEY=sk-ant-... \
-e CHVOR_TOKEN=my-token \
ghcr.io/luka-zivkovic/chvor:latest
From a file with --env-file
docker run -d \
--env-file .env \
ghcr.io/luka-zivkovic/chvor:latest
In Docker Compose with env_file
services:
chvor:
image: ghcr.io/luka-zivkovic/chvor:latest
env_file:
- .env
See Environment Variables for the complete list.
Updating the Image
# Pull the latest image
docker compose pull
# Recreate the container with the new image
docker compose up -d
Your data is safe because it lives in the volume, not the container. The new container mounts the same volume and picks up where it left off.
To pin a specific version instead of latest:
image: ghcr.io/luka-zivkovic/chvor:1.2.0
Docker Networking for Channels
When running Chvor in Docker with messaging channels (Telegram, Discord, Slack), the container needs outbound internet access to reach external APIs. This works by default with Docker’s bridge network.
Telegram webhooks
If you are using Telegram webhooks (instead of polling), your Chvor container must be reachable from the internet. Set up a reverse proxy on the host:
services:
chvor:
image: ghcr.io/luka-zivkovic/chvor:latest
ports:
- "127.0.0.1:3001:3001" # Only expose to localhost
env_file:
- .env
volumes:
- chvor-data:/root/.chvor
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- chvor
volumes:
chvor-data:
The nginx.conf:
server {
listen 443 ssl;
server_name chvor.yourdomain.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
location / {
proxy_pass http://chvor:3001;
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;
proxy_read_timeout 300s;
}
client_max_body_size 50M;
}
server {
listen 80;
server_name chvor.yourdomain.com;
return 301 https://$host$request_uri;
}
Connecting to host services
If an MCP server runs on the host machine (not in Docker), use host.docker.internal to reach it:
{
"mcpServers": {
"my-tool": {
"command": "curl",
"args": ["http://host.docker.internal:8080"]
}
}
}
Note:
host.docker.internalworks on Docker Desktop (macOS/Windows) by default. On Linux, add--add-host=host.docker.internal:host-gatewayto yourdocker runcommand or add it to your compose file:extra_hosts: - "host.docker.internal:host-gateway"
Running MCP Tools in Docker
MCP tools that use npx work inside the Chvor container because Node.js is already installed. However, tools that require other runtimes (Python, Go, etc.) need those runtimes available.
Option 1: Install additional runtimes in a custom image
FROM ghcr.io/luka-zivkovic/chvor:latest
# Add Python for Python-based MCP servers
RUN apt-get update && apt-get install -y python3 python3-pip
RUN pip3 install uvx
Build and use:
docker build -t chvor-custom .
Option 2: Run MCP servers as separate containers
For complex tool setups, run MCP servers as sidecar containers and connect them over the Docker network. This is an advanced pattern — see the MCP documentation for details on running MCP servers over network transports.
Resource Limits
Set resource limits to prevent runaway processes:
services:
chvor:
image: ghcr.io/luka-zivkovic/chvor:latest
deploy:
resources:
limits:
memory: 2G
cpus: "2.0"
reservations:
memory: 512M
cpus: "0.5"
Troubleshooting
| Problem | Solution |
|---|---|
| Container exits immediately | Check logs: docker compose logs chvor. Usually a missing API key. |
| Cannot connect on port 3001 | Verify the port mapping: docker compose ps. Check firewall rules. |
Data lost after docker compose down | Use named volumes. docker compose down -v deletes volumes — avoid the -v flag. |
MCP tool npx fails | The container image includes Node.js. Check that the npm package name is correct. |
| WebSocket connection fails | Ensure your reverse proxy forwards Upgrade headers (see nginx config above). |
Next Steps
- Deployment & Self-Hosting — bare-metal deployment with systemd.
- Channel Setup — connect Telegram, Discord, and Slack.
- Environment Variables — complete variable reference.