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
#!/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
- 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.pyTASK [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 --listRun 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:
plugin: community.docker.docker_containersThis 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.