Web-based tool that checks your Docker containers for outdated images by comparing local digests against remote registry digests (therefore no unneccessry image pulls). Update notifications using Telegram. Supports one-click container updates via a "Clone & Swap" pattern.
Responsive design, light/dark themes
- No image pulls — uses Docker Registry v2 API to fetch manifests and compare digests
- One-click updates — "Clone & Swap" pattern: stop → rename → pull → create → start → remove old
- Resilient updates — pull retries with backoff, automatic rollback, and a retry queue for updates started in bulk
- Anonymous auth where possible (Docker Hub, ghcr.io, gcr.io, quay.io, ECR Public)
- Digest caching — same image referenced by multiple containers is only checked once per run
- Real-time progress via Server-Sent Events (SSE)
- Includes stopped containers — always checks all containers, not just running ones
- Persistent results — saved to disk and restored on page load
- Auto-check scheduler — adapts interval based on Docker Hub rate limits
- Clickable stat cards — filter by Up to date / Outdated / Unknown / Total
- Responsive table — columns collapse progressively on smaller screens
- Telegram notifications — get alerted when outdated containers are found and when an update succeeds or fails, with per-container overrides, multiple chats and editable templates.
- Dark/Light theme — toggle persisted in localStorage with automatic support
# docker-compose.yml
services:
docker-image-checker-king:
image: victoare/docker-image-checker-king:latest
container_name: docker-image-checker-king
ports:
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/data
environment:
- AUTO_CHECK_FAST_MINUTES=60 # Auto-check interval when rate limits are healthy
- AUTO_CHECK_MINUTES=360 # Auto-check interval when rate limits are low
- TELEGRAM_BOT_TOKEN=123456:ABC-DEF... # Telegram bot token for notifications (optional)
restart: unless-stoppeddocker compose up -dOpen http://localhost:8080 in your browser.
Managing users and access is out of scope by design. This is a homelab-grade tool with full access to your Docker socket and no authentication of its own. Run it behind your own security layer (reverse proxy, VPN, firewall) and never put it directly on the open internet.
Hitting Update on several rows at once starts all of them in parallel. That is the fast path, and most of the time it is also the right one — but the daemon deduplicates layers shared between concurrent pulls, so one pull can sit silent for minutes while another downloads the same layer, and a registry under load will simply be slow.
The update flow is built around that:
- Generous stream timeouts — a pull is only abandoned after 10 minutes of complete silence, not 30 seconds
- Pull retries — the image pull is retried up to 3 times with a 5s / 15s / 45s backoff before the attempt is given up
- Retry queue — an update that still fails is not marked failed; it is parked in a queue that drains one container at a time. The row shows a purple sweeping bar while it waits. Only a container that fails its queued attempt too ends up failed.
- Rollback — if the new container cannot be created or started, the previous one is renamed back and restarted. The old container is deleted only once the swap has actually succeeded.
- Persistent failure marker — a failed update leaves a red bar on the row that survives page reloads. It is cleared when a later update succeeds, or when a check notices that the container moved on anyway (new image digest or a new container id — e.g. you updated it outside this tool).
Get notified on Telegram when outdated containers are detected. Edit the message template to suit your own liking. You can add your bot to multiple chats and set different containers to notify in different chats. Mute notification on selected containers.
- Create a Telegram bot via @BotFather and copy the bot token
- Pass the token as an environment variable:
environment: - TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
- Add the bot to a group chat or start a private conversation with it and send it a message
- Open the Settings (gear icon) in the web UI → click Discover chats to auto-detect available chat IDs
- Enable the chats you want to receive notifications on
Each chat can be set to one of two modes:
| Mode | Behavior |
|---|---|
| Once (default) | Sends one notification per outdated container; re-notifies when the local digest changes (e.g. after you update the container) |
| Every | Sends a notification each time a new remote image version is detected for an outdated container |
Independently of the outdated alerts, each chat reports the outcome of container updates. Successful and Failed are two separate switches, both on by default, and both can be overridden per container (Default / On / Off) from the bell icon. The two messages have their own editable templates — open the template editor and switch tabs.
Click the bell icon (🔔) on any container row to:
- Disable notifications entirely for that container
- Override the notification mode per chat
- Fine-tune which chats receive alerts for specific containers
| Env Variable | Default | Description |
|---|---|---|
AUTO_CHECK_FAST_MINUTES |
60 |
Auto-check interval when rate limits are healthy |
AUTO_CHECK_MINUTES |
360 |
Auto-check interval when rate limits are low |
TELEGRAM_BOT_TOKEN |
(empty) | Telegram bot token for notifications (optional) |
DOCKER_TIMEOUT_MS |
30000 |
Idle timeout for ordinary Docker API calls |
DOCKER_STREAM_STALL_MS |
600000 |
How long a pull stream may go completely silent before it is abandoned |
PULL_ATTEMPTS |
3 |
Pull attempts per update attempt, with a 5s / 15s / 45s backoff |
STOP_GRACE_SECONDS |
30 |
Seconds a container gets to exit on SIGTERM before it is killed (a ceiling, not a wait) |
| Registry | Auth Method |
|---|---|
| Docker Hub (docker.io) | Anonymous token (100 req/6h per IP) |
| ghcr.io | Anonymous for public images |
| gcr.io / Artifact Registry | Anonymous for public images |
| quay.io | Anonymous for public images |
| public.ecr.aws | Anonymous for public images |
| Private registries | Requires docker login on the host |
A Prometheus-compatible endpoint is exposed at /metrics. It is read-only: it renders the result of the last check from the cache and never contacts a registry, so scraping it costs nothing in registry rate limit.
| Metric | Type | Labels | Meaning |
|---|---|---|---|
docker_image_checker_build_info |
gauge | version |
Always 1; carries the build version |
docker_image_checker_containers |
gauge | result |
Containers per result (UpToDate, Outdated, Unknown, Pinned, NoLocalDigest) |
docker_image_checker_container_outdated |
gauge | container, image, registry |
1 when a newer image is available |
docker_image_checker_last_check_timestamp_seconds |
gauge | – | Unix time of the last completed check |
docker_image_checker_last_check_duration_seconds |
gauge | – | Duration of the last check in this process |
docker_image_checker_registry_rate_limit_remaining |
gauge | registry |
Remaining pulls reported by the registry |
docker_image_checker_registry_rate_limit_limit |
gauge | registry |
Pull limit reported by the registry |
docker_image_checker_updates_total |
counter | result |
Container updates run since start (success, failed) |
scrape_configs:
- job_name: docker-image-checker-king
scrape_interval: 5m
static_configs:
- targets: ['docker-image-checker-king:8080']A long scrape interval is enough — the values only change when a check runs. The most useful alert is on last_check_timestamp_seconds: it stops advancing when the checker has stopped checking, which is otherwise invisible.
- alert: ImageCheckerStalled
expr: time() - docker_image_checker_last_check_timestamp_seconds > 86400
annotations:
summary: No image check completed in the last 24 hoursgit clone https://github.com/Victoare/DockerImageCheckerKing.git
cd DockerImageCheckerKing
docker build -t docker-image-checker-king ./sourceBrowser ──SSE──► Express (Node.js) ──unix socket──► Docker Engine API
│
├──► Registry v2 API (HEAD /v2/.../manifests/<tag>)
├──► Token endpoints (auth.docker.io, ghcr.io/token, etc.)
└──► /data/*.json (persists results across restarts)
Idea out of pure frustration by Victoare
Mostly vibe coded using Claude Opus by anthropic.
Logo image made by ChatGPT

