How to Turn a Docker Compose File into an Architecture Diagram

A compose.yaml already lists your services, networks and volumes. Here is how to read it into a diagram, find the calls it hides, and keep the picture current.

10/22/2026

How to Turn a Docker Compose File into an Architecture Diagram

A Compose file is often the most honest description of a system you have. It is what actually starts on a laptop, in CI and sometimes in production. It is also a wall of YAML, and a new teammate still has to ask which service talks to which.

This guide turns a Compose file into an architecture diagram you can trust: render the file Compose really runs, map each key to a diagram element, add the calls the file cannot express, and keep the drawing in step with the file. At the end we use Archboard's free tool to get the first draft in seconds.

The example: a small shop stack

Here is the compose.yaml we will draw. It is short, but it has the usual traps: a service on two networks, a dependency that only exists in a config file, and a dev-only tool.

name: shop

services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - api
    networks: [frontend]

  api:
    build: ./api
    environment:
      DATABASE_URL: postgres://shop:${POSTGRES_PASSWORD}@db:5432/shop
      REDIS_URL: redis://cache:6379/0
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    networks: [frontend, backend]

  worker:
    build: ./api
    command: ["node", "worker.js"]
    environment:
      DATABASE_URL: postgres://shop:${POSTGRES_PASSWORD}@db:5432/shop
      REDIS_URL: redis://cache:6379/0
    depends_on:
      - db
    deploy:
      replicas: 2
    networks: [backend]

  db:
    image: postgres:16
    environment:
      POSTGRES_USER: shop
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U shop"]
      interval: 5s
    networks: [backend]

  cache:
    image: redis:7
    networks: [backend]

  adminer:
    image: adminer
    ports:
      - "8081:8080"
    profiles: [debug]
    networks: [backend]

volumes:
  db-data:

networks:
  frontend:
  backend:

Step 1: Render the file Compose actually runs

Teams rarely run one file as written. An override file adds ports, a .env file fills in variables, and extends pulls fields from another service. Before you draw, ask Compose for the result:

docker compose config > rendered.yaml

docker compose config merges the override files, fills in variables and resolves extends, then prints the model Compose will run. Draw from that output, not from the file you happen to have open.

One warning: the output contains the real values of your variables, including anything secret in .env. Treat rendered.yaml like the .env file itself and do not commit or share it.

Step 2: Map Compose keys to diagram elements

Most of the diagram comes straight from the file:

Compose key Diagram element
services.<name> One card per service, named after its key
image / build On the card: the image tag, or the build context
ports (8080:80) On the card, and on the edge from outside: :8080 → 80
deploy.replicas / scale A replica count on the card, not extra boxes
networks A frame per network; each service sits in the frame of its first network
Named volumes A volume node connected to the services that mount it
secrets, configs Nodes connected to the services that use them, by name only
depends_on Start-up order, written on the card or as a dashed edge
profiles Optional services: leave them out or mark them as optional

Two rules keep the picture honest. A service that joins two networks, like api, is the bridge between them, so place it at the edge of its first network's frame and let its connections cross into the other. And replicas are a number, not a stack of boxes: worker ×2 stays correct when you scale to five.

Step 3: Add the calls the file does not show

This is the step tools cannot do for you. depends_on is about start-up order: Compose starts db before api and, with condition: service_healthy, waits for the health check to pass. It says nothing about who calls whom at runtime.

The real calls hide in three places:

  • Connection strings. DATABASE_URL and REDIS_URL name hosts (db, cache) and ports. Each one is an edge.
  • Config files mounted into containers. nginx.conf contains proxy_pass http://api:3000;. That is the edge from web to api, and it lives outside the Compose file.
  • Code. A service can call another one without any line in Compose. worker uses REDIS_URL but has no depends_on for cache. If Redis starts late, the worker fails on start-up, and the diagram is the first place that gap becomes visible.

A quick search finds most of them:

grep -rnE "://([^/@[:space:]]*@)?(api|web|db|cache)[:/]|proxy_pass" --include="*.conf" --include="*.yaml" --include="*.yml" --include="*.env" .

Then confirm with whoever owns each service. Draw runtime calls as solid edges labeled with protocol and port, and keep depends_on as a note or a dashed edge, so nobody mistakes start-up order for traffic.

Step 4: Decide what to leave out

A Compose file mixes the system with the tools around it. Usually you leave out:

  • Dev-only services behind profiles, such as adminer, mail catchers or debug proxies.
  • One-off jobs such as migrations or seeders, unless the diagram is about deployment.
  • Bind mounts for source code and config files. Show a config file only through the dependency it carries, like the proxy_pass edge from nginx.conf here.
  • Environment values. Names of variables can explain an edge; values belong in your secret store, not on a diagram.

Step 5: Lay it out

Put traffic left to right: the browser, then the published port, then the services behind it. Draw each network as a frame, so a call that crosses networks is easy to spot. Group the data stores together and put volumes under the service that mounts them. Label every edge with protocol and port, for example HTTP :3000 or SQL :5432.

Get the first draft with the free tool

You do not have to draw the cards by hand. Paste the file (or the rendered output; the tool does not read environment values) into the free Docker Compose to diagram tool. Viewing needs no account. Services become cards with their image, ports and replicas, networks become frames, named volumes, secrets and configs are connected to the services that use them, and depends_on becomes connections between services.

The tool draws only what the file says. It does not show runtime calls, it does not read environment values or env_file contents, it does not follow include, and for a service that uses extends it draws only the fields written in that service. That is why steps 1 and 3 still matter.

With a free account you open the result in Archboard as a new diagram, then add the runtime edges from step 3 and remove the noise from step 4. This is the shop stack after that pass:

Docker diagram of a Compose project: the browser reaches web (nginx) on port 8080, web proxies to api, and api plus a two-replica worker call db (PostgreSQL) and cache (Redis) in the backend network. Volume db-data is mounted by db.

10 components · 7 connections

Open in Archboard
Diagram contents
  • Browser
  • frontend
  • backend
  • web
  • api
  • cache
  • db
  • worker
  • db-data
  • HTTP :8080 → 80
  • proxy_pass :3000
  • SQL :5432
  • RESP :6379
  • jobs :6379
  • /var/lib/postgresql/data
The shop stack from this guide: services in their network frames, the database volume, and the runtime calls read from nginx.conf and the connection URLs, which the Compose file alone does not show.

The cards come from the Docker pack, the browser from the Network pack, and the whole diagram stays within the Free plan's 15 diagram nodes. For another Docker layout, with containers, a registry and a load balancer, see the Docker runtime mesh example.

Keep it current

A diagram drawn from a Compose file goes stale the day someone adds a service. Three habits help:

  • Add a line to the pull request template: "Adds a service, a network, a published port or a connection string? Update the diagram."
  • Re-run docker compose config during reviews and compare the service list with the diagram.
  • Link the live diagram from the README instead of pasting a screenshot, so there is only one copy to update.

When the stack moves to Kubernetes, the same method applies to manifests; see From Kubernetes YAML to an architecture diagram.

FAQ

Does depends_on mean one service calls another? No. It controls start-up order, and with condition: service_healthy it also waits for a health check. Runtime calls come from connection strings, config files and code.

Can I diagram several Compose files at once? Render them first with docker compose config, which merges override files into one model. The free tool reads one file at a time.

Does the tool read my .env file? No. Variables are not filled in: an image tag or port written with a variable, such as image: shop-api:${TAG}, is shown exactly as written, and values under environment and in env_file are not read.

Should I draw one box per container? Draw one card per service and write the replica count on it. Individual containers come and go; the service is what the team reasons about.

Will the diagram update when I change compose.yaml? No. It is a starting point. Keep it current with the habits above, or redraw from the tool and copy over your runtime edges.

Start free — one seat, no credit card.

Related posts

← Back to blog

Open a canvas in a minute

Sign in with Google or GitHub. Free for a single seat. Upgrade when the team joins.