Diun (Docker Image Update Notifier) is a Go daemon that watches your running containers and notifies you when upstream image updates are available on Docker Hub, GHCR, or a private registry. It runs headlessly — there is no web UI — and communicates entirely through notification providers like Gotify, Slack, Telegram, or webhooks. When Diun crashes, loses its Docker socket, or silently fails a scan cycle, you receive no update notifications for any of your containers. Without external monitoring, you can go weeks unaware that your image update alerting has been broken. Vigilmon lets you track Diun process health, scan schedule execution, notification delivery, and Docker socket connectivity so you know immediately when your image update pipeline has a gap.
What You'll Set Up
- Diun process health via cron heartbeats
- Scan schedule execution monitoring
- Docker socket connectivity checks
- Notification delivery verification
- Alert routing for the Diun monitoring stack
Prerequisites
- Diun 0.20+ running as a systemd service or Docker container
- Docker socket accessible at
/var/run/docker.sock - A free Vigilmon account
Step 1: Monitor Diun Process Health
Diun runs as a single long-lived process that holds an open watch on the Docker socket and wakes on a cron schedule to scan images. When it crashes — due to OOM, a Docker API error, or a registry TLS failure — the process simply disappears. There are no automatic restarts unless you configure systemd or Docker's restart policy. Monitor process health first:
- In Vigilmon, click Add Monitor → Cron Heartbeat.
- Set Expected ping interval to
5 minutes. - Copy the heartbeat URL (e.g.,
https://vigilmon.online/heartbeat/abc123).
Create the process health check script:
#!/bin/bash
# /usr/local/bin/diun-health-check.sh
HEARTBEAT_URL="https://vigilmon.online/heartbeat/abc123"
# Check if Diun is running as a system process
if pgrep -x diun > /dev/null; then
echo "Diun running (PID: $(pgrep -x diun))"
curl -s "$HEARTBEAT_URL"
exit 0
fi
# Check if Diun is running in a Docker container
if docker ps --filter "name=diun" --filter "status=running" --format "{{.Names}}" 2>/dev/null | grep -q .; then
echo "Diun running in Docker"
curl -s "$HEARTBEAT_URL"
exit 0
fi
# Check systemd service status
if systemctl is-active --quiet diun 2>/dev/null; then
echo "Diun active via systemd"
curl -s "$HEARTBEAT_URL"
exit 0
fi
echo "Diun not running"
exit 1
Install as a cron job:
chmod +x /usr/local/bin/diun-health-check.sh
(crontab -l 2>/dev/null; echo "*/5 * * * * /usr/local/bin/diun-health-check.sh >> /var/log/diun-health.log 2>&1") | crontab -
If the heartbeat stops arriving, Vigilmon alerts within 5 minutes — enough time to restart Diun before a full scan cycle is missed.
Step 2: Monitor Scan Schedule Execution
Diun runs image scans on a cron schedule (default: every hour). If the schedule silently misses cycles — because the cron expression is wrong, the process entered a deadlock waiting on a registry response, or a previous scan never completed — you get no update notifications for images that changed since the last successful scan. Monitor scan execution directly from Diun's logs:
#!/bin/bash
# /usr/local/bin/diun-scan-check.sh
HEARTBEAT_URL="https://vigilmon.online/heartbeat/def456"
LOG_FILE="/var/log/diun/diun.log" # Adjust for your deployment
MAX_MINUTES_SINCE_SCAN=90 # Alert if no scan in 90 minutes
# For Docker deployments, use: docker logs diun 2>&1 | tail -500
RECENT_LOGS=$(tail -500 "$LOG_FILE" 2>/dev/null || \
docker logs diun --since 2h 2>&1 2>/dev/null || \
journalctl -u diun --since "2 hours ago" --no-pager 2>/dev/null)
if [ -z "$RECENT_LOGS" ]; then
echo "Cannot read Diun logs"
exit 1
fi
# Find the timestamp of the most recent scan completion
LAST_SCAN=$(echo "$RECENT_LOGS" | grep -E "Starting Diun|Scan completed|Analysis done|Finished watching" | tail -1)
if [ -z "$LAST_SCAN" ]; then
echo "No scan completion found in recent logs"
exit 1
fi
# Extract timestamp and compute age
LAST_SCAN_TIME=$(echo "$LAST_SCAN" | grep -oP '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}' | head -1)
if [ -z "$LAST_SCAN_TIME" ]; then
# Try alternate log format
LAST_SCAN_TIME=$(echo "$LAST_SCAN" | grep -oP '\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}' | head -1)
fi
if [ -z "$LAST_SCAN_TIME" ]; then
echo "Could not parse scan timestamp from: $LAST_SCAN"
exit 1
fi
SCAN_EPOCH=$(date -d "$LAST_SCAN_TIME" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%S" "$LAST_SCAN_TIME" +%s 2>/dev/null)
NOW_EPOCH=$(date +%s)
MINUTES_AGO=$(( (NOW_EPOCH - SCAN_EPOCH) / 60 ))
if [ "$MINUTES_AGO" -le "$MAX_MINUTES_SINCE_SCAN" ]; then
echo "Last scan ${MINUTES_AGO} minutes ago (OK)"
curl -s "$HEARTBEAT_URL"
else
echo "Last scan ${MINUTES_AGO} minutes ago (stale — threshold: ${MAX_MINUTES_SINCE_SCAN} min)"
exit 1
fi
Set the Vigilmon heartbeat expected interval to 90 minutes (or 1.5× your Diun scan interval). This catches both a crashed Diun process and a hung scan that never completes.
Step 3: Monitor Docker Socket Connectivity
Diun reads container metadata through the Docker socket (/var/run/docker.sock). When the Docker daemon is restarted, the socket is replaced, and Diun — if it held a stale file descriptor — stops receiving container events without crashing. Verify Docker socket connectivity independently:
#!/bin/bash
# /usr/local/bin/diun-socket-check.sh
HEARTBEAT_URL="https://vigilmon.online/heartbeat/ghi789"
SOCKET_PATH="/var/run/docker.sock"
TIMEOUT=10
# Verify the socket exists
if [ ! -S "$SOCKET_PATH" ]; then
echo "Docker socket missing at $SOCKET_PATH"
exit 1
fi
# Query Docker API directly through the socket
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
--unix-socket "$SOCKET_PATH" \
--max-time "$TIMEOUT" \
"http://localhost/v1.41/info")
if [ "$HTTP_CODE" != "200" ]; then
echo "Docker API unresponsive through socket (HTTP $HTTP_CODE)"
exit 1
fi
# Verify we can list containers (what Diun needs)
CONTAINER_COUNT=$(curl -s \
--unix-socket "$SOCKET_PATH" \
--max-time "$TIMEOUT" \
"http://localhost/v1.41/containers/json?all=true" | \
python3 -c "import sys, json; print(len(json.load(sys.stdin)))" 2>/dev/null || echo -1)
if [ "$CONTAINER_COUNT" -eq -1 ]; then
echo "Failed to list containers via Docker API"
exit 1
fi
echo "Docker socket OK: $CONTAINER_COUNT containers visible"
curl -s "$HEARTBEAT_URL"
Run this check every 5 minutes. Socket connectivity failures are usually caused by Docker daemon upgrades or the Diun container losing its volume mount — catching them early prevents a full scan cycle from silently failing.
Step 4: Verify Notification Delivery
Diun's primary value is notification delivery — alerting you when an image update is available. If the notification provider (Gotify, Slack, webhook, etc.) is misconfigured, its token is revoked, or the target URL is unreachable, Diun completes scans successfully but delivers no alerts. Monitor notification delivery by injecting a test notification and confirming receipt:
#!/bin/bash
# /usr/local/bin/diun-notify-check.sh
HEARTBEAT_URL="https://vigilmon.online/heartbeat/jkl012"
# ---- Gotify delivery check ----
GOTIFY_URL="http://localhost:8080"
GOTIFY_TOKEN="your-gotify-app-token"
TIMEOUT=10
# Send a test message to Gotify
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
--max-time "$TIMEOUT" \
-X POST "${GOTIFY_URL}/message?token=${GOTIFY_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"title":"Diun health check","message":"Notification delivery test","priority":1}')
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ]; then
echo "Gotify notification delivery OK (HTTP $HTTP_CODE)"
curl -s "$HEARTBEAT_URL"
else
echo "Gotify notification delivery failed (HTTP $HTTP_CODE)"
exit 1
fi
For Slack webhook delivery:
#!/bin/bash
HEARTBEAT_URL="https://vigilmon.online/heartbeat/jkl012"
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
TIMEOUT=10
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
--max-time "$TIMEOUT" \
-X POST "$SLACK_WEBHOOK" \
-H "Content-Type: application/json" \
-d '{"text":"Diun health check: notification delivery OK"}')
if [ "$HTTP_CODE" = "200" ]; then
echo "Slack notification delivery OK"
curl -s "$HEARTBEAT_URL"
else
echo "Slack delivery failed (HTTP $HTTP_CODE)"
exit 1
fi
Run this check every 15 minutes. Add it to cron as a separate heartbeat from the process health check so you can distinguish between "Diun is down" (process alert) and "Diun is running but can't notify" (delivery alert).
Step 5: Monitor Registry Connectivity
Diun polls upstream registries (Docker Hub, GHCR, ECR, or private) to check for image updates. When DNS resolution fails, a registry returns 503, or a private registry's certificate expires, Diun logs errors per-image but continues running. Monitor connectivity to the registries Diun watches:
#!/bin/bash
# /usr/local/bin/diun-registry-check.sh
HEARTBEAT_URL="https://vigilmon.online/heartbeat/mno345"
TIMEOUT=15
FAILED=0
# Docker Hub connectivity
DOCKERHUB_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
--max-time "$TIMEOUT" \
"https://registry-1.docker.io/v2/")
if [ "$DOCKERHUB_CODE" != "401" ] && [ "$DOCKERHUB_CODE" != "200" ]; then
echo "Docker Hub registry unreachable (HTTP $DOCKERHUB_CODE)"
FAILED=1
else
echo "Docker Hub OK (HTTP $DOCKERHUB_CODE — 401 is expected for anonymous)"
fi
# GHCR connectivity
GHCR_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
--max-time "$TIMEOUT" \
"https://ghcr.io/v2/")
if [ "$GHCR_CODE" != "401" ] && [ "$GHCR_CODE" != "200" ]; then
echo "GHCR unreachable (HTTP $GHCR_CODE)"
FAILED=1
else
echo "GHCR OK (HTTP $GHCR_CODE)"
fi
# Private registry (if applicable)
PRIVATE_REGISTRY="registry.your-domain.com"
if [ -n "$PRIVATE_REGISTRY" ]; then
PRIVATE_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
--max-time "$TIMEOUT" \
"https://${PRIVATE_REGISTRY}/v2/")
if [ "$PRIVATE_CODE" != "401" ] && [ "$PRIVATE_CODE" != "200" ]; then
echo "Private registry unreachable (HTTP $PRIVATE_CODE)"
FAILED=1
else
echo "Private registry OK (HTTP $PRIVATE_CODE)"
fi
fi
if [ "$FAILED" -eq 0 ]; then
curl -s "$HEARTBEAT_URL"
else
exit 1
fi
Set the heartbeat to 10 minutes. Registry outages lasting more than one scan cycle mean you miss image updates published during the outage window — these need to be retried manually if Diun doesn't retry them automatically.
Step 6: Set Up Alert Channels
Configure Vigilmon to route Diun alerts to the right people:
- Go to Alert Channels in Vigilmon.
- Add a Slack webhook to
#ops-alertsor#docker-updates— whoever manages image updates needs to know when the notifier is broken. - Add email notification for the scan schedule monitor — scan gaps are urgent but not necessarily pager-worthy.
- Set Consecutive failures before alert to
2for process health — eliminates alerts from the 30-second window between Diun crash and systemd restart.
Add a maintenance window when upgrading Diun:
# Suppress alerts during planned Diun upgrade
curl -X POST https://vigilmon.online/api/maintenance \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"monitor_id": "DIUN_MONITOR_ID",
"duration_minutes": 5,
"reason": "Diun version upgrade"
}'
systemctl stop diun
# Upgrade Diun binary
systemctl start diun
Summary
| Monitor | Target | What It Catches |
|---|---|---|
| Cron heartbeat (process) | pgrep diun + Docker status | Crash, OOM kill, missing restart policy |
| Cron heartbeat (scan) | Log timestamp of last scan | Hung scan, missed cron cycle, deadlock |
| Cron heartbeat (socket) | Docker API via /var/run/docker.sock | Stale socket FD, Docker daemon restart |
| Cron heartbeat (notify) | Gotify / Slack delivery test | Revoked token, unreachable webhook URL |
| Cron heartbeat (registry) | Docker Hub / GHCR / private | DNS failure, registry outage, cert expiry |
Diun's failure mode is invisible silence — it stops alerting on image updates without crashing, without error dialogs, and without any user-facing indication. Vigilmon's external heartbeat monitoring fills the gap: you know within minutes when image update detection has broken, rather than discovering weeks later that you've been running outdated containers without knowing it.
Start monitoring for free at vigilmon.online.