Skip to content

How to monitor Docker containers with Compose

Monitor Docker containers with Compose: CPU, memory, network, I/O and state per service, with the agent image, a socket proxy and a service-down alert.

Last checked on

You monitor Docker containers with inping by adding the agent as a service to your project's Compose file. For every service it reports CPU, memory, network, disk I/O and whether it is running, plus the values of the host. If a service is down, an alert opens with the next report. The agent reaches the Docker socket only through a proxy that rejects every write.

What do you see afterwards?

  • Services: one row per service of the Compose project with state, CPU, memory, network and I/O. The rates appear within two intervals, because they need two measurements.
  • The host: CPU, memory, load and disks, read through read-only mounts of /proc, /sys and /.
  • Project → Packages: the packages from the lock files of the mounted project directory.

Not reported in a container: host packages and scheduled tasks (they would be the container's), runtime versions such as php -v (they cannot be queried across container boundaries) and packages inside the images of other services.

What should you keep an eye on with containers?

  • Is it running at all? The state is Docker's running state. A container in a restart loop counts as not running whenever a measurement catches it restarting. A container that runs but no longer responds still counts as running. So also check web services from the outside with an HTTP(S) check on the URL.
  • Memory against the limit: inping relates memory without file cache to the container's limit, or to the host's memory if there is no limit. When a container reaches its limit, the kernel ends processes inside it. An alert beforehand is worth more than a restart afterwards.
  • CPU: inping shows the share of the whole host CPU (0 to 100), not per core like docker stats. This way the containers add up against the usage of the host.
  • The host's disks: logs and volumes live on the host and fill its disk. So the rule for disks applies here too.

Requirements

  • Docker Engine with Compose v2 on the host, and the project's docker-compose.yml
  • outgoing HTTPS to www.inping.io and to ghcr.io
  • a free inping account and a registration token (valid for 24 hours)
  • the image version and digest from the release notes in the app under Agents → Installation and downloads (/agents/install)

Step by step

The Docker tab in the Agents → Add agent dialog shows all three parts with your values. Here you see placeholders.

1. Three lines in the .env next to the Compose file

INPING_AGENT_IMAGE=ghcr.io/medienakzent/inping-agent:<version>@sha256:<digest>
INPING_API_URL=https://www.inping.io
INPING_PROJECT_ID=<project-id>

The image is pinned to a version and digest. There is no moving tag such as latest: this way a tampered registry cannot swap the agent on pull.

2. Services, config, volume and network in the Compose file

Add the following parts to the project's Compose file. The agent runs with a read-only file system, without capabilities and as user 65532; it can only write to the volume inping-agent-state.

services:
  inping-agent:
    image: ${INPING_AGENT_IMAGE:?set INPING_AGENT_IMAGE to a release}
    restart: unless-stopped
    read_only: true
    cap_drop: [ALL]
    security_opt:
      - no-new-privileges:true
    configs:
      - source: inping-agent
        target: /etc/inping/agent.toml
    volumes:
      - inping-agent-state:/var/lib/inping-agent
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/host/root:ro
      - ./:/srv/app:ro
    networks: [default, inping]
    depends_on: [inping-docker-proxy]

  inping-docker-proxy:
    image: tecnativa/docker-socket-proxy:0.3.0
    restart: unless-stopped
    environment:
      CONTAINERS: 1
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks: [inping]

configs:
  inping-agent:
    content: |
      api_url = "${INPING_API_URL:?set INPING_API_URL}"
      state_dir = "/var/lib/inping-agent"

      [docker]
      endpoint = "tcp://inping-docker-proxy:2375"

      [[project]]
      id = "${INPING_PROJECT_ID:?set INPING_PROJECT_ID}"
      path = "/srv/app"

volumes:
  inping-agent-state:

networks:
  inping:
    internal: true

The proxy is not optional: it is the only service that sees the socket, and it sits in the internal network inping. It rejects every write and everything outside /containers. Pin its image by digest too, once you have determined it:

docker buildx imagetools inspect tecnativa/docker-socket-proxy:0.3.0

3. Register once and start

The token goes in through standard input and ends up in no file.

echo "<token>" | docker compose run --rm -T inping-agent register --token -
docker compose up -d

There is one project per Compose file. A second project on the same host gets its own agent in its own Compose file.

Without the socket proxy

The agent can also read the socket directly: with /var/run/docker.sock mounted, group_add with the socket's group and the endpoint unix:///var/run/docker.sock. It then sends the same requests, but anyone who breaks into its container controls the host. Take this route only on a host where nothing else runs alongside the agent, such as your own Synology NAS. The details are in the installation guide, chapter 8.

How do you check that it works?

docker compose ps                        # inping-agent and inping-docker-proxy: Up
docker compose logs --tail 20 inping-agent

The log shows msg=starting, msg=configuration and msg="docker: reporting the agent's own Compose project" project=<name>. In inping the Services section appears on the agent's page. If it is missing, switch on the metric Services (Docker) under Settings → Collected metrics.

Thresholds and alerts

The rule for services is active on every new agent: if a service is not running, an alert opens immediately and closes by itself as soon as the service is running again. On top of that come the host rules for CPU and memory (above 90% for 5 minutes) and disks (above 90% immediately).

You create your own rules on the agent's page under Settings → Alert rules, for one particular service or for all. CPU, memory in per cent or bytes, network and I/O per second are possible. A good start is the memory of a service above 90% for 5 minutes.

For you to hear about it, the assigned project needs a notification rule, for example for Service down, Server threshold exceeded or Agent offline, by mail or webhook. The agent counts as offline after three missed reports.

Update

The agent never replaces itself in a container. Enter the new version and the new digest in the .env and pull the image again:

docker compose pull inping-agent && docker compose up -d inping-agent

Uninstall

Remove the two services, the config, the volume and the network from the Compose file and run docker compose up -d --remove-orphans. With a copy of the example file, docker compose down -v is enough, where -v deletes the state volume. Then delete the agent in inping.

When nothing arrives

  • register reports registration failed: the token has expired or been used, or INPING_API_URL is wrong. The message names the response.
  • set INPING_AGENT_IMAGE to a release: the .env is not next to the Compose file, or a variable is missing.
  • The agent restarts in a loop: look in docker compose logs inping-agent. cannot start means it was never registered (step 3). docker source unusable means the proxy cannot be reached: check that it is attached to the network inping and that the endpoint is tcp://inping-docker-proxy:2375.
  • Host metrics arrive, services do not: the proxy answers with 403 because CONTAINERS: 1 is missing. Or the agent's container belongs to no Compose project, in which case every container on the host is reported.
  • The project's packages are missing: ./:/srv/app:ro mounts the wrong directory. The lock files have to be at the top level of /srv/app.

Frequently asked questions

What does the agent report per container?

For each service of the Compose project: the name, the image, whether it is running, its CPU share of the whole host, its memory without file cache along with the limit, plus network and disk I/O per second. On top of that come the values of the host itself: CPU, memory, disks and platform.

Why does inping show different CPU values from docker stats?

Docker counts per core, so a container can be above 100% there. inping shows the share of the whole host CPU from 0 to 100, so that the containers add up against the usage of the host.

Is a read-only docker.sock mount not safe enough?

No. The :ro only protects the socket file; the daemon still accepts every call. Whoever reaches the socket can start, stop and create containers. That is why the agent only sees the socket proxy, which rejects every write.

Does the socket proxy also protect the data of the other containers?

Not completely. The proxy allows all read calls under /containers, which includes inspect output with environment variables, logs and files. It protects the host from the agent, but it is no separation between the containers. For metrics the agent itself sends only two GET requests, the container list and the statistics, and with a Docker log source also the query for the container logs.

Does inping find vulnerabilities in my images?

No. The agent reads the lock files of the mounted project directory, not the images of the other services. The image of each service is merely reported alongside. The container variant does not report host packages or scheduled tasks either.

Does the agent update itself in the container?

No. In a container the command inping-agent update refuses to do so. A new version is a new image: you enter the version and digest in the .env and pull the image again.

How many agents do I need for several Compose projects?

One per Compose file; a second project on the same host gets its own agent. The Free plan includes 2 agents free of charge.

Further reading