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.


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.

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

PathContents
/root/.chvor/chvor.dbSQLite database (conversations, memory)
/root/.chvor/skills/Custom skill YAML files
/root/.chvor/config.jsonMCP 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.internal works on Docker Desktop (macOS/Windows) by default. On Linux, add --add-host=host.docker.internal:host-gateway to your docker run command 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

ProblemSolution
Container exits immediatelyCheck logs: docker compose logs chvor. Usually a missing API key.
Cannot connect on port 3001Verify the port mapping: docker compose ps. Check firewall rules.
Data lost after docker compose downUse named volumes. docker compose down -v deletes volumes — avoid the -v flag.
MCP tool npx failsThe container image includes Node.js. Check that the npm package name is correct.
WebSocket connection failsEnsure your reverse proxy forwards Upgrade headers (see nginx config above).

Next Steps