Ansible for Docker and Podman: Container Automation Complete Guide
By Luca Berton · Published 2024-01-01 · Category: installation
Automate Docker and Podman with Ansible. Build images, manage containers, configure networks and volumes, deploy with docker-compose, manage registries.
Why Ansible for Container Management?
Docker CLI and docker-compose handle single-host container workflows well. But when you need to deploy containers across dozens of servers, enforce consistent configuration, manage secrets, coordinate rolling updates, and integrate with existing infrastructure automation — Ansible fills the gap.
Ansible treats containers like any other infrastructure resource: declarative, idempotent, and version-controlled.
See also: Ansible Execution Environments: Build Custom EEs for Enterprise Automation
Collections
# Docker modules
ansible-galaxy collection install community.docker
# Podman modules
ansible-galaxy collection install containers.podman
# Dependencies
pip install docker # For community.docker
Docker Container Management
Run Containers
---
- name: Deploy application containers
hosts: docker_hosts
become: true
tasks:
- name: Run nginx container
community.docker.docker_container:
name: nginx-proxy
image: nginx:1.27-alpine
state: started
restart_policy: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /etc/nginx/conf.d:/etc/nginx/conf.d:ro
- /etc/letsencrypt:/etc/letsencrypt:ro
- nginx-logs:/var/log/nginx
env:
TZ: "UTC"
labels:
app: proxy
environment: production
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/"]
interval: 30s
timeout: 10s
retries: 3
- name: Run PostgreSQL
community.docker.docker_container:
name: postgres
image: postgres:16-alpine
state: started
restart_policy: unless-stopped
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
- /backups/postgres:/backups
env:
POSTGRES_USER: "{{ vault_pg_user }}"
POSTGRES_PASSWORD: "{{ vault_pg_password }}"
POSTGRES_DB: app
memory: "2g"
cpus: 2
shm_size: "256m"
- name: Run Redis
community.docker.docker_container:
name: redis
image: redis:7-alpine
state: started
restart_policy: unless-stopped
command: redis-server --requirepass {{ vault_redis_password }} --maxmemory 512mb --maxmemory-policy allkeys-lru
ports:
- "127.0.0.1:6379:6379"
volumes:
- redis-data:/data
Build Images
---
- name: Build and push Docker images
hosts: build_server
become: true
vars:
app_version: "{{ lookup('env', 'APP_VERSION') | default('latest') }}"
registry: registry.company.com
tasks:
- name: Build application image
community.docker.docker_image:
name: "{{ registry }}/myapp"
tag: "{{ app_version }}"
source: build
build:
path: /opt/app
dockerfile: Dockerfile
args:
NODE_ENV: production
BUILD_DATE: "{{ ansible_date_time.iso8601 }}"
pull: true
nocache: "{{ force_rebuild | default(false) }}"
push: true
force_source: true
- name: Tag as latest
community.docker.docker_image:
name: "{{ registry }}/myapp"
tag: latest
source: local
repository: "{{ registry }}/myapp"
push: true
force_tag: true
Docker Networks
- name: Create application network
community.docker.docker_network:
name: app-network
driver: bridge
ipam_config:
- subnet: 172.20.0.0/16
gateway: 172.20.0.1
driver_options:
com.docker.network.bridge.name: br-app
- name: Create overlay network (Swarm)
community.docker.docker_network:
name: service-mesh
driver: overlay
attachable: true
encrypted: true
Docker Volumes
- name: Create named volumes
community.docker.docker_volume:
name: "{{ item }}"
state: present
loop:
- pgdata
- redis-data
- app-uploads
- nginx-logs
- name: Create NFS volume
community.docker.docker_volume:
name: shared-data
driver: local
driver_options:
type: nfs
o: "addr=10.0.1.100,rw,nfsvers=4"
device: ":/exports/shared"
Docker Compose with Ansible
---
- name: Deploy with docker-compose
hosts: app_servers
become: true
tasks:
- name: Deploy compose stack
community.docker.docker_compose_v2:
project_src: /opt/app
files:
- docker-compose.yml
- docker-compose.prod.yml
state: present
pull: always
env_files:
- .env.production
register: compose_result
- name: Show deployment result
ansible.builtin.debug:
msg: "Services: {{ compose_result.services | map(attribute='name') | list }}"
- name: Wait for app to be healthy
ansible.builtin.uri:
url: "http://localhost:8080/health"
status_code: 200
register: health
retries: 30
delay: 5
until: health.status == 200
Registry Management
- name: Log into private registry
community.docker.docker_login:
registry_url: registry.company.com
username: "{{ vault_registry_user }}"
password: "{{ vault_registry_password }}"
- name: Pull specific image
community.docker.docker_image:
name: registry.company.com/myapp
tag: "{{ deploy_version }}"
source: pull
- name: Prune old images
community.docker.docker_prune:
images: true
images_filters:
until: 168h # 7 days
volumes: true
builder_cache: true
See also: Ansible Builder & Execution Environments: Complete Guide (2026)
Podman Container Management
Podman is the rootless, daemonless alternative to Docker — increasingly standard on RHEL, Fedora, and CentOS.
Run Podman Containers
---
- name: Deploy with Podman
hosts: podman_hosts
become: true
tasks:
- name: Run nginx with Podman
containers.podman.podman_container:
name: nginx
image: docker.io/nginx:1.27-alpine
state: started
restart_policy: always
ports:
- "80:80"
- "443:443"
volumes:
- /etc/nginx/conf.d:/etc/nginx/conf.d:ro,Z
- nginx-logs:/var/log/nginx:Z
label:
app: proxy
- name: Run rootless container (user scope)
containers.podman.podman_container:
name: dev-app
image: docker.io/node:22-alpine
state: started
user: "1000"
userns: keep-id
ports:
- "3000:3000"
volumes:
- ./app:/app:Z
command: node /app/server.js
become: false # Run as regular user
Podman Pods (Like Kubernetes Pods)
- name: Create application pod
containers.podman.podman_pod:
name: webapp
state: started
ports:
- "8080:80"
- "5432:5432"
infra: true
- name: Run app in pod
containers.podman.podman_container:
name: webapp-app
image: myapp:latest
pod: webapp
state: started
- name: Run database in same pod
containers.podman.podman_container:
name: webapp-db
image: postgres:16-alpine
pod: webapp
state: started
env:
POSTGRES_PASSWORD: "{{ vault_pg_password }}"
Generate Systemd Units from Podman
- name: Generate systemd service for container
containers.podman.podman_container:
name: myapp
image: myapp:latest
state: started
generate_systemd:
path: /etc/systemd/system/
restart_policy: always
names: true
new: true
- name: Enable container service
ansible.builtin.systemd:
name: container-myapp
enabled: true
daemon_reload: true
Podman Compose Alternative — Quadlet
# Quadlet: systemd-native container management (Podman 4.4+)
- name: Deploy Quadlet container unit
ansible.builtin.copy:
content: |
[Container]
Image=docker.io/nginx:1.27-alpine
PublishPort=80:80
Volume=/etc/nginx/conf.d:/etc/nginx/conf.d:ro,Z
AutoUpdate=registry
[Service]
Restart=always
[Install]
WantedBy=default.target
dest: /etc/containers/systemd/nginx.container
mode: '0644'
notify: reload systemd
- name: Deploy Quadlet network unit
ansible.builtin.copy:
content: |
[Network]
Subnet=172.20.0.0/16
Gateway=172.20.0.1
[Install]
WantedBy=default.target
dest: /etc/containers/systemd/app.network
mode: '0644'
notify: reload systemd
Install Docker with Ansible
---
- name: Install Docker Engine
hosts: all
become: true
tasks:
- name: Install prerequisites
ansible.builtin.apt:
name:
- ca-certificates
- curl
- gnupg
state: present
update_cache: true
- name: Add Docker GPG key
ansible.builtin.get_url:
url: https://download.docker.com/linux/ubuntu/gpg
dest: /etc/apt/keyrings/docker.asc
mode: '0644'
- name: Add Docker repository
ansible.builtin.apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
- name: Install Docker
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
update_cache: true
- name: Start Docker
ansible.builtin.systemd:
name: docker
state: started
enabled: true
- name: Add user to docker group
ansible.builtin.user:
name: "{{ ansible_user }}"
groups: docker
append: true
See also: Ansible with Podman: Manage Containers Using Inventory & Modules (Guide)
Rolling Updates
---
- name: Rolling container update
hosts: app_servers
serial: 1
become: true
tasks:
- name: Pull new image
community.docker.docker_image:
name: registry.company.com/myapp
tag: "{{ deploy_version }}"
source: pull
- name: Remove from load balancer
ansible.builtin.uri:
url: "http://lb.company.com/api/pool/{{ inventory_hostname }}"
method: DELETE
delegate_to: localhost
- name: Wait for connections to drain
ansible.builtin.pause:
seconds: 30
- name: Update container
community.docker.docker_container:
name: myapp
image: "registry.company.com/myapp:{{ deploy_version }}"
state: started
restart: true
restart_policy: unless-stopped
ports:
- "8080:8080"
- name: Wait for health check
ansible.builtin.uri:
url: "http://localhost:8080/health"
status_code: 200
register: health
retries: 30
delay: 5
until: health.status == 200
- name: Re-add to load balancer
ansible.builtin.uri:
url: "http://lb.company.com/api/pool"
method: POST
body_format: json
body:
host: "{{ inventory_hostname }}"
port: 8080
delegate_to: localhost
FAQ
Should I use Ansible or docker-compose?
Use docker-compose for single-host development environments. Use Ansible when you need to deploy containers across multiple servers, integrate with non-container infrastructure (networking, storage, DNS), manage secrets with Vault, or coordinate rolling updates. They're complementary — Ansible can deploy docker-compose files with community.docker.docker_compose_v2.
Should I use Docker or Podman?
Docker is the industry standard with the largest ecosystem. Podman is rootless by default, daemonless, and the default on RHEL/Fedora. If you're running RHEL-based systems or need rootless containers for security, use Podman. For most other cases, Docker works well. Ansible supports both equally.
Can Ansible build Docker images?
Yes. community.docker.docker_image with source: build builds images from a Dockerfile. For complex CI/CD pipelines, dedicated tools like Buildah, Kaniko, or GitHub Actions may be more appropriate, but Ansible handles simple builds well.
How do I manage secrets in containerized apps?
Use ansible.builtin.vault to encrypt secrets, then pass them as environment variables to containers via the env parameter. For Docker Swarm, use Docker secrets. For Kubernetes, use Kubernetes secrets managed by Ansible's kubernetes.core collection.
Conclusion
Ansible automates the full container lifecycle — install Docker/Podman, build images, manage registries, deploy containers with networking and volumes, orchestrate rolling updates, and clean up unused resources. Use community.docker for Docker and containers.podman for Podman. For production deployments across multiple hosts, Ansible's idempotent approach ensures consistent, repeatable container infrastructure.
Related Articles
• Ansible for Kubernetes: Complete Automation Guide • Ansible CI/CD Pipeline Integration • Install Ansible Complete Guide • Ansible for IoT and Edge Computing • Ansible Proxmox Complete Guide • Ansible Container Security: Image Scanning & Runtime ProtectionCategory: installation