Skip to content

Dynamic Docker Inventory Example


Time to close the loop on the actual lab this entire course has been built around — a script that discovers myubuntu and myfedora’s real IP addresses directly from Docker, instead of a hand-maintained inventory.yaml that could quietly go stale the moment those containers are rebuilt.

Building The Script

inventory_docker.py
#!/usr/bin/env python3

import json
import subprocess
import sys

NETWORK = "ansible_demonet"
CONTAINER_PREFIX = "ansible"


def get_containers():
    result = subprocess.run(
        ["docker", "ps", "--format", "{{.Names}}"],
        capture_output=True,
        text=True,
        check=True,
    )

    return [
        name.strip()
        for name in result.stdout.splitlines()
        if name.strip().startswith(CONTAINER_PREFIX)
    ]


def get_container_ip(name):
    """Get the IP address of a container on the Ansible Docker network."""
    result = subprocess.run(
        [
            "docker",
            "inspect",
            "-f",
            f"{{{{.NetworkSettings.Networks.{NETWORK}.IPAddress}}}}",
            name,
        ],
        capture_output=True,
        text=True,
        check=True,
    )

    return result.stdout.strip()


def build_inventory():
    containers = get_containers()

    hosts = []
    hostvars = {}

    for container in containers:
        # ansible_ubuntu -> ubuntu
        # ansible_fedora -> fedora
        # ansible-fedora-01 -> fedora
        host = container[len(CONTAINER_PREFIX) :].lstrip("_-").split("-", 1)[0]
        hosts.append(host)

        hostvars[host] = {
            "ansible_host": get_container_ip(container),
            "ansible_user": "alice",
        }

    return {
        "servers": {
            "hosts": hosts,
        },
        "_meta": {
            "hostvars": hostvars,
        },
    }


if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "--list":
        print(json.dumps(build_inventory(), indent=2))
    else:
        print(json.dumps({}))

Every value this course’s static inventory hardcoded — the IPs specifically — is now discovered fresh, by actually asking Docker, every time this script runs.

Using It

site.yaml
- name: Play
  hosts: servers
  tasks:
    - name: Say Hi
      ansible.builtin.command:
        cmd: "echo HI"
chmod +x inventory_docker.py
ansible-playbook site.yaml -i inventory_docker.py
TASK [Say Hi] *****
changed: [ubuntu]
changed: [fedora]

Rebuild the containers, and their IPs could genuinely change — the static inventory.yaml used throughout this entire course would need manual editing to keep up. This script needs nothing at all; it asks Docker directly, every single run.

Verifying It Works

./inventory_docker.py --list

Run it directly, exactly as Ansible would, to confirm the JSON output looks right before ever pointing a real playbook at it — a genuinely useful debugging step for any dynamic inventory script. Example output:

{
  "servers": {
    "hosts": [
      "fedora",
      "ubuntu"
    ]
  },
  "_meta": {
    "hostvars": {
      "fedora": {
        "ansible_host": "10.0.0.2",
        "ansible_user": "alice"
      },
      "ubuntu": {
        "ansible_host": "10.0.0.1",
        "ansible_user": "alice"
      }
    }
  }
}

What A Real Production Setup Would Actually Use

This script is for understanding the mechanism, not a production recommendation. For a genuine Docker-based inventory, the community.docker.docker_containers inventory plugin does this properly — handling edge cases, multiple networks, and container lifecycle states this teaching example doesn’t attempt to cover:

inventory_docker.yaml
plugin: community.docker.docker_containers

This mirrors exactly the guidance from the roles section about checking Ansible Galaxy before writing something from scratch — understanding how the hand-rolled version works makes trusting the properly-maintained plugin version make a lot more sense.

Best Practices

  • Verify a dynamic inventory script’s output directly (--list) before pointing a real playbook at it — confirm the JSON is actually correct in isolation first.
  • Prefer an existing, maintained inventory plugin over a hand-rolled script for real infrastructure — reserve scripts like this one for genuinely custom sources nothing else already covers.
  • Remember every other tool in this course — patterns, --limit, ansible-inventory, host and group variables — works identically regardless of whether the inventory behind it is static or dynamic. Dynamic inventory changes where the data comes from, not how anything else in Ansible treats it.
Last updated on