Skip to content

Run with Docker

For the first installation, follow the Docker tab in Install Numi. This page covers an existing checkout with its .env, database, and runtime directories prepared.

Replace an application container while the database and file storage remain on the host.

Start, stop, and inspect

Run commands from the repository directory:

docker compose up -d
docker compose ps
docker compose logs -f numi-chat

Open http://localhost:4567. The container health check requests /health. To use a different host port:

NUMI_CHAT_PORT=8080 docker compose up -d

The container still listens on port 4567. Set NUMI_CHAT_PORT in .env if the mapping should persist across later commands.

Task Command
Stop the service docker compose stop numi-chat
Start a stopped service docker compose start numi-chat
Recreate after environment changes docker compose up -d
Rebuild after updating the checkout docker compose up --build -d
Apply migrations explicitly docker compose run --rm numi-chat db-migrate
Remove containers and their network docker compose down

Startup applies migrations automatically. Review Database migrations before upgrading an existing database.

Make aliases such as make docker-up, make docker-build, and make docker-logs are also available. The build/up Make targets restore web assets with uv on the host; the direct Compose commands above build those assets inside the image and do not require host uv.

Runtime data

The supplied Compose file uses these host paths:

Host path Contents
./chats.db SQLite database, mounted at /app/state/chats.db
./state/uploads/ Chat uploads
./state/generated_files/ Generated files, plots, and images
./state/models.json Complete private model catalog, if customized
./state/avatars/ User avatars
./state/deno-cache/ Deno cache
./logs/ Application logs

The database has its own file mount even though the complete ./state directory is also mounted. Back up the host's chats.db alongside state/; copying only state/ misses the database in this configuration. SQLite sidecar files such as chats.db-wal, when present, live in state/ and belong in the same backup.

Keep the complete ./state:/app/state directory mount. Admin catalog writes replace models.json atomically, which an individual file bind mount can prevent. Container recreation does not remove these host files.

The image runs as the user selected by HOST_UID and HOST_GID (both default to 1000). Set them to the owner of the bind-mount directories when needed.

The supplied Compose file includes two bind mounts for REWE credential files. If you are not using REWE, remove those two volume entries before starting the service. If you are using it, supply the files at the configured host paths; they are not included in the image. See Tools reference for integration setup.

Back up and update

  1. Stop the service with docker compose stop numi-chat.
  2. Copy chats.db, state/, and any logs you retain to a protected backup outside the checkout. Store .env and integration credentials securely as well; they are needed to restore the same configuration.
  3. Update the checkout to the version you intend to run, preserving your configuration and data.
  4. Run docker compose up --build -d, inspect the logs, and verify both an existing chat and a new response.

A database backup must match the runtime files from the same point in time. Do not replace a populated database with an empty file to fix startup errors.

Reverse proxy

For an internet-facing instance, terminate HTTPS at a reverse proxy and forward WebSocket connections. Bind the application port to loopback when the proxy runs on the same host by changing the Compose port mapping to:

ports:
  - "127.0.0.1:${NUMI_CHAT_PORT:-4567}:4567"

For Nginx, place this location inside your existing TLS server configuration with its real hostname and certificates:

location / {
    proxy_pass http://127.0.0.1:4567;
    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;
}

Set the environment for the public origin:

ENVIRONMENT=production
CORS_ALLOW_ORIGINS="https://chat.example.com"
TOOLS_CODE_INTERPRETER_UNSANDBOXED=false

If you changed the host port, update proxy_pass to match it.

Set FORWARDED_ALLOW_IPS to the trusted proxy address as seen by the container. Keep the backend inaccessible to untrusted clients. See Security for authentication and tool boundaries, and Privacy and data for the operator information shown to users.