Run with Docker
The image carries the built app, the content pack, the sound library and the embedding model, 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:3005Open the address and create the first account, which becomes the administrator. Creating it takes the one-time setup code 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 guided setup 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 encryption key, stores it at data/.db-key in the data volume, and prints it once:
docker compose logs | head -20Back 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:
| Variable | Effect |
|---|---|
ODM_PORT | The host port (default 3005). |
ODM_IMAGE_TAG | Pin a version, for example 0.26.2. |
ODM_IMAGE | Switch registry, for example ghcr.io/lebbitheplow/open-dungeon-master. |
OPENAI_COMPAT_BASE_URL, OPENAI_COMPAT_MODEL, OPENAI_COMPAT_API_KEY | The text backend. See below for host addresses. |
COMFYUI_URL, KOKORO_URL, TTS_PROVIDER, STT_URL | Image, narration and speech-to-text services. |
ODM_SETUP_CODE | Choose the first account's setup code yourself, for a scripted install. |
VOICE_ENABLED, VOICE_RTC_PORT, VOICE_ANNOUNCED_IP | Voice chat. See Voice chat setup. |
APP_PUBLIC_URL | The address players use, when a reverse proxy sits in front. |
Everything in the environment can also be set later in the admin panel, 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 llama.cpp 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 -dHost networking needs Compose v2.24 or newer and is not available on Docker Desktop for Mac or Windows. ODM_PORT has no effect under it; the server listens on 3005 directly.
Volumes
Six named volumes hold everything worth keeping: odm-data (the database, the key, world packs), odm-uploads, odm-generated, odm-audio, odm-speech (the built-in speech recognition model, once downloaded) and odm-home (an agent program's sign-in). docker compose down leaves them alone; docker compose down -v destroys them. The content pack 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 database key is loaded:
docker compose exec open-dungeon-master ./scripts/docker-entrypoint.sh \
node scripts/make-admin.mjs <username>The same shape runs the backup and restore scripts; the admin panel's Backup tab does it without a shell.
Updating
docker compose pull && docker compose up -dVolumes 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 agent program 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-authThe 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.