A powerful Discord bot and web interface to manage Docker containers remotely. This application bridges the gap between Discord and your Docker environment, allowing container monitoring and control directly through Discord channels.
Control your Docker containers directly from Discord! This application provides a Discord bot and a web interface to manage Docker containers (start, stop, restart, view status) with a focus on stability, security, and performance. The default image is an ultra-optimized Alpine Linux build with the latest security patches and enhanced performance.
β β v3.1.1 (2026-10-10) - Empty servers stop, and a cleaner image
Updating from v3.1.0 is a plain image update.
A game server nobody plays on can stop itself: a new watchdog rule "a game server nobody
plays on" stops it (or notifies) after the minutes you set, 30 by default. It saves RAM and CPU
on servers that run empty all night.
The overview shows a join at once, in the same minute as the join notice, not at the next
update interval.
The live-log panel, gone over: up to 4000 characters of readable log lines in your time
zone, a π₯ button that sends the last 5000 lines as a file, and the panel goes after its timeout
instead of standing dead.
No task for a date that does not exist: Discord no longer offers 31 February.
"Validate key" checks by the server's list, the same one the save uses, and no key list
sits in the browser any more.
A quieter log: a minute without a problem leaves no line at INFO.
No known vulnerability in the image (zlib and expat updated).
β β v3.1.0 (2026-10-05) - Only when nobody plays, and who is playing
Updating from v3.0 is a plain image update.
A restart or stop can wait for an empty server: "restart daily at 4, but only once nobody
plays, at the latest at 6", with a warning to the players before it, in scheduled tasks and
auto-actions alike. DDC wakes up for the warning time the admin set; if it could not warn in
time, it warns and waits that long before acting.
Every container has an info display, with who is playing on a game server, its version,
uptime, restarts and health, and whether a newer image is in the registry. Player joins can be
announced: you choose the channels, and per container which game servers.
A dead game server is not shown green: a container that runs but whose game does not answer
is drawn β οΈ.
The web panel asks Docker once instead of once per container, and only while it is used:
on a host with 37 containers, 2 requests instead of 38 every 30 seconds, and none at all once
nobody has looked for five minutes.
One beat for everything that asks Docker: one loop fetches once per beat and feeds the
overviews, player counts, join notices and the watchdog from it, instead of a second loop that
fetched on its own. "Every minute" means every minute again.
Every message but the status and control overviews has a lifetime, kept over a restart:
auto-action notices 1 hour, a join notice 30 minutes, a donation's thank-you 24 hours, an update
notice 7 days. The overview stays where it is while only such notices stand below it, and moves
to the bottom when someone else writes. Private panels close after at most ten minutes unused
and are deleted reliably, also when DDC restarts.
A file that cannot be read is not written over: an unreadable admins.json or config.json is
reported and left alone instead of being replaced by the next save.
A review before the release fixed well over a hundred smaller defects, each with its own
test. 8,378 tests pass.
GitHub's code scanner checked the v3.0 code when it reached main. Two of its findings were
real and are fixed; the others were checked by hand and are false positives. From v3.0.0 it is a
plain image update.
A login link can no longer send the browser to another host. A crafted next parameter
with a tab character slipped past the check and, once the browser removed the tab, pointed
to a foreign site right after login.
The group list no longer shows a raw error text that could name a file path on the host.
The image carries the current Alpine security fixes (expat 2.8.5, CVE-2026-93990): the
build no longer takes the package layer from an old cache.
β β v3.0.0 (2026-09-27) - Security, Watchdog & Groups
The biggest DDC release so far. DDC can now only reach the Docker endpoints it needs, watches
your containers and tells you when one dies, works with container groups, and has a web panel
you can protect with two-factor authentication.
Updating on Unraid: just update. Settings, containers, channels, tasks, auto-action rules,
translation pairs, the mech, the bot token and your password carry over (checked by replaying an
update from v2.4.1 on Unraid). You may have to log in once more - the panel has a login page now.
Behind a reverse proxy, set DDC_TRUSTED_PROXIES. Going back to v2.4.1 is not supported.
Allowlist proxy in front of the Docker socket: only list, inspect, logs, stats, start, stop
and restart get through; create, exec and everything else are refused.
Container watchdog: react when a container stops, turns unhealthy, restarts repeatedly,
stays above a CPU or memory threshold, or has a newer image - with maintenance pauses and
alarms to ntfy, Gotify or any webhook when Discord is down.
Container groups in scheduled tasks, auto-action rules and the Discord admin list.
Two-factor authentication and HTTPS for the web panel (offered, never forced).
Backup & restore of the whole configuration as one file, with a preview.
Channel translation of followed announcement channels, explained in the panel.
The mech's energy is a battery, a reworked panel with five tabs, and all 40 languages complete.
GitHub's code scanner flagged ten places after v2.4.0. Four were real and are fixed, six were
false positives. No migration and no logout: from v2.4.0 it is a plain image update.
Two Web UI error answers no longer pass raw error text (which can contain host paths) to the
browser; the details go to the log.
The first-time setup page shows its messages as text instead of HTML.
The image build workflow limits its GitHub token to read-only by default.
Coming from v2.3.1 or earlier? Read the v2.4.0 upgrade notes below first.
A complete audit of DDC, followed by a second review pass focused on what changes for existing
installations on upgrade. 188 findings fixed and a suite of 5,715 tests. Every source
file in the project carries written evidence that it was read: 184 of 184.
β οΈ Please read the upgrade notesβ before updating. You
will be logged out once, long-dead scheduled tasks are paused instead of being resurrected, and
your bot token stays in plaintext until you encrypt it.
Fixed, among others
One container can no longer make all your servers disappear. A container whose image had
been removed from the host emptied the whole container list.
Admin users can be saved again. Every save failed with "Failed to save admin users".
Scheduled tasks run the way you set them up. Weekly tasks were dropped, cron tasks
silently switched off, and one missed run could stop a recurring task for good.
The bot stays responsive. Docker calls no longer block it, which caused repeated container
timeouts and the flood of "SLOW batched processing" warnings.
Every button answers. A failure now gets a message instead of an endless "thinkingβ¦".
Auto-actions with several containers work. They locked themselves out and never ran.
"Change password" changes the password. It used to do nothing and stored the new password
in plaintext.
π Security
The Encrypt token button really encrypts the token; before, it reported success and changed
nothing. If you pressed it on an earlier version, press it again (see the upgrade notes).
CSRF protection on every route; rejected requests get a clear message.
Session cookie renamed to ddc_session with SameSite=Lax; new passwords require 12 characters.
Auto-action regex patterns are validated, and each search runs in a separate process with a
hard 0.5 s budget, so a catastrophic pattern can no longer freeze the bot.
Bumped cryptography β 50.0.0 to resolve CVE-2026-69247 (GHSA-g6cj-pr64-35w5): a Bleichenbacher timing/error oracle in PKCS#7 EnvelopedData decryption (High severity).
No functional changes β dependency-only patch release across all platform images.
β β v2.3.0 (2026-07-01) - Live Game-Server Player Counts
Show live player counts for your game servers right in the Discord status overview (e.g. Valheim 3/8).
New: per-container player-count feature
Live "players online / max" for game servers via the opengsqβ library β shown compactly behind the server name in the community status overview (Name 3/8) and in the per-container control box (β Players: 3/8).
4 protocols: Source / A2S (most Steam games β Valheim, Enshrouded, V Rising, Icarus, Rust, CS, Project Zomboid, β¦), Minecraft, Satisfactory (app token) and Palworld (REST admin password).
Automatic detection: DDC probes each online container across all its published ports (transport-aware ordering), auto-detects Source vs Minecraft, and unlocks the per-server "Players" toggle only for servers that actually answer β with a 15-minute probe window, permanent verdicts, and a manual "Test now" re-check for slow-booting servers.
Guided setup: the container name/image suggests the right protocol, and token games (Satisfactory / Palworld) make the Info button glow until a credential is entered.
Fully non-blocking & best-effort β a dead or slow game server never affects or delays the Discord status loop.
Localized in all 40 supported languages.
π Quality: built and hardened across three independent multi-agent code reviews; +102 unit tests; validated live against real game servers.
gevent bumped 25.5.1 β 25.9.1 for Python 3.14 wheel compatibility
Validated with a full soak test against a live Discord server before release: bot connects, fetches containers, posts overviews, all v2.2.3 fixes work β zero runtime errors on 3.14
Note: the Alpine busybox advisory CVE-2025-60876 (Medium) has no upstream fix in any branch yet and remains until Alpine ships a patched package β apk upgrade (already in the Dockerfile) will pick it up automatically on a future rebuild.
Long-lived overviews (edited in place) older than 30 days were skipped by the age-limited cleanup, so a regeneration could post a fresh overview on top of the stale one β now the tracked overview is deleted by its known message ID first
Concurrent posters (inactivity regeneration, event recreate, recovery, /ss, /control, initial send, channel hot-reload/teardown) are serialized by a per-channel lock with re-validation, so two paths can never produce a duplicate
Overview message IDs are persisted and restored on restart (atomic mech_state.json writes), so a restart deletes the previous overview by ID instead of orphaning it
β¨ Overview re-posts below the bot's own messages
The "move overview to the bottom" feature now also triggers when the bot's own notifications (restart/update-watcher triggers) bury the overview β previously only foreign messages counted. The managed overview is never treated as foreign, so in-place editing is untouched
Regeneration is skipped while a user is mid-interaction (e.g. expanding the mech)
π Dependencies
cryptography>=48.0.1 (GHSA-537c-gmf6-5ccf β vulnerable OpenSSL bundled in wheels < 48.0.1)
Upgrade note: on the first restart after this version, mech_state.json does not yet contain the new channel_overview_message_ids key, so a pre-existing >30-day overview may survive that first regeneration once. It is removed automatically on the next cycle (self-healing); thereafter the persisted ID prevents duplicates across restarts.
β β v2.1.9 (2026-03-21) - Critical Channel Save Fix
π Critical Bug Fix:
Jinja2 Scoping Bug (Root Cause of Issue #14): Fixed {% set %} variable inside {% for %} loop not surviving outer scope in Jinja2 templates. This caused the empty default channel row to ALWAYS render alongside existing channels, creating duplicate form fields. When saved, Flask received lists instead of strings for channel IDs, corrupting the data and causing all existing channels to be deleted. Fixed using Jinja2 namespace() objects.
π Additional Bug Fixes:
Channel Delete Button: Fixed delete button not working for existing channel rows in the web UI. The click handler was only attached to dynamically added rows, not template rendered ones. Fixed via event delegation.
π Improvements:
Port Diagnostics: Cleaned up false "PORT ISSUES DETECTED" warnings when port mapping detection is unavailable from inside the container. Now only warns when the port is genuinely not listening.
β β v2.1.6 (2026-03-08) - Bugfixes & Story Service
π Bug Fixes:
Donation Restore: Fixed toggle-count pattern across all three layers (delete, rebuild, list/stats) β restoring deleted donations now works correctly
Mech Story Reading: Added missing get_all_chapters() and get_chapter_key_for_level() methods to MechStoryService β "Geschichte lesen" button no longer crashes