Skip to content

Docker deployment ​

Xiajiao (虾饺) is designed so you don’t need Docker—npm start is enough. If you prefer containers, this path is fully supported.

When to use Docker ​

ScenarioDocker?
Personal / local dev❌ faster with npm start
Shared team / reproducible env✅
Co-deploy with other services✅ Docker Compose
CI/CD✅ build + push
Avoid local Node install✅ image includes runtime

Quick start ​

bash
git clone https://github.com/moziio/xiajiao.git
cd xiajiao

docker build -t xiajiao .

docker run -d -p 18800:18800 \
  -v xiajiao-data:/app/data \
  -v xiajiao-uploads:/app/public/uploads \
  -e OWNER_KEY=my-secret \
  --name xiajiao \
  --restart unless-stopped \
  xiajiao

Open http://localhost:18800.

Image facts ​

PropertyValue
Basenode:22-slim
Buildnpm ci --omit=dev
COPY strategySelective — only server/, public/, templates, presets, config
SizeSmall (Debian slim, no dev deps)
Port18800
Workdir/app
NODE_ENVproduction (set in image)
Volume/app/data (single volume for all persistent data)

Optimized build

The Dockerfile uses selective COPY instead of COPY . . — only production-necessary files enter the image. Combined with node:22-slim base and NODE_ENV=production, the result is a small, secure image.

Persistent volumes ​

One primary volume:

VolumeMountContentsCritical
xiajiao-data/app/dataSQLite, workspaces, SOUL templates, memory, HTTP tool definitions, custom toolsyes

Uploads live under public/uploads/ (not under data/). If you need to persist uploads across container recreation, mount a separate volume for /app/public/uploads as shown in the quick start example above.

Mount volumes

Without volumes, removing the container deletes messages, Agents, and memory.

Bind mounts (host paths) ​

For direct file access (backup, edit SOUL.md):

bash
mkdir -p /opt/xiajiao-data /opt/xiajiao-uploads

docker run -d -p 18800:18800 \
  -v /opt/xiajiao-data:/app/data \
  -v /opt/xiajiao-uploads:/app/public/uploads \
  -e OWNER_KEY=my-secret \
  --name xiajiao xiajiao

Edit /opt/xiajiao-data/workspace-xxx/SOUL.md on the host.

docker-compose.yml:

yaml
services:
  xiajiao:
    build: .
    ports:
      - "18800:18800"
    volumes:
      - ./volumes/data:/app/data
      - ./volumes/uploads:/app/public/uploads
    environment:
      - OWNER_KEY=${OWNER_KEY:-admin}
      - IM_PORT=18800
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:18800"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

.env:

bash
OWNER_KEY=your-strong-password
bash
docker compose up -d

docker compose ps
docker compose logs -f xiajiao

docker compose down

Environment variables ​

VariablePurposeDefault
IM_PORTPort18800
OWNER_KEYAdmin passwordadmin
LLM_MODELLM modedirect
NODE_ENVNode envproduction

Common tasks ​

Logs ​

bash
docker logs -f xiajiao
docker logs --tail 100 xiajiao

Shell in container ​

bash
docker exec -it xiajiao sh

ls /app/data/
cat /app/data/workspace-xxx/SOUL.md

Upgrade ​

bash
cd xiajiao
git pull
docker build -t xiajiao .
docker stop xiajiao
docker rm xiajiao
docker run -d -p 18800:18800 \
  -v xiajiao-data:/app/data \
  -v xiajiao-uploads:/app/public/uploads \
  -e OWNER_KEY=my-secret \
  --name xiajiao xiajiao

With Compose:

bash
git pull
docker compose up -d --build

Backup ​

bash
docker cp xiajiao:/app/data ./backup-data
docker cp xiajiao:/app/public/uploads ./backup-uploads

docker run --rm -v xiajiao-data:/data -v $(pwd):/backup alpine \
  tar czf /backup/xiajiao-data-backup.tar.gz -C /data .

Nginx reverse proxy + HTTPS ​

docker-compose.yml excerpt (Xiajiao only exposes internally):

yaml
services:
  xiajiao:
    build: .
    expose:
      - "18800"
    volumes:
      - ./volumes/data:/app/data
      - ./volumes/uploads:/app/public/uploads
    environment:
      - OWNER_KEY=${OWNER_KEY:-admin}
    restart: unless-stopped
    networks:
      - web

networks:
  web:
    external: true

Host Nginx:

nginx
server {
    listen 80;
    server_name im.yourdomain.com;

    location / {
        proxy_pass http://xiajiao:18800;
        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 86400;
        proxy_send_timeout 86400;
    }
}

WebSockets

Upgrade and Connection are required. Long proxy_read_timeout avoids dropping WS.

FAQ ​

Cannot reach container ​

  1. docker ps — port mapping
  2. Firewall: sudo ufw allow 18800
  3. docker logs xiajiao

Data gone after restart ​

Confirm mounts: docker inspect xiajiao → Mounts.

Permissions ​

bash
sudo chown -R 1000:1000 /opt/xiajiao-data

Docker Compose + Ollama (fully private) ​

yaml
# docker-compose.ollama.yml
services:
  xiajiao:
    build: .
    ports:
      - "18800:18800"
    volumes:
      - ./volumes/data:/app/data
      - ./volumes/uploads:/app/public/uploads
    environment:
      - OWNER_KEY=${OWNER_KEY:-admin}
    restart: unless-stopped
    depends_on:
      ollama:
        condition: service_healthy

  ollama:
    image: ollama/ollama:latest
    ports:
      - "11434:11434"
    volumes:
      - ollama-models:/root/.ollama
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
      interval: 10s
      timeout: 5s
      retries: 5
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

volumes:
  ollama-models:
bash
docker compose -f docker-compose.ollama.yml up -d

docker exec ollama ollama pull qwen2.5

# In Xiajiao (虾饺) UI:
# API Base: http://ollama:11434/v1
# API Key: ollama
# Model: qwen2.5

No GPU?

Remove deploy.resources; Ollama falls back to CPU (slower).

Logging ​

Structured log output ​

bash
# JSON logs (ELK / Loki friendly)
docker logs xiajiao 2>&1 | jq '.'

docker logs --since "2026-03-19T00:00:00" xiajiao

# Limit log file size (see docker-compose.yml below)

Limit log size in Compose:

yaml
services:
  xiajiao:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Production checklist ​

✅ OWNER_KEY changed (not admin)
✅ Volumes mounted (docker inspect)
✅ restart: unless-stopped
✅ Healthcheck configured
✅ Log rotation limits
✅ Firewall minimal exposure
✅ Public: Nginx + HTTPS
✅ Backups scheduled