Docker Compose diff

Paste two versions of a Compose file and see what really changed, and which changes make it riskier. Key order and short or long syntax do not count. Both files stay in this browser.

Stays in your browser. What you paste or choose is read on this page only. It is not sent to a server, not saved in the link or in browser storage, and never part of analytics.

Paste the text, or choose a file from your computer. Up to 512 KiB.

0 of 512 KiB

Stays in your browser. What you paste or choose is read on this page only. It is not sent to a server, not saved in the link or in browser storage, and never part of analytics.

Paste the text, or choose a file from your computer. Up to 512 KiB.

0 of 512 KiB

What it does

It reads both files with the same safe parser as the Compose Analyzer, brings each one to a normalized form and compares the forms, service by service and key by key. Two files that Compose reads the same way have no changes, whatever their key order and whichever syntax they use. Then it classifies the changes that make the second file riskier, and runs the Analyzer's rules on both files to show the findings the change introduces and the ones it resolves.

How it works

  1. Both files are parsed with size, depth and alias limits; no YAML tag or include is followed, and nothing is run or fetched.
  2. Each file is normalized: ports and volumes in short or long syntax become one form, environment, labels and networks as a list or a mapping become a mapping, depends_on gets its default condition, and a command written as a string is split into words the way Compose splits it.
  3. Mappings are compared entry by entry and lists such as ports, volumes and cap_add item by item, so a reordered list is not a change. Values of secret-looking variables are masked in the result.
  4. Seven diff rules find the changes that raise risk, and the Analyzer's rules run on both files. A finding that a diff rule already reports is not reported twice.

Example

A WordPress update in which the second file is also reordered and written in other syntax. The diff reports four changes:

Before
before/docker-compose.yml
services:
  wordpress:
    image: wordpress:6.8-apache
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_NAME: wordpress
    depends_on:
      db:
        condition: service_healthy
  db:
    image: mariadb:11.8
    restart: unless-stopped
    volumes:
      - db:/var/lib/mysql
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
volumes:
  db: {}
After
after/docker-compose.yml
services:
  db:
    image: mariadb:latest
    restart: unless-stopped
    ports:
      - "3306:3306"
    healthcheck:
      interval: 10s
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
  wordpress:
    image: wordpress:6.9-apache
    restart: unless-stopped
    ports:
      - target: 80
        host_ip: 127.0.0.1
        published: 8080
    environment:
      - WORDPRESS_DB_HOST=db
      - WORDPRESS_DB_NAME=wordpress
    depends_on:
      db:
        condition: service_healthy
volumes:
  db: {}
  • Changed: db › image: mariadb:11.8 → mariadb:latest
  • Added: db › ports: 3306:3306
  • Removed: db › volumes: db:/var/lib/mysql
  • Changed: wordpress › image: wordpress:6.8-apache → wordpress:6.9-apache

Three of them raise risk:

  • db now publishes 3306:3306 on every network interface. Docker publishes ports past ufw rules, so it can be reachable from the internet.
  • db moved from the pinned image mariadb:11.8 to mariadb:latest, which follows whatever version is published next.
  • db no longer mounts db:/var/lib/mysql. Data the container wrote there is not in the new container, and what it writes from now on is lost when the container is recreated.

And the Analyzer adds one finding:

  • db runs the database image mariadb:latest without a volume for its data directory /var/lib/mysql.

Limits

  • Static comparison: nothing is run, so images, ports and paths are not checked against a running system.
  • Not a text diff and not a merge: comments, formatting and key order are ignored, and the tool does not write a combined file.
  • Durations are compared as written: 90s and 1m30s count as a change.
  • Variables are not read from .env: ${VAR} is compared as written, except in a published port or a volume source, where a variable with a default counts as its default.
  • The risk rules cover the changes listed above. A change they do not name can still matter: read the full list of changes.

Methodology: how the numbers are worked out

Sources

  1. Docker Docs: Compose file reference
  2. Compose Specification
  3. Docker Docs: Docker Engine security
  4. Docker Docs: Packet filtering and firewalls

Updated

Next step

Docker Compose Analyzer

A free public tool by ANAROQ, a company building compute infrastructure. What ANAROQ is building