Monitor All Your Docker Containers in Uptime Kuma (The Right Way)

Share
Monitor All Your Docker Containers in Uptime Kuma (The Right Way)
Uptime Kuma dashboard showing Docker container monitors grouped inside a Docker containers group with green uptime bars

Uptime Kuma is great for HTTP and TCP monitoring, but a lot of people don't realize it can also watch your Docker containers directly — showing you whether a container is running or not, completely separate from whether its exposed port responds.

The problem is the default setup. Most guides tell you to bind-mount /var/run/docker.sock into the Uptime Kuma container, which works, but it's a really bad idea from a security standpoint. The Docker socket is basically root access to your host. If Kuma ever has a vulnerability — or gets compromised through one of your monitored services somehow — you've handed over the keys.

There's a better way: a lightweight proxy called docker-socket-proxy that sits between Kuma and the Docker socket, and only allows the specific API calls Kuma actually needs. Everything else gets blocked at the proxy level.

On top of that, if you have 15+ containers running, adding monitors one by one through the Uptime Kuma UI is painful. So I'll also write a small Python script that reads your running containers and bulk-imports all of them into a group automatically.


What You'll Need

  • Docker and Docker Compose on your server
  • Uptime Kuma running (or about to run) via Docker Compose
  • Python 3 with pip available on the host
  • sqlite3 package
  • About 10 minutes

The Compose Setup

Here's the full compose.yml. Two services: Uptime Kuma and the socket proxy, isolated on an internal network so nothing else can reach the proxy.

services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    ports:
      - "127.0.0.1:3001:3001"
    volumes:
      - ./data:/app/data
    networks:
      - kuma-internal
    environment:
      - TZ=Africa/Cairo
    depends_on:
      - socket-proxy
    deploy:
      resources:
        limits:
          memory: 256M
          cpus: "0.75"
        reservations:
          memory: 128M
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  socket-proxy:
    image: tecnativa/docker-socket-proxy:latest
    container_name: socket-proxy
    restart: unless-stopped
    environment:
      - CONTAINERS=1
      - IMAGES=1
      - INFO=1
      - PING=1
      - VERSION=1
      - POST=0
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - kuma-internal
    read_only: true
    tmpfs:
      - /run
      - /tmp
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

networks:
  kuma-internal:
    driver: bridge

A few things worth pointing out here:

The proxy has POST=0 set, which blocks write operations over the Docker API. Kuma only needs to read container state, so there's no reason to allow anything else. The read_only: true flag locks down the container's own filesystem too. We add /run and /tmp as tmpfs mounts because the proxy's entrypoint script writes a haproxy config at startup — without those, it crashes in a restart loop.

/usr/local/bin/docker-entrypoint.sh: line 21: can't create /tmp/haproxy.cfg: Read-only file syste

Kuma itself no longer has the socket mounted at all. It talks to the proxy over the internal Docker network using a plain TCP connection.

Run it:

docker compose up -d
docker logs socket-proxy --tail 20

You can verify connectivity from inside the Kuma container before touching the UI:

docker exec uptime-kuma curl -s http://socket-proxy:2375/_ping

If that returns OK, the proxy is working and Kuma can reach it.


Connecting the Docker Host in Uptime Kuma

Log into the Uptime Kuma UI and go to Settings → Docker Hosts → Add Docker Host.

  • Friendly Name: Docker containers (or whatever you like)
  • Connection Type: TCP
  • Docker Daemon: tcp://socket-proxy:2375

Hit Test — it should connect immediately. If it doesn't, double-check that both containers are on the same kuma-internal network:

docker inspect uptime-kuma socket-proxy --format '{{.Name}}: {{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}'

Bulk-Adding All Containers via Python

Adding monitors one by one through the UI works fine if you have three or four containers. Once you've more than 20 or 30, it's a lot of wasted time. The uptime-kuma-api library talks to Kuma over its internal Socket.IO API and lets you automate everything.

First, install it:

pip3 install uptime-kuma-api

You'll need your Docker Host ID. Rather than authenticating through the API just to get it, query the SQLite database directly — Kuma stores everything there:

sqlite3 ./data/kuma.db "SELECT id, name FROM docker_host;"

Now create a file called bulk_kuma.py:

import subprocess
from uptime_kuma_api import UptimeKumaApi, MonitorType

KUMA_URL = "http://127.0.0.1:3001"
USERNAME = "your_username"
PASSWORD = "your_password"
DOCKER_HOST_ID = 1  # replace with the ID from the sqlite query
GROUP_NAME = "Docker containers" #replace with the group name you want

result = subprocess.run(["docker", "ps", "--format", "{{.Names}}"], capture_output=True, text=True)
containers = [c for c in result.stdout.strip().split("\n") if c]

api = UptimeKumaApi(KUMA_URL)
api.login(USERNAME, PASSWORD)

# Clean up any existing Docker-type monitors
for m in api.get_monitors():
    if m.get("type") == MonitorType.DOCKER:
        api.delete_monitor(m["id"])
        print(f"[DELETED] {m['name']}")

# Create the group
group = api.add_monitor(type=MonitorType.GROUP, name=GROUP_NAME)
group_id = group["monitorID"]
print(f"[GROUP] Created '{GROUP_NAME}' with ID {group_id}")

# Add all running containers under the group
for name in containers:
    api.add_monitor(
        type=MonitorType.DOCKER,
        name=name,
        docker_container=name,
        docker_host=DOCKER_HOST_ID,
        parent=group_id,
    )
    print(f"[ADDED] {name}")

api.disconnect()

Run it:

python3 bulk_kuma.py

The script will delete any existing Docker monitors (so you start clean), create a group called "Docker containers", then add every currently-running container underneath it. Groups in Kuma are just monitors with a special type — the parent parameter is what nests them visually in the UI.

When it's done, refresh the Uptime Kuma dashboard. You'll see a collapsible "Docker containers" group with one monitor per container. Containers that are running show as UP; anything that stops will immediately flip to DOWN and trigger your configured notifications.


A Note on Re-running the Script

If you deploy new containers later and want to add them without duplicating existing monitors, change the delete block to a skip instead:

existing = {m["name"] for m in api.get_monitors()}

for name in containers:
    if name in existing:
        print(f"[SKIP] {name} already exists")
        continue
    api.add_monitor(
        type=MonitorType.DOCKER,
        name=name,
        docker_container=name,
        docker_host=DOCKER_HOST_ID,
        parent=group_id,
    )
    print(f"[ADDED] {name}")

You'll need to set group_id to your existing group's ID in that case — get it from the same sqlite query:

sqlite3 ./data/kuma.db "SELECT id, name FROM monitor WHERE type='group';"