Skip to content
Go back

Automating Docker via Ansible

· Updated:
By SumGuy 6 min read
Automating Docker via Ansible

As containerized applications continue to gain popularity, the need for efficient deployment and management solutions becomes essential. Ansible, a powerful IT automation engine, offers simplicity, repeatability, and scalability to manage complex deployments effectively. This article dives into using Ansible for automating Docker container deployments and management, providing practical examples through Ansible playbooks.

Introduction to Ansible for Docker Management

Ansible can configure systems, deploy software, and orchestrate more advanced IT tasks such as continuous deployments or zero downtime rolling updates. When paired with Docker, Ansible simplifies and automates the provisioning and management of containers, ensuring that the environment is exactly as you’ve defined it.

Key Benefits:

Setting Up Your Environment

Before diving into the Ansible playbooks, ensure your environment is set up with both Docker and Ansible installed. You can install Docker from its official pageofficial page and Ansible by following the instructions on the Ansible documentationAnsible documentation.

Example 1: Deploying a Simple Web Application

The following playbook demonstrates deploying a simple Dockerized web application across multiple hosts. It pulls an image from Docker Hub, creates a new container, and ensures it’s running.

Playbook: deploy_web_app.yml

- name: Deploy Web Application
hosts: web_servers
become: true
tasks:
- name: Pull the latest Docker image
docker_image:
name: nginx
source: pull
- name: Start container
docker_container:
name: my_nginx
image: nginx
state: started
published_ports:
- "80:80"

This playbook targets hosts grouped under web_servers. It first pulls the latest nginx image from Docker Hub, then starts a container named my_nginx, mapping port 80 on the host to port 80 in the container.

Example 2: Rolling Update for a Microservice

This example demonstrates a zero-downtime rolling update, which is crucial for production environments. It pulls a newer version of an image and progressively replaces the older containers.

Playbook: rolling_update.yml

- name: Rolling update of app
hosts: app_servers
become: true
serial: 1 # Update one server at a time
tasks:
- name: Pull the updated Docker image
docker_image:
name: my_app:latest
source: pull
- name: Stop the old container
docker_container:
name: my_app_container
state: absent
- name: Start new container
docker_container:
name: my_app_container
image: my_app:latest
state: started
published_ports:
- "8080:8080"
env:
NODE_ENV: production

The playbook updates app_servers one at a time (serial: 1), reducing the risk of downtime. It ensures the application is always available even during the update process.

Advanced Configuration using Ansible Roles

For more complex deployments, you can organize your Ansible playbooks into roles. Roles are a way to group tasks, handlers, files, templates, and variables into a clean, reusable structure.

Example of a Role Directory Structure for Docker

roles/
docker_deploy/
tasks/
main.yml
handlers/
main.yml
templates/
nginx.conf.j2
vars/
main.yml

This structure helps maintain organization and clarity, especially as your automation requirements grow.

Conclusion

By integrating Ansible with Docker, developers and system administrators can automate the deployment and management of containerized applications efficiently and reliably. Whether deploying a simple web service or managing complex updates in production, Ansible offers the tools and flexibility needed to handle various scenarios effectively. With the help of practical examples and organized roles, you can leverage the power of Ansible in your containerized environments, ensuring that your applications are always deployed consistently and correctly.

What Actually Breaks This (And How to Fix It)

Roles and playbooks are great — right up until they aren’t. Here are the things that will bite you before you get to “consistently and correctly.”

The community.docker Collection Isn’t Installed

The docker_container and docker_image modules don’t ship with Ansible core anymore. They live in the community.docker collection, and if you just pip-installed Ansible fresh, they’re not there. You’ll get a cryptic “module not found” error and spend 20 minutes questioning your life choices.

Fix it once:

Terminal window
ansible-galaxy collection install community.docker

Add a requirements.yml to your project root so anyone else who clones your repo doesn’t hit the same wall:

requirements.yml
collections:
- name: community.docker
version: ">=3.0.0"

Then ansible-galaxy collection install -r requirements.yml and you’re done.

The Docker Socket Permissions Trap

Your playbook runs, Ansible connects fine, become: true is set — and you still get “permission denied” on the Docker socket. What gives?

The become escalation runs the Ansible task as root, but if Docker on your host is configured to use a Unix socket owned by a docker group, and your playbook is doing something weird with connection delegation, you can end up with a mismatch. The clean fix is to make sure your Ansible remote user is in the docker group on each target host:

Terminal window
sudo usermod -aG docker your_ansible_user
# Log out and back in, or:
newgrp docker

If you’re managing the hosts with Ansible anyway, just add a task to your initial provisioning playbook. Don’t let this be a manual step you forget every time you spin up a new node.

Stale Images After a “Pull”

Here’s a subtle one: you run your deploy playbook, the docker_image task reports ok (not changed), and you assume the image is current. It’s not. docker_image with source: pull only reports changed if the image digest actually changed — but if the registry layer cache is warm and Docker thinks it already has the image, it’ll happily skip the pull.

The safer pattern is to force-pull and tie the container task to it using force_source:

roles/docker_deploy/tasks/main.yml
- name: Pull latest image (force refresh)
community.docker.docker_image:
name: "{{ app_image }}"
source: pull
force_source: true
register: image_pull
- name: Recreate container if image changed
community.docker.docker_container:
name: "{{ app_container_name }}"
image: "{{ app_image }}"
state: started
recreate: "{{ image_pull.changed }}"
restart_policy: unless-stopped

The recreate flag tied to image_pull.changed means the container only bounces when something actually updated. Your 2 AM self will appreciate not getting paged because a routine deploy restarted a healthy container for no reason.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
LiteLLM & vLLM: One API to Rule All Your Models
Next Post
MinIO Is Archived: Move to Garage

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts