Introduction
The community.docker collection lets you manage Docker from Ansible — pull images, run containers, manage networks and volumes, and deploy multi-container apps with Docker Compose. Automate your entire container lifecycle alongside infrastructure provisioning.
Install the Collection
ansible-galaxy collection install community.docker
Requirements on remote hosts:
- Docker Engine installed
- Python
dockerSDK (pip install docker)
# Install prerequisites with Ansible
- name: Install Docker SDK for Python
ansible.builtin.pip:
name: docker
state: present
Run a Container
- name: Run nginx container
community.docker.docker_container:
name: webserver
image: nginx:latest
state: started
ports:
- "80:80"
- "443:443"
restart_policy: unless-stopped
- name: Run with environment variables
community.docker.docker_container:
name: myapp
image: myapp:2.0
state: started
env:
DATABASE_URL: "postgresql://db:5432/myapp"
REDIS_URL: "redis://redis:6379"
APP_ENV: "production"
ports:
- "8080:8080"
volumes:
- /opt/myapp/data:/app/data
- /opt/myapp/logs:/app/logs
restart_policy: always
memory: "512m"
cpus: 1.0
Container Parameters
| Parameter | Description |
|---|---|
name | Container name (required) |
image | Docker image with tag |
state | started, stopped, absent, present |
ports | Port mappings (host:container) |
volumes | Volume mounts (host:container[:mode]) |
env | Environment variables (dict) |
restart_policy | no, always, unless-stopped, on-failure |
network_mode | bridge, host, none, or network name |
networks | List of networks to connect to |
command | Override container command |
entrypoint | Override entrypoint |
memory | Memory limit (512m, 1g) |
cpus | CPU limit |
labels | Container labels (dict) |
pull | Pull policy: always, missing, never |
recreate | Force recreate even if running |
comparisons | Control what triggers recreation |
Pull Images
# Pull latest
- name: Pull nginx image
community.docker.docker_image:
name: nginx
tag: latest
source: pull
# Pull specific version
- name: Pull app image
community.docker.docker_image:
name: myregistry.com/myapp
tag: "{{ app_version }}"
source: pull
force_source: true # Always check for updates
# Login to private registry
- name: Log in to registry
community.docker.docker_login:
registry_url: https://myregistry.com
username: "{{ registry_user }}"
password: "{{ registry_password }}"
- name: Pull private image
community.docker.docker_image:
name: myregistry.com/myapp
tag: "{{ app_version }}"
source: pull
Docker Networks
# Create network
- name: Create app network
community.docker.docker_network:
name: app-network
driver: bridge
ipam_config:
- subnet: 172.20.0.0/16
gateway: 172.20.0.1
# Run containers on the network
- name: Run database
community.docker.docker_container:
name: postgres
image: postgres:16
networks:
- name: app-network
aliases:
- db
env:
POSTGRES_PASSWORD: "{{ db_password }}"
volumes:
- pgdata:/var/lib/postgresql/data
- name: Run app
community.docker.docker_container:
name: myapp
image: myapp:latest
networks:
- name: app-network
env:
DATABASE_URL: "postgresql://postgres:{{ db_password }}@db:5432/myapp"
ports:
- "8080:8080"
Docker Volumes
# Create named volume
- name: Create data volume
community.docker.docker_volume:
name: app-data
state: present
# Use in container
- name: Run with named volume
community.docker.docker_container:
name: myapp
image: myapp:latest
volumes:
- app-data:/app/data # Named volume
- /host/path:/container/path # Bind mount
- /host/logs:/app/logs:ro # Read-only bind mount
# Remove volume
- name: Remove old volume
community.docker.docker_volume:
name: old-data
state: absent
force: true
Docker Compose
# Deploy with docker-compose.yml
- name: Deploy stack
community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
register: output
# Pull and recreate
- name: Update stack
community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
pull: always
recreate: always
# Stop and remove
- name: Tear down stack
community.docker.docker_compose_v2:
project_src: /opt/myapp
state: absent
# Scale a service
- name: Scale workers
community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
services:
- worker
scale:
worker: 3
Deploy Compose File from Template
- name: Deploy docker-compose.yml
ansible.builtin.template:
src: docker-compose.yml.j2
dest: /opt/myapp/docker-compose.yml
mode: "0644"
- name: Start services
community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
pull: always
Build Images
# Build from Dockerfile
- name: Build app image
community.docker.docker_image:
name: myapp
tag: "{{ app_version }}"
source: build
build:
path: /opt/myapp
dockerfile: Dockerfile
args:
APP_VERSION: "{{ app_version }}"
pull: true
force_source: true
# Build and push
- name: Build and push
community.docker.docker_image:
name: myregistry.com/myapp
tag: "{{ app_version }}"
source: build
build:
path: /opt/myapp
push: true
Practical Example: Full Application Stack
---
- name: Deploy application stack
hosts: appservers
become: true
vars:
app_version: "2.1.0"
db_password: "{{ vault_db_password }}"
app_domain: app.example.com
tasks:
- name: Create Docker network
community.docker.docker_network:
name: app-net
- name: Create volumes
community.docker.docker_volume:
name: "{{ item }}"
loop: [pgdata, redis-data, app-uploads]
- name: Run PostgreSQL
community.docker.docker_container:
name: postgres
image: postgres:16-alpine
networks: [{name: app-net}]
env:
POSTGRES_DB: myapp
POSTGRES_USER: myapp
POSTGRES_PASSWORD: "{{ db_password }}"
volumes:
- pgdata:/var/lib/postgresql/data
restart_policy: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myapp"]
interval: 10s
timeout: 5s
retries: 5
- name: Run Redis
community.docker.docker_container:
name: redis
image: redis:7-alpine
networks: [{name: app-net}]
volumes:
- redis-data:/data
restart_policy: unless-stopped
- name: Run application
community.docker.docker_container:
name: myapp
image: "myregistry.com/myapp:{{ app_version }}"
pull: true
networks: [{name: app-net}]
ports:
- "8080:8080"
env:
DATABASE_URL: "postgresql://myapp:{{ db_password }}@postgres:5432/myapp"
REDIS_URL: "redis://redis:6379"
APP_ENV: production
volumes:
- app-uploads:/app/uploads
restart_policy: unless-stopped
- name: Run nginx reverse proxy
community.docker.docker_container:
name: nginx
image: nginx:alpine
networks: [{name: app-net}]
ports:
- "80:80"
- "443:443"
volumes:
- /opt/nginx/conf.d:/etc/nginx/conf.d:ro
- /etc/letsencrypt:/etc/letsencrypt:ro
restart_policy: unless-stopped
Container Management
# Stop a container
- community.docker.docker_container:
name: myapp
state: stopped
# Remove a container
- community.docker.docker_container:
name: myapp
state: absent
# Restart a container
- community.docker.docker_container:
name: myapp
state: started
restart: true
# Execute command in running container
- name: Run migrations
community.docker.docker_container_exec:
container: myapp
command: python manage.py migrate
# Prune unused resources
- name: Prune images
community.docker.docker_prune:
images: true
images_filters:
dangling: true
volumes: true
Troubleshooting
"Cannot connect to the Docker daemon"
The Ansible user needs Docker access:
- name: Add user to docker group
ansible.builtin.user:
name: "{{ ansible_user }}"
groups: docker
append: true
Container Keeps Recreating
Ansible compares container config. If anything differs, it recreates. Use comparisons to control this:
- community.docker.docker_container:
name: myapp
image: myapp:latest
comparisons:
image: ignore # Don't recreate just because image changed
env: strict # Recreate if env changes
Related Articles
Conclusion
The community.docker collection gives full Docker control from Ansible playbooks. Use docker_container for individual containers, docker_compose_v2 for multi-container stacks, and docker_image for building and pulling images. Combine with Ansible's strengths — variables, vault encryption, inventory management — to deploy containerized apps across environments. Always use named volumes for persistent data, create custom networks for service discovery, and set restart_policy for production containers.