The README had grown to 388 lines of reference material and was reported as too difficult to follow. It is now a 72-line landing page - a working compose block, the game table, and an index - with the detail moved to whichever page owns it: docs/quickstart.md first server, start to finish docs/games.md per game: files, keys, config names, quirks docs/configuration.md environment variable reference docs/volumes-and-configs.md volume layout, config symlinks, logs docs/rcon.md passwords, rcon-cli, who may send RCON docs/iw4madmin.md sidecar setup, parsers, the whitelist trap docs/healthcheck.md what healthy means, restarts, autoheal docs/troubleshooting.md symptom-first FAQ Nothing was dropped; the length came out of duplication. The troubleshooting page is deliberately symptom-first, and every entry is a failure that actually happened - the T5 key that presents as a broken server, the CoD4x RCON password silently refused for being under 8 characters, the IW4MAdmin whitelist. Examples are split by scenario under examples/, replacing the single 228-line file: single-server, per-game (a block for all seven), multi-server, with-iw4madmin, and env.example for secrets. EXAMPLE-docker-compose.yml stays as a signpost because forum posts link to it. The IW4MAdmin example and docs now mount the log FILE rather than the app directory. Pointing IW4MAdmin at Plutainer's logs/ symlink means it silently ingests nothing: it decides whether to read by comparing the file's size, and .NET reports a symlink's size as the length of the link text, so the log never appears to grow. Mounting the file makes Docker resolve the symlink at mount time. Verified across all eleven servers. docs/ and examples/ are excluded from the build context.
7.7 KiB
IW4MAdmin
Running IW4MAdmin alongside Plutainer servers.
Verified against eleven servers at once — every supported game, MP and ZM — all attached.
What IW4MAdmin needs from each server
- An RCON password. Set
PLUTAINER_RCON_PASSWORD(RCON). - The game log, so it can read events — and it must be mounted as a file, not reached through a symlink. See the symlink trap below; getting this wrong costs you every in-game event with no error message.
- Network reach to the server's port.
Compose
services:
t6zm-1:
image: ghcr.io/ayymoss/plutainer:latest
container_name: t6zm-1
restart: unless-stopped
ports: ["4976:4976/udp"]
volumes:
- /opt/game-files/T6ServerFiles:/home/plutainer/gamefiles:ro
- ./t6zm-1:/home/plutainer/app
environment:
PLUTAINER_GAME: t6zm
PLUTAINER_CONFIG_FILE: dedicated_zm.cfg
PLUTAINER_RCON_PASSWORD: ${T6ZM_RCON}
PLUTO_SERVER_KEY: ${T6ZM_KEY}
networks: [games-net]
iw4madmin:
image: ghcr.io/raidmax/iw4madmin:latest
container_name: iw4madmin
restart: unless-stopped
ports: ["1624:1624"]
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/London
volumes:
- ./IW4MAdmin/Configuration:/app/Configuration
- ./IW4MAdmin/Plugins:/app/Plugins
- ./IW4MAdmin/Localization:/app/Localization
- ./IW4MAdmin/Database:/app/Database
- ./IW4MAdmin/Log:/app/Log
# Mount the LOG FILE itself, not the directory. Docker resolves the
# symlink on the host at mount time, so the container sees a real file.
- ./t6zm-1/logs/games_zm.log:/gamelogs/t6zm-1/logs/games_zm.log:ro
depends_on:
t6zm-1:
condition: service_healthy # wait for the server, not just the container
networks: [iw4m-net]
networks:
games-net:
driver: bridge
iw4m-net:
driver: bridge
depends_on: service_healthy matters more than it looks: IW4x spends its first few minutes downloading 1–2 GB, and IW4MAdmin aborts startup if a configured server doesn't answer. Without it, a fresh deployment can come up with IW4MAdmin dead.
A full working example is in examples/with-iw4madmin.yml.
Server entries
In IW4MAdmin/Configuration/IW4MAdminSettings.json. Address the servers by your host IP and published port:
{
"Servers": [
{
"IPAddress": "10.0.0.5",
"Port": 4976,
"Password": "your-rcon-password",
"ManualLogPath": "/gamelogs/t6zm-1/logs/games_zm.log",
"RConParserVersion": "Plutonium T6 Parser (2024)",
"EventParserVersion": "Plutonium T6 Parser (2024)"
}
]
}
ManualLogPath matches the mount path above. Because the mount was taken through logs/, Docker resolved Plutainer's stable symlink at mount time and IW4MAdmin sees a plain file — which is what it needs.
Parsers and log names
| Game | Parser | Log file |
|---|---|---|
| T4 MP | Plutonium T4 MP Parser |
games_mp.log |
T4 ZM (t4sp) |
Plutonium T4 CO-OP/Zombies Parser |
games_zm.log |
| T5 MP | Plutonium T5 Parser |
games_mp.log |
T5 ZM (t5sp) |
Plutonium T5 Parser |
games_sp.log |
| T6 MP / ZM | Plutonium T6 Parser (2024) |
games_mp.log / games_zm.log |
| IW5 | Plutonium IW5 Parser |
games_mp.log |
| IW4x | IW4x Parser |
games_mp.log |
| T7x | BOIII Parser |
games_mp.log / games_zm.log |
| CoD4x | CoD4x Parser |
games_mp.log |
IW4MAdmin needs a pre-existing IW4MAdminSettings.json before its container will start — it won't generate one unattended.
The symlink trap: no events at all
If IW4MAdmin connects, shows the server online and responds to RCON, but never sees chat, joins, or in-game !commands, this is almost certainly why.
IW4MAdmin decides whether to read by watching the log file's size:
var fileSize = _reader.Length; // new FileInfo(path).Length
var fileDiff = fileSize - _previousFileSize;
if (fileDiff < 1 ...) return; // nothing new, don't read
On Linux, .NET's FileInfo.Length for a symlink returns the length of the link text, not of the target. Plutainer's app/logs/games_mp.log is a symlink, so its "size" is a constant 38 bytes no matter how much the real log grows. The difference is never positive, so IW4MAdmin never reads a single line — and logs no error, because nothing failed.
Measured directly: with ManualLogPath pointing at the symlink, a real map change grew the log from 191 to 861 bytes and IW4MAdmin logged nothing. Repointing it at the real file and repeating the map change produced New map loaded within two seconds.
Two ways to get it right:
- Mount the log file (what the examples do). Docker resolves the host-side symlink when it creates the mount, so the container sees a regular file and you still get to name it via the stable
logs/path. - Point at the real path under
runtime/, e.g./gamelogs/t6zm-1/runtime/plutonium/storage/t6/main/logs/games_zm.log. Works, but the path differs per game and moves when you change mods.
One caveat with the file mount: the target must exist when the container starts, or Docker creates a directory in its place. Start the game server first — depends_on: condition: service_healthy handles both this and the startup race.
The whitelist rule that catches everyone
If IW4MAdmin attaches to your other games but T5 and T6 fail with:
Not monitoring server due to uncorrectable errors [10.0.0.5:4976]
NetworkException: Reached maximum retry attempts to send RCon data to server
…here's what's happening. T5 and T6 gate unauthenticated queries — getinfo and getstatus — on the RCON whitelist, and IW4MAdmin's T5/T6 parsers open the connection with getinfo. So the RCON handshake succeeds and the connection still fails:
getinfo -> (no reply) ← fails here
rcon <pw> version -> "Plutonium T6 …" ← RCON is fine
rcon <pw> sv_running -> "1"
getinfo ×6 -> (no reply) ← gives up
Measured on T6, from off-loopback:
| whitelist state | RCON | getinfo |
|---|---|---|
| upstream's placeholder IPs | blocked | blocked |
| empty | works | blocked |
| gateway whitelisted | works | works |
Note the middle row: an empty whitelist is not permissive for these queries, even though it is for RCON commands — which is the opposite of what the comment in the stock config implies.
Plutainer handles this automatically. The address a sidecar appears to come from is the server container's Docker bridge gateway, assigned at run time and therefore impossible to bake into a config file — so Plutainer detects it at startup and passes +rconWhitelistAdd <gateway> as a launch argument. Your config is not modified.
You only need to intervene if your admin tool is on another machine:
environment:
PLUTAINER_RCON_WHITELIST: "10.10.1.20"
PLUTAINER_RCON_WHITELIST_GATEWAY=false disables the automatic entry. Note that any whitelist entry makes the whitelist non-empty, restricting T5/T6 RCON to the listed addresses plus loopback — the posture upstream's placeholder entries intended.
T4, IW5, IW4x, T7x and CoD4x answer queries regardless and get no whitelist entries.
Networking
Running IW4MAdmin on a separate bridge network from the game servers is the tested arrangement, and what the examples use. Traffic then arrives at the game from the game's own gateway, which is the address Plutainer whitelists.
Running IW4MAdmin bare-metal on the host, or on a different machine entirely, also works — in that case whitelist its address with PLUTAINER_RCON_WHITELIST for T5/T6.