Run with Docker

The compose file, the images on Docker Hub and GHCR, the encryption key and setup code printed on first boot, the six volumes, pointing the container at AI services on the host, host networking on Linux, an agent program inside the image, pinning a version, and running maintenance scripts inside the container.

Server

The image carries the built app, the , the and the , so a fresh container needs no network and no setup steps. Only the AI services stay outside it, on the host or elsewhere on your network.

Start

mkdir odm && cd odm
curl -O https://raw.githubusercontent.com/Lebbitheplow/open-dungeon-master/main/docker-compose.yml
docker compose pull
docker compose up -d        # http://localhost:3005

Open the address and create the first account, which becomes the administrator. Creating it takes the one-time the container prints in its log, in a banner that repeats at every start until that account exists, with a link that fills it in:

docker compose logs | grep -A12 "no accounts yet"

Open the link and the follows. Images: lebbi/open-dungeon-master on Docker Hub and ghcr.io/lebbitheplow/open-dungeon-master on GHCR, linux/amd64. A release tagged v0.26.2 publishes 0.26.2, 0.26 and, unless it is a pre-release, latest. On an arm64 host (a DGX Spark, an Ampere or Graviton box), build your own with docker compose up -d --build from a clone.

The encryption key

On first boot the container generates a database , stores it at data/.db-key in the data volume, and prints it once:

docker compose logs | head -20

Back that key up. The database is encrypted at rest with it and there is no recovery without it. To manage it yourself, put DB_ENCRYPTION_KEY=... in a .env file beside the compose file before the first start; an explicit key always wins over the stored one. Never change the key on an existing database.

Configuration

Copy .env.docker.example from the repository to .env beside the compose file, or write the variables you need by hand. The ones that matter first:

VariableEffect
ODM_PORTThe host port (default 3005).
ODM_IMAGE_TAGPin a version, for example 0.26.2.
ODM_IMAGESwitch , for example ghcr.io/lebbitheplow/open-dungeon-master.
OPENAI_COMPAT_BASE_URL, OPENAI_COMPAT_MODEL, OPENAI_COMPAT_API_KEYThe . See below for host addresses.
COMFYUI_URL, KOKORO_URL, TTS_PROVIDER, STT_URLImage, and speech-to-text services.
ODM_SETUP_CODEChoose the first account's yourself, for a scripted install.
VOICE_ENABLED, VOICE_RTC_PORT, VOICE_ANNOUNCED_IPVoice chat. See Voice chat setup.
APP_PUBLIC_URLThe address players use, when a sits in front.

Everything in the environment can also be set later in the , which wins over the environment. The full reference lists every variable.

Reaching AI services on the host

The container reaches the host through host.docker.internal, so on the host's 127.0.0.1:8001 is http://host.docker.internal:8001/v1 from inside. On Linux you can share the host's network stack instead, which makes the plain 127.0.0.1 defaults work as they do in a host install:

docker compose -f docker-compose.yml -f docker-compose.host-net.yml up -d

Host networking needs Compose v2.24 or newer and is not available on Desktop for Mac or Windows. ODM_PORT has no effect under it; the server listens on 3005 directly.

Volumes

Six hold everything worth keeping: odm-data (the database, the key, ), odm-uploads, odm-generated, odm-audio, odm-speech (the built-in speech recognition model, once downloaded) and odm-home (an 's sign-in). docker compose down leaves them alone; docker compose down -v destroys them. The is baked into the image at /app/content, outside the volumes, so a mount can never hide it.

Maintenance inside the container

Scripts must run through the entrypoint so the is loaded:

docker compose exec open-dungeon-master ./scripts/docker-entrypoint.sh \
  node scripts/make-admin.mjs <username>

The same shape runs the and restore scripts; the 's Backup tab does it without a shell.

Updating

docker compose pull && docker compose up -d

Volumes carry over and schema changes are applied on start. See Updating.

An agent program inside the image

A container cannot start programs on its host, so an storyteller has to live inside the image. Build it in with AGENT_PROGRAMS in .env, then sign in once:

# .env: AGENT_PROGRAMS=@openai/codex   (or @anthropic-ai/claude-code, opencode-ai)
docker compose up -d --build
docker compose exec open-dungeon-master codex login --device-auth

The sign-in lives in the odm-home volume and survives updates; the image carries the system certificate store the program needs. See Agent programs and connected agents. Every other backend works through a URL.

Health

The image declares a health check on /api/health (every 30 seconds, 45 seconds of grace at start). docker compose ps shows it; a container that never becomes healthy has its reason in docker compose logs, most often a missing or wrong key.

Checked against desktop and Android app 0.17.2 and server 0.26.2 on 2026-10-11. Something wrong or missing? Open an issue.

Download · v0.17.2
Server app · v0.26.2

Optional. Runs the table for a whole group on one machine that stays on. Everyone else just installs the app.

Free & open source · MIT licensed · served from GitHub releases