Docker container for hosting a dedicated server for The Lord of the Rings: Return to Moria. The game server is a Windows binary and runs under Wine.
Server files are downloaded via DepotDownloader using an anonymous Steam login — no Steam account or credentials required. The container also pulls the Steamworks Common Redistributables (AppID 228980) alongside the game (AppID 3349480) — Moria ships steamclient.dll/steamclient64.dll next to its binary and needs this depot present to run.
Initial container start → "data" volume is empty, new files are created in live "server" volume.
:Loop Container shutdown → Live data is copied to the "data" volume. Container start (subsequent launches) → Files in "data" volume are copied to live "server" volume (overwriting existing). Environment variables are then applied on top. Goto Loop
If you modify files in the "server" volume directly, they will be overwritten the next time the server starts.
.
├── data # Volume bind mount
│ ├── .wine # Wine prefix (initialized on first run, persistent)
│ ├── SaveGamesDedicated # Synced with server folder on container start / shutdown
│ └── MoriaServerConfig.ini # Synced with server folder on container start / shutdown
└── server # Volume bind mount, populated via DepotDownloader
├── Engine
├── Moria
│ ├── Binaries
│ │ └── Win64
│ │ └── MoriaServer-Win64-Shipping.exe
│ └── Saved
│ └── SaveGamesDedicated # Contents synced with data folder on start / shutdown
├── MoriaServerConfig.ini # Synced with data folder on start / shutdown
└── ...
docker-compose.yml file from the example below.docker compose up -d and monitor with docker compose logs -f.7777 (UDP) on your router.AdvertiseAddress=auto is the default and works for most setups behind NAT with the port forwarded).The Moria dedicated server binary (not this container) has its own first-run quirk: on a completely fresh world, the very first launch will log a line similar to:
LogOptions: Warning: Key 'None' doesn't exist in Action 'MenuToggleLast'.
When you see this, stop the container (docker compose down or docker stop moria) and start it again. On the second start, the game finishes creating its saved-data folders (including SaveGamesDedicated). At that point you can, if you wish:
data/MoriaServerConfig.ini directly for settings not covered by environment variables (do this while the container is stopped — see MoriaServerConfig.ini below).data/SaveGamesDedicated.Then start the container normally going forward.
MoriaServerConfig.ini lives in the data volume and is synced into the server volume on every start (and back out to the data volume on every shutdown), the same way saves are. It is patched with your environment variable settings (where set) on every container start.
Important: Do not manually edit
MoriaServerConfig.iniin the server volume — it will be overwritten on next start. Edit it in the data volume instead, then bring the container back up. Only edit it while the container is stopped, per the game's own guidance in the file ("Only edit this configuration when the server is offline").
Any settings not covered by the environment variables below can still be hand-edited in data/MoriaServerConfig.ini — the container only patches the specific keys listed in the table; everything else is left as the game wrote it.
If DepotDownloader gets into a bad state or you want a clean reinstall:
REMOVE_SERVER_FILES: "1" in your docker-compose.yml for one launch."0". Your saves and MoriaServerConfig.ini are preserved in the data volume and will be restored automatically.| Variable | Description | Default |
|---|---|---|
| TZ | Timezone | "UTC" |
| PUID | Numeric user ID | "1000" |
| PGID | Numeric group ID | "1000" |
| SKIP_UPDATE | Skip DepotDownloader validation on start (faster startup, no update check) | "0" |
| WORLD_NAME | Name of the saved world to load (created if it doesn't exist) | (existing value) |
| WORLD_SEED | World generation seed — only applies when creating a new world (random or an integer) | (existing value) |
| DIFFICULTY_PRESET | World difficulty preset for new worlds: story, solo, normal, hard, or custom | (existing value) |
| SERVER_PASSWORD | Join password | (existing value) |
| LISTEN_PORT | Port the server binds and listens on (adjust port mapping if changed) | (existing value, ships as 7777) |
| ADVERTISE_ADDRESS | Address reported to clients: auto (detect public IP), local (LAN), or a manual IP | (existing value, ships as auto) |
| SERVER_FPS | Server tick rate | (existing value, ships as 60) |
| LOADED_AREA_LIMIT | Maximum loaded areas at once (4–32); higher = more memory/CPU/bandwidth | (existing value, ships as 12) |
| REMOVE_SERVER_FILES | Wipe server files for a clean reinstall (set to "1" for one launch only) | "0" |
services:
moria:
image: rhavinx/moria:latest
container_name: moria
stop_grace_period: 30s
environment:
TZ: "UTC"
# PUID: "1000"
# PGID: "1000"
SKIP_UPDATE: "0"
WORLD_NAME: "My Moria Server"
WORLD_SEED: "random"
DIFFICULTY_PRESET: "normal"
SERVER_PASSWORD: "changeme"
LISTEN_PORT: "7777"
# ADVERTISE_ADDRESS: "auto"
# SERVER_FPS: "60"
# LOADED_AREA_LIMIT: "12"
# REMOVE_SERVER_FILES: "0"
volumes:
- /path/to/server:/home/steam/moria/server
- /path/to/data:/home/steam/moria/data
ports:
- "7777:7777/udp"
restart: unless-stopped
| Path (in container) | Purpose |
|---|---|
/home/steam/moria/server | Game binaries, downloaded/updated via DepotDownloader. Safe to delete/recreate (REMOVE_SERVER_FILES). |
/home/steam/moria/data | MoriaServerConfig.ini, SaveGamesDedicated, and the Wine prefix (.wine). This is your persistent data — back this up. |
| Port | Protocol | Purpose |
|---|---|---|
| 7777 | UDP | Game traffic. |
Content type
Image
Digest
sha256:59918fd9f…
Size
956.4 MB
Last updated
22 days ago
docker pull rhavinx/moria