Curiosity

Deploying on Docker

Running Curiosity Workspace as a Docker container is the most common way to install it. The same image runs in local dev, in CI, and in production behind a reverse proxy.

This page covers single-host Docker deployments. For Kubernetes, see Kubernetes; for managed cloud variants, see AWS, Azure, GCP, and OpenShift.

Requirements

  • A 64-bit Linux, macOS, or Windows host with Docker installed. Follow the official Docker docs.
  • 8 GB of RAM available to Docker (16 GB+ for production with embeddings).
  • A persistent volume mount for MSK_GRAPH_STORAGE.
  • A network plan: bind to 127.0.0.1 for local dev, put a TLS-terminating proxy in front for shared deployments.

Pulling the image

The image is published as curiosityai/curiosity:

docker pull curiosityai/curiosity:latest

To pin to a specific release:

docker pull curiosityai/curiosity:v1.42.0
Pin in production

:latest will roll forward on every deploy. Pin to a versioned tag in production (and staging) so upgrades are explicit. See Upgrades and migrations.

Local development — single docker run

The simplest invocation, suitable only for a developer's own machine:

mkdir -p ~/curiosity/storage
docker run --name curiosity \
  -p 127.0.0.1:8080:8080 \
  -v ~/curiosity/storage:/data \
  -e MSK_GRAPH_STORAGE=/data/curiosity \
  -e MSK_ADMIN_PASSWORD="$(openssl rand -base64 24)" \
  curiosityai/curiosity:latest

Notes:

  • 127.0.0.1:8080:8080 binds the workspace to loopback only. Drop the IP prefix when you're ready to expose it on the host network.
  • MSK_ADMIN_PASSWORD replaces the default admin/admin. Read it back from your shell history or docker inspect.

On Windows, swap the volume syntax:

mkdir c:\curiosity\storage
docker run --name curiosity `
  -p 127.0.0.1:8080:8080 `
  -v c:/curiosity/storage:/data `
  -e MSK_GRAPH_STORAGE=/data/curiosity `
  -e MSK_ADMIN_PASSWORD=ChangeMeNow! `
  curiosityai/curiosity:latest

Docker on Windows expects forward slashes inside volume paths.

Staging / production — Docker Compose

For anything beyond local dev, use Docker Compose so the configuration is versioned and reproducible.

docker-compose.yml:

services:
  curiosity:
    image: curiosityai/curiosity:v1.42.0
    container_name: curiosity
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"   # exposed via the reverse proxy below
    volumes:
      - curiosity-data:/data
      - curiosity-backups:/backups
      - ./certs:/certs:ro
    environment:
      MSK_GRAPH_STORAGE: /data/curiosity
      MSK_GRAPH_BACKUP_FOLDER: /backups
      MSK_GRAPH_JOURNAL_FOLDER: /data/journal
      MSK_PUBLIC_ADDRESS: https://workspace.example.com
      MSK_USE_HSTS: "true"
      MSK_REDIRECT_TO_HTTPS: "true"
      # Secrets injected via env_file (kept out of git)
    env_file:
      - .env
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:8080/api/login/check", "-o", "/dev/null"]
      interval: 30s
      timeout: 5s
      retries: 5
      start_period: 60s

volumes:
  curiosity-data:
  curiosity-backups:

.env (kept out of source control — see your secret manager):

MSK_ADMIN_PASSWORD=<from secret manager>
MSK_ADMIN_EMAIL=ops@example.com
MSK_JWT_KEY=<32+ random bytes, from secret manager>
MSK_LICENSE=<your license token>
MSK_GRAPH_MASTER_KEY=<32+ random bytes, backed up separately>

Bring it up:

docker compose up -d
docker compose logs -f curiosity

Production checklist

A short list of must-haves before a Docker-based workspace serves real users:

  • Versioned image tag (curiosityai/curiosity:vX.Y.Z), not :latest.
  • Persistent volume for MSK_GRAPH_STORAGE on durable storage (SSD or better).
  • Backup volume for MSK_GRAPH_BACKUP_FOLDER, off-host on a schedule.
  • All secrets injected from a secret manager (never baked into the image or compose file).
  • TLS terminated at a reverse proxy or in-container (set MSK_CERT_FILE, MSK_CERT_FILE_PRIVATE_KEY, MSK_USE_HSTS=true, MSK_REDIRECT_TO_HTTPS=true).
  • MSK_PUBLIC_ADDRESS set so generated links use the right host.
  • Container memory/CPU limits sized for embedding workload (start at 16 GB / 8 vCPU).
  • Healthcheck enabled so the orchestrator can restart the container on hangs.
  • Log routing to your aggregator — either MSK_LOG_PATH on a mounted volume or the platform's stdout collector.
  • Backup/restore drill completed and documented.

See Production deployment for the broader checklist that also covers identity, monitoring, and disaster recovery.

Healthcheck details

The healthcheck above pings /api/login/check, which returns 401 (no token attached) once the workspace is accepting traffic and 5xx while it's still booting. Curl's -f flag treats 401 as success — that's intentional, because reaching the auth layer is exactly what we want to verify.

Upgrading

Pinning a version means upgrades are deliberate:

docker compose pull
docker compose up -d

For breaking changes, follow Upgrades and migrations: take a backup first, run on staging, validate, then promote.

Common pitfalls

  • Ephemeral storage — running without -v or with a tmpfs mount loses data on container restart. Always mount a real volume on MSK_GRAPH_STORAGE.
  • Mixed latest and pinned tags across environments leads to "works on staging" surprises. Be consistent.
  • Container time drift — embedding providers reject requests with skewed clocks. Run with the host's clock (default on Linux).
  • No MSK_PUBLIC_ADDRESS behind a proxy — generated links (SSO callback URLs, email links) point at the internal hostname instead of the user-facing one.
  • Insufficient memory — the workspace process maps indexes into memory. OOM-killed containers leave the indexes in a recovery state on the next boot. Size accordingly.

See also

© 2026 Curiosity. All rights reserved.
Powered by Neko