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.dockercollection installed- Docker and Docker Compose on target hosts
# Install the Docker collection
ansible-galaxy collection install community.dockerStep 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: trueSee 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: outputUsing 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: alwaysIndividual Container Management
- name: Run a single container
community.docker.docker_container:
name: redis
image: redis:7-alpine
ports:
- "6379:6379"
restart_policy: always
state: startedPull Latest Images
- name: Pull latest images
community.docker.docker_compose_v2:
project_src: /opt/myapp
pull: always
state: present
recreate: alwaysSee 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 == 200Best Practices
- Use templates for docker-compose.yml — inject environment-specific values
- Store secrets in Ansible Vault — never hardcode passwords
- Pin image versions — avoid
latesttag in production - Use health checks — verify services are healthy after deployment
- Manage volumes carefully — persistent data survives container recreation
- Use
.envfiles 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: trueWith Pull and Recreate
- community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
pull: always
recreate: always
register: outputStop Stack
- community.docker.docker_compose_v2:
project_src: /opt/myapp
state: absentDeploy 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: 2Manage Specific Services
# Restart only the web service
- community.docker.docker_compose_v2:
project_src: /opt/myapp
services:
- web
state: restartedFull 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_linesDeploy Compose Stack
- community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
become: true
register: outputPull and Recreate
- docker_compose_v2:
project_src: /opt/myapp
state: present
pull: always
recreate: smart # Only recreate changed services
become: trueDeploy 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.changedCompose 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: trueStop 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: trueFull 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 == 200FAQ
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
Category: installation