AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

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 GuideAnsible CI/CD Pipeline IntegrationInstall Ansible Complete GuideAnsible for IoT and Edge ComputingAnsible Proxmox Complete GuideAnsible Container Security: Image Scanning & Runtime Protection

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home