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 docker SDK (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

ParameterDescription
nameContainer name (required)
imageDocker image with tag
statestarted, stopped, absent, present
portsPort mappings (host:container)
volumesVolume mounts (host:container[:mode])
envEnvironment variables (dict)
restart_policyno, always, unless-stopped, on-failure
network_modebridge, host, none, or network name
networksList of networks to connect to
commandOverride container command
entrypointOverride entrypoint
memoryMemory limit (512m, 1g)
cpusCPU limit
labelsContainer labels (dict)
pullPull policy: always, missing, never
recreateForce recreate even if running
comparisonsControl 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

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.