Run it in Docker

The Orchestrator runs as a container of curiosityai/orchestrator (:latest, or :<version>) for linux/amd64 and linux/arm64. It does not run Docker itself. It drives the host's Docker through the socket you mount into it, so the workspaces are containers next to it on the host, not inside it. They keep running when the Orchestrator's container is stopped, upgraded or removed.

Run it

docker run -d --name curiosity-orchestrator --restart unless-stopped \
  -p 443:443 -p 80:8080 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v curiosity-orchestrator-data:/data \
  -e ORC_ADMIN_PASSWORD='choose-a-password' \
  -e ORC_CERT_SELF_SIGNED=true -e ORC_PORT=443 -e ORC_PORT_LOCAL=8080 -e ORC_REDIRECT_TO_HTTPS=true \
  -e ORC_PUBLIC_ADDRESS=https://workspaces.example.com \
  --stop-timeout 60 \
  curiosityai/orchestrator:latest

This serves HTTPS with a self-signed certificate and redirects plain HTTP to it. For a first look over plain HTTP, publish -p 8080:8080 and set only ORC_ADMIN_PASSWORD, then open http://localhost:8080/#/manage.

What each part does

Part Why
-v /var/run/docker.sock:/var/run/docker.sock Gives the Orchestrator access to the host's Docker. Without it, the console's overview says it cannot reach the Docker daemon.
-v curiosity-orchestrator-data:/data The Orchestrator's state: its RocksDB database and certificates. Without a volume it forgets its workspaces when the container is re-created.
--stop-timeout 60 The image runs the Orchestrator under tini, so a stop is a clean shutdown. 60 seconds is time to finish what it was doing.
ORC_PUBLIC_ADDRESS The URL visitors use. It is named in the self-signed certificate.
The Docker socket is root on the host

Access to /var/run/docker.sock is equivalent to root on the host. Treat the Orchestrator's container the same way.

Other Docker setups

  • Rootless Docker: mount $XDG_RUNTIME_DIR/docker.sock instead.
  • Docker over TCP: set ORC_DOCKER_ENDPOINT=tcp://host:2375 and mount nothing.

How it reaches the workspaces

Inside a container, the Orchestrator reaches each workspace by container name (ORC_CONTAINER_ACCESS=network, set in the image). At start it joins its own container to the workspaces' network (ORC_DOCKER_NETWORK, created if missing), so only the Orchestrator's own ports are published.

If joining fails, the log says so. Connect it by hand:

docker network connect curiosity-orchestrator curiosity-orchestrator

The workspaces' containers and volumes are named ORC_CONTAINER_PREFIX (curiosity-) plus their address. Do not run two Orchestrators against one Docker daemon with the same prefix and network.

Docker Compose

services:
  orchestrator:
    image: curiosityai/orchestrator:latest
    restart: unless-stopped
    environment:
      - ORC_ADMIN_PASSWORD=${ORC_ADMIN_PASSWORD:?Set ORC_ADMIN_PASSWORD}
      - ORC_PUBLIC_ADDRESS=${ORC_PUBLIC_ADDRESS:-https://localhost}
      - ORC_CERT_SELF_SIGNED=true
      - ORC_PORT=443
      - ORC_PORT_LOCAL=8080
      - ORC_REDIRECT_TO_HTTPS=true
    ports:
      - "443:443"
      - "80:8080"
    volumes:
      - orchestrator-data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    stop_grace_period: 1m

volumes:
  orchestrator-data:
ORC_ADMIN_PASSWORD='choose-a-password' docker compose up -d

For certificates from Let's Encrypt, prepare DNS and the firewall as in Run it on a server, then replace the environment section:

    environment:
      - ORC_ADMIN_PASSWORD=${ORC_ADMIN_PASSWORD:?Set ORC_ADMIN_PASSWORD}
      - ORC_PUBLIC_ADDRESS=https://workspaces.example.com
      - ORC_PORT=443
      - ORC_PORT_HTTP=8080              # the port published as 80: Let's Encrypt, and the redirect to HTTPS
      - ORC_LETSENCRYPT=true
      - ORC_LETSENCRYPT_EMAIL=ops@example.com
      - ORC_LETSENCRYPT_ACCEPT_TERMS=true
      - ORC_LETSENCRYPT_STAGING=true    # remove once it works

To switch from staging to real certificates, remove the staging line and delete the staging certificates from the data volume:

docker compose stop
docker compose run --rm --entrypoint rm orchestrator -rf /data/letsencrypt
docker compose up -d

Upgrade

docker pull curiosityai/orchestrator:latest

Then remove and re-run the container with the same volume. Workspaces that were running keep running, and the new Orchestrator picks them up.

Referenced by