Skip to main content
This guide sets up Comis in a Docker container. You do not need Node.js installed on your system — Docker handles everything.
You don’t need to understand the technical details to use this feature. The configuration examples below are copy-paste ready.
You need Docker Engine 24+ and Docker Compose v2+ installed. Check with:
Production = Linux host only. macOS and Windows Docker Desktop run containers inside a linuxkit VM whose kernel does not allow nested PID-namespace + procfs mounts. This breaks the bubblewrap exec sandbox.On Docker Desktop the daemon detects this at startup and automatically disables the exec sandbox so the agent is functional for development. The trade-off:
  • Dev/testing UX on Mac and Windows: the agent runs end-to-end. You can drive exec commands, run scripts in the workspace, and exercise every feature that depends on shell execution.
  • The documented exec-isolation boundary is absent on Docker Desktop. With the sandbox off, agent-issued shell commands may read /home/comis/.comis/.env, the encrypted secrets.db, /etc/comis/config.yaml, channel tokens, the SECRETS_MASTER_KEY, and anything else the daemon user owns. Treat prompt-driven shell execution as host-equivalent authority within the container account.
This is fine for development and local testing only. For production, deploy to a real Linux host (bare metal, VPS, or a Linux cloud VM) and verify that bubblewrap is active after startup. Some Linux hosts restrict unprivileged user namespaces through AppArmor and require the host configuration described in Operations: Docker → Per-command exec sandbox.See Operations: Docker → Platform Support for the full explanation, including local Linux-VM alternatives (Lima / Colima vz driver / OrbStack / UTM) that get you a working sandbox on a Mac.

Automated setup

The repository includes a setup script that creates directories, generates a gateway token, builds the Docker image, and starts the daemon. This is the recommended way to get started.
1

Clone the repository

2

Configure Compose and credentials

Copy the included Compose settings template:
Keep persistent credentials in the mounted daemon environment file (the default COMIS_ENV_FILE is ~/.comis/.env):
Add the credentials required by your chosen provider, for example ANTHROPIC_API_KEY=... or OPENAI_API_KEY=.... OAuth and local keyless providers do not need a static provider key. If you change COMIS_ENV_FILE in the project-root .env, edit that target instead.
Never store API keys, tokens, or passwords directly in config.yaml. Use the .env file or Secret Manager for credential management.
.env.docker.example contains image, path, and network settings plus a small set of optional explicit credential overrides. Blank overrides are removed by the container entrypoint, so they cannot hide non-empty values from COMIS_ENV_FILE.
3

Run the setup script

The script performs these steps automatically:
  1. Creates the data and config directories (both ~/.comis by default)
  2. Generates a gateway authentication token (or reuses an existing one)
  3. Writes token and network settings to COMIS_ENV_FILE
  4. Creates a schema-valid config.yaml with authenticated gateway, logging, and memory paths
  5. Builds the Docker image from the included Dockerfile
  6. Prepares data and config ownership for the container’s non-root user (UID 1000)
  7. Starts the comis-daemon service and waits for its health check to pass
The script prints the gateway URL and credential-file path only after the daemon is healthy; it does not echo the token. On timeout it exits nonzero and prints the logs command to run.
4

Verify

Confirm the daemon is healthy:
Expected response:

Browser tool (optional)

The published image is browser-free. To use the agent browser tool, rebuild from the repo with one of three build args mirroring the bare-VPS install flags. Image-size impact in parentheses.
The entrypoint shim (/usr/local/bin/comis-entrypoint.sh) starts Xvfb on :99 automatically when the image was built with COMIS_WITH_XVFB=1. It verifies the X11 socket before starting the daemon and exits if the virtual display cannot start. The daemon’s findChrome() probes ~/.cloakbrowser/chromium-*/chrome first, so when the cloak binary is baked in it wins over stock Chrome with no config change. Run the CloakBrowser image identically to the no-browser one — same volumes, same ports, same environment variables:

Alternative: installer-based image

The repo also ships Dockerfile.install. It packs the local workspace and runs the installer’s non-interactive, non-root package-install path inside Ubuntu 24.04. This exercises package installation and selected browser-provisioning branches, but skips interactive setup, initialization, service management, dedicated-user creation, and the public-registry download path.
The main Dockerfile is the primary multi-stage image path. Dockerfile.install is a focused installer validation image, not a parity image for a managed host.
CloakBrowser does not include a proxy, change IP reputation, or guarantee access to any site. Review the target site’s terms and the binary license before enabling it.
See agent-tools/browser for the browser-tool security model.

Optional services

The daemon is the only service that starts by default. The web dashboard and CLI are available as Docker Compose profiles.

Web dashboard

Start the Nginx-served web UI on port 8080:
Then open http://localhost:8080 in your browser. The dashboard proxies API calls and WebSocket connections to the daemon automatically.

CLI

Run one-shot CLI commands against the running daemon:
The CLI container shares the daemon’s network namespace, so it reaches the gateway at 127.0.0.1:4766 without extra configuration.

Stopping and updating

Deploying on a VPS

The supplied Compose file and quick-start examples bind the host port to 127.0.0.1 by default. If you omit the host address (-p 4766:4766) or set the host-side bind to 0.0.0.0, a VPS may expose the gateway to the public internet. Choose an intentional posture below before changing the loopback default.
The wizard’s “LAN mode” sets gateway.host: 0.0.0.0 inside the container. That’s correct — the daemon must bind all interfaces so Docker port-forwarding can reach it. The exposure decision is made on the host side by the -p mapping (or Compose ports:). Confusing the two is the #1 way to accidentally expose a daemon publicly.
Bind the host port to loopback only and put nginx (or Caddy/Traefik) in front to terminate TLS:
Or in docker-compose.yml:
Then point your reverse proxy at http://127.0.0.1:4766 (HTTP) and http://127.0.0.1:4766/ws (WebSocket). See Operations: Reverse Proxy for full nginx / Caddy snippets with WebSocket upgrade headers.

Acceptable: direct exposure + token auth + firewall

If you don’t want a reverse proxy, you can expose the gateway directly, but you must combine three controls:
  1. Token auth on the gateway. The init wizard enables this by default (Authentication method → Token). Verify your ~/.comis/config.yaml contains a gateway.tokens: section and ~/.comis/.env has a COMIS_GATEWAY_TOKEN.
  2. Firewall to trusted IPs only. On Ubuntu/Debian:
  3. TLS. Without TLS, the bearer token crosses the wire in cleartext. Either run with gateway.tls.cert configured (see HTTP gateway: mTLS or front it with a reverse proxy that does TLS for you (in which case use the recommended posture above instead).

comis status from your laptop against a remote VPS

The CLI’s comis status defaults to reading ~/.comis/config.yaml on the machine where it runs. Copying the VPS’s config to your laptop won’t work on its own — gateway.host: 0.0.0.0 is a bind address that the CLI remaps to local loopback, not the VPS’s public IP. Set COMIS_GATEWAY_URL (and the matching token) on your laptop instead:
Use wss:// (TLS) when the gateway is reachable over the public internet. Use ws:// only over a trusted network (VPN, SSH tunnel).

Common issues

Common causes:
  • Docker not running — start Docker Desktop or the Docker daemon
  • Port 4766 in use — stop the other process or set COMIS_GATEWAY_PORT in .env
  • Missing openssl — the script uses openssl rand -hex 32 for token generation; install it with your system package manager
Symptom: curl http://127.0.0.1:4766/health returns “connection refused” or “connection reset by peer” even though the container is reported healthy.Fix: Two distinct binds are involved — get them in the right place:
  1. Host-side bind (which interface the host listens on): controlled by COMIS_GATEWAY_HOST in the project-root Compose .env. The default is 127.0.0.1; use 0.0.0.0 only when you intentionally want LAN exposure and have configured authentication and transport security.
  2. Container-side bind (which interface the daemon listens on inside the container): the base Compose file sets this to 0.0.0.0 so port forwarding can reach the daemon. A mounted config.yaml has higher precedence; if it sets gateway.host: 127.0.0.1, change it to 0.0.0.0 or remove the field.
Other things to check:
  • The container is running and healthy: docker compose ps
  • No firewall is blocking the port
  • Confirm no additional Compose file intentionally replaces the loopback host-port mapping.
Symptom: The container starts but stops within seconds.Fix: Check the logs for the error:
Common causes:
  • Invalid config.yaml — check YAML syntax and required fields. The daemon writes a recovery snapshot at ~/.comis/config.last-good.yaml; run cp ~/.comis/config.last-good.yaml ~/.comis/config.yaml to restore the previous-known-good config, then docker compose restart.
  • Missing API key — verify COMIS_ENV_FILE (default ~/.comis/.env) has the correct variable name
  • Port already in use — another process is using port 4766 on the host
Symptom: Bringing up Compose without first running docker-setup.sh, the daemon fails to persist credentials and you see cannot create /home/comis/.comis/.env: Is a directory in the logs.Cause: Docker bind-mounts ${COMIS_ENV_FILE:-~/.comis/.env} into the container. If the host path doesn’t exist, Docker silently creates it as a directory instead of a file, then every write fails.Fix: create the file before docker compose up:
If .env already exists as a directory, remove it first: rmdir ~/.comis/.env && touch ~/.comis/.env.The repo’s docker-setup.sh handles this automatically — this only affects manual docker compose up.

Next steps

Configuration Guide

Customize your agent settings, add channels, and configure advanced options.

Verify Installation

Run diagnostic commands to confirm everything is working.

Operations: Docker

Docker deployment boundaries, multi-stage builds, and security hardening.