Ansible Docker Compose: Manage Multi-Container Stacks (Guide)
By Luca Berton · Published 2024-01-01 · Category: installation
How to manage Docker Compose stacks with Ansible. Deploy, update, and manage multi-container applications with community.docker collection examples.

Managing Docker Compose with Ansible
Ansible and Docker Compose are a powerful combination. Ansible handles the infrastructure and deployment automation; Docker Compose manages multi-container applications. This guide shows you how to use them together.
See also: community.docker 5.1.0 — New Feature for Docker Compose Pull
Prerequisites
• Ansible 2.9+ on your control node •community.docker collection installed
• Docker and Docker Compose on target hosts
# Install the Docker collection
ansible-galaxy collection install community.docker
Step 1: Install Docker with Ansible
---
- name: Install Docker
hosts: docker_hosts
become: true
tasks:
- name: Install prerequisites
ansible.builtin.apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
update_cache: true
- name: Add Docker GPG key
ansible.builtin.apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker repository
ansible.builtin.apt_repository:
repo: "deb 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-compose-plugin
state: present
- name: Start and enable Docker
ansible.builtin.service:
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 Docker: Complete Guide to Container Automation (2026)
Step 2: Deploy Docker Compose Files
Using the docker_compose_v2 Module
---
- name: Deploy application
hosts: app_servers
become: true
tasks:
- name: Create project directory
ansible.builtin.file:
path: /opt/myapp
state: directory
- name: Copy docker-compose.yml
ansible.builtin.copy:
src: docker-compose.yml
dest: /opt/myapp/docker-compose.yml
- name: Copy environment file
ansible.builtin.template:
src: .env.j2
dest: /opt/myapp/.env
- name: Deploy with Docker Compose
community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
register: output
- name: Show deployment output
ansible.builtin.debug:
var: output
Using Templates for docker-compose.yml
# templates/docker-compose.yml.j2
version: '3.8'
services:
web:
image: "{{ app_image }}:{{ app_version }}"
ports:
- "{{ app_port }}:80"
environment:
DATABASE_URL: "postgresql://{{ db_user }}:{{ db_password }}@db:5432/{{ db_name }}"
depends_on:
- db
restart: always
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: "{{ db_name }}"
POSTGRES_USER: "{{ db_user }}"
POSTGRES_PASSWORD: "{{ db_password }}"
restart: always
volumes:
pgdata:
Step 3: Managing Containers
Start/Stop/Restart
- name: Stop application
community.docker.docker_compose_v2:
project_src: /opt/myapp
state: absent
- name: Restart application
community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
recreate: always
Individual Container Management
- name: Run a single container
community.docker.docker_container:
name: redis
image: redis:7-alpine
ports:
- "6379:6379"
restart_policy: always
state: started
Pull Latest Images
- name: Pull latest images
community.docker.docker_compose_v2:
project_src: /opt/myapp
pull: always
state: present
recreate: always
See also: Ansible for Docker and Podman: Container Automation Complete Guide
Rolling Updates with Zero Downtime
---
- name: Zero-downtime deploy
hosts: app_servers
serial: 1
become: true
tasks:
- name: Pull new image
community.docker.docker_image:
name: "myapp:{{ new_version }}"
source: pull
- name: Update compose file
ansible.builtin.template:
src: docker-compose.yml.j2
dest: /opt/myapp/docker-compose.yml
- name: Recreate containers
community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
recreate: always
- name: Wait for health check
ansible.builtin.uri:
url: "http://localhost:{{ app_port }}/health"
status_code: 200
register: health
retries: 10
delay: 5
until: health.status == 200
Best Practices
Use templates for docker-compose.yml — inject environment-specific values Store secrets in Ansible Vault — never hardcode passwords Pin image versions — avoidlatest tag in production
Use health checks — verify services are healthy after deployment
Manage volumes carefully — persistent data survives container recreation
Use .env files via Ansible templates for Docker environment variables
Serial deployments for zero-downtime rolling updates
FAQ
Which module should I use: docker_compose or docker_compose_v2?
Usedocker_compose_v2 (for Docker Compose V2 CLI plugin). The older docker_compose module is deprecated.
Can Ansible build Docker images?
Yes, usecommunity.docker.docker_image with build.path parameter to build from a Dockerfile.
How do I pass secrets to Docker containers via Ansible?
Use Ansible Vault to encrypt secrets, then pass them as environment variables in the docker-compose template.Conclusion
Ansible + Docker Compose gives you reproducible, automated deployments for containerized applications. Use Ansible for the infrastructure layer and Docker Compose for application orchestration.
For more Docker and container tutorials, visit AnsiblePilot.
Deploy Docker Compose Stack
- name: Deploy app stack
community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
become: true
With Pull and Recreate
- community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
pull: always
recreate: always
register: output
Stop Stack
- community.docker.docker_compose_v2:
project_src: /opt/myapp
state: absent
Deploy with Template
- name: Deploy docker-compose.yml
ansible.builtin.template:
src: docker-compose.yml.j2
dest: /opt/myapp/docker-compose.yml
become: true
- name: Start services
community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
pull: always
become: true
{# templates/docker-compose.yml.j2 #}
services:
web:
image: nginx:{{ nginx_version }}
ports:
- "{{ http_port }}:80"
volumes:
- ./html:/usr/share/nginx/html:ro
app:
image: myapp:{{ app_version }}
environment:
DATABASE_URL: "postgresql://{{ db_user }}:{{ db_pass }}@db:5432/{{ db_name }}"
depends_on:
- db
db:
image: postgres:{{ postgres_version }}
environment:
POSTGRES_DB: "{{ db_name }}"
POSTGRES_USER: "{{ db_user }}"
POSTGRES_PASSWORD: "{{ db_pass }}"
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
Scale Services
- community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
scale:
web: 3
worker: 2
Manage Specific Services
# Restart only the web service
- community.docker.docker_compose_v2:
project_src: /opt/myapp
services:
- web
state: restarted
Full Deployment Playbook
---
- name: Deploy application
hosts: docker_hosts
become: true
vars:
app_version: "2.5.0"
tasks:
- name: Create app directory
file: { path: /opt/myapp, state: directory }
- name: Deploy compose file
template:
src: docker-compose.yml.j2
dest: /opt/myapp/docker-compose.yml
- name: Deploy env file
template:
src: .env.j2
dest: /opt/myapp/.env
mode: '0600'
- name: Pull and deploy
community.docker.docker_compose_v2:
project_src: /opt/myapp
pull: always
state: present
register: deploy
- name: Show deployment result
debug:
msg: "{{ deploy.actions | map(attribute='status') | list }}"
Key Parameters
| Parameter | Description |
|-----------|-------------|
| project_src | Path to docker-compose.yml |
| state | present, absent, restarted |
| pull | always, missing, never |
| recreate | always, never, smart |
| services | Target specific services |
| scale | Scale service replicas |
| profiles | Compose profiles |
FAQ
docker_compose vs docker_compose_v2?
docker_compose is for the old Python library (deprecated). docker_compose_v2 uses the Docker CLI plugin (docker compose). Always use v2.
How do I pass environment variables?
Use .env file in project directory, or environment: in docker-compose.yml. Template both with Ansible.
How do I view logs?
- command: docker compose -f /opt/myapp/docker-compose.yml logs --tail=50
register: logs
- debug: var=logs.stdout_lines
Deploy Compose Stack
- community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
become: true
register: output
Pull and Recreate
- docker_compose_v2:
project_src: /opt/myapp
state: present
pull: always
recreate: smart # Only recreate changed services
become: true
Deploy with Template
# Generate docker-compose.yml from template
- template:
src: docker-compose.yml.j2
dest: /opt/myapp/docker-compose.yml
become: true
register: compose_file
# Deploy (only if config changed)
- docker_compose_v2:
project_src: /opt/myapp
state: present
become: true
when: compose_file.changed
Compose Template Example
{# templates/docker-compose.yml.j2 #}
services:
app:
image: myorg/webapp:{{ app_version }}
ports:
- "{{ app_port }}:8080"
environment:
DB_HOST: postgres
DB_PASSWORD: "{{ vault_db_password }}"
REDIS_URL: redis://redis:6379
depends_on:
- postgres
- redis
restart: unless-stopped
postgres:
image: postgres:{{ postgres_version | default('16') }}
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: "{{ vault_db_password }}"
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
pgdata:
redis_data:
Manage Specific Services
# Restart only the app service
- docker_compose_v2:
project_src: /opt/myapp
services: [app]
state: restarted
become: true
# Scale a service
- docker_compose_v2:
project_src: /opt/myapp
services: [worker]
scale:
worker: 3
state: present
become: true
Stop and Remove
# Stop all services
- docker_compose_v2:
project_src: /opt/myapp
state: stopped
become: true
# Remove everything (containers + networks)
- docker_compose_v2:
project_src: /opt/myapp
state: absent
remove_volumes: true # Also remove volumes
become: true
Full Deployment Playbook
- hosts: docker_hosts
become: true
vars:
app_version: "2.5.0"
tasks:
- name: Create app directory
file: { path: /opt/myapp, state: directory }
- name: Deploy compose file
template:
src: docker-compose.yml.j2
dest: /opt/myapp/docker-compose.yml
register: compose_config
- name: Deploy environment file
template:
src: .env.j2
dest: /opt/myapp/.env
mode: '0600'
register: env_config
- name: Deploy stack
docker_compose_v2:
project_src: /opt/myapp
pull: always
state: present
register: deploy
- name: Show deployed services
debug:
msg: "{{ deploy.services | default({}) }}"
Health Check After Deploy
- docker_compose_v2:
project_src: /opt/myapp
state: present
become: true
- uri:
url: "http://localhost:{{ app_port }}/health"
status_code: 200
retries: 10
delay: 5
register: health
until: health.status == 200
FAQ
docker_compose vs docker_compose_v2?
docker_compose is deprecated (uses docker-compose v1 Python library). Use docker_compose_v2 which uses the docker compose CLI (v2).
How to handle .env files?
Deploy .env with template module before running docker_compose_v2. Or pass environment variables in the compose template directly.
Can I use docker-compose.yml from a git repo?
- git: { repo: "{{ repo }}", dest: /opt/myapp, version: main }
- docker_compose_v2: { project_src: /opt/myapp }
Related Articles
• the Ansible Galaxy reference • Ansible template vs copy module • Ansible env var patterns • the Ansible Vault walkthrough • the Ansible Docker referenceCategory: installation