A monitoring tool can find your containers on its own, with no list of targets to write. There is no magic involved: it relies on the Docker and Kubernetes APIs, which already know everything about what is running. This post walks through how Maintenant, a Go monitoring tool, does it with the Docker SDK for Go and client-go.
Runtime detection
On startup, the tool runs a detection sequence:
- If the
MAINTENANT_RUNTIMEenvironment variable is set, it forces the mode - Otherwise, if
KUBERNETES_SERVICE_HOSTis set, it switches to Kubernetes mode with in-cluster config - Otherwise, it tries a kubeconfig (
KUBECONFIGor~/.kube/config) and uses it if the cluster answers - Otherwise, it connects to the Docker socket (
/var/run/docker.sock) and switches to Docker mode
Docker mode
In Docker mode, the official Docker SDK for Go connects to the Unix socket, mounted read-only. Three things happen.
Initial listing
A call to ContainerList retrieves every container, stopped ones included. For each container, the tool extracts Docker labels, the Compose project name (via the com.docker.compose.project label), exposed ports, and health check configuration.
Event listening
A dedicated goroutine listens to Docker events in real time via Events. Every start, stop, die, and health_status event is captured and processed immediately, which is what lets the dashboard update in real time over SSE.
Metrics collection
The Docker ContainerStats API provides CPU, memory, network, and disk I/O metrics per container. They are collected at regular intervals and stored in SQLite.
Kubernetes mode
In Kubernetes mode, the tool uses the official client-go library with in-cluster configuration (service account). The required RBAC is minimal and read-only:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: maintenant
rules:
- apiGroups: [""]
resources: ["namespaces", "nodes", "pods", "events", "services"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets", "daemonsets"]
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["get", "list", "watch"]
- apiGroups: ["metrics.k8s.io"]
resources: ["pods", "nodes"]
verbs: ["get", "list"]
Nothing is created, modified, or deleted in the cluster: observation only.
Workloads
Unlike Docker mode, which operates at the container level, Kubernetes mode operates at the workload level: Deployments, DaemonSets, StatefulSets. Pods are grouped under their parent workload, which gives a more meaningful view of a service's health than a flat list of pods.
Storage: SQLite in WAL mode
All collected data is stored in SQLite using WAL mode (Write-Ahead Logging). Readers do not block the writer and the writer does not block readers, which matters for a process that continuously collects metrics while serving a real-time dashboard.
Memory stays low (under 30 MB at rest) thanks to strict goroutine management and Go's garbage collector.
Summary
Auto-discovery is the use of existing Docker and Kubernetes APIs (list, then watch events, then poll stats), combined with a lightweight Go architecture and simple storage choices.
Originally published on maintenant.dev.
Maintenant is an open-source, self-hosted monitoring tool for Docker and Kubernetes: containers, endpoints, cron heartbeats, TLS certificates and alerts, in one container that discovers your stack on its own. Install it in a minute: https://maintenant.dev
This project was funded by the French Government as part of the France 2030 plan operated by Bpifrance.



Top comments (0)