Automated Docker Engine Deployment with Ansible: The Reliable Path
Manual Docker installation across multiple environments inevitably leads to drift—dependencies missed on some nodes, obsolete package caches, or version mismatches. A single, idempotent Ansible playbook eliminates these inconsistencies and delivers predictable results on every server, regardless of scale.
Below is a minimal, production-oriented playbook for Ubuntu 20.04 LTS and 22.04 LTS hosts. This approach uses native package management (APT), handles GPG keys securely, and configures user permissions to drop the requirement for sudo
in daily Docker usage.
Core Advantages of Ansible-based Docker Provisioning
- Uniformity: Aligns package versions and configurations at scale.
- Idempotence: Repeated runs introduce no side effects or cumulative changes.
- Recoverability: Cleanly recreate or recover node states via playbook replay.
- Parallelization: Scale from a single test VM to a full production cluster with negligible effort.
- Update Simplicity: Centralized control for Docker upgrades, rollbacks, and configuration changes.
Prerequisites
- Control machine running Ansible (tested with Ansible ≥2.10).
- Inventory with SSH connectivity to Ubuntu targets (20.04 or 22.04).
- Sudo-enabled user on each managed host.
- At least 2GB RAM and ≥10GB free disk on target systems (production containers may require more).
Example Playbook: install-docker.yml
---
- name: Docker Engine installation and configuration (Ubuntu)
hosts: all
become: true
tasks:
- name: Refresh apt cache (always)
ansible.builtin.apt:
update_cache: yes
cache_valid_time: 3600
- name: Install prerequisite packages for APT over HTTPS
ansible.builtin.apt:
name:
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
- name: Ensure /etc/apt/keyrings directory exists
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: '0755'
- name: Add Docker’s official GPG key (saved in /etc/apt/keyrings)
ansible.builtin.get_url:
url: https://download.docker.com/linux/ubuntu/gpg
dest: /etc/apt/keyrings/docker.gpg
mode: '0644'
register: docker_gpg_key
# Prevent repeated re-adding of the apt source file
- name: Add Docker repository for current Ubuntu release
ansible.builtin.apt_repository:
repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_lsb.codename }} stable"
filename: docker
state: present
- name: Update apt cache after adding Docker repo
ansible.builtin.apt:
update_cache: yes
- name: Install Docker Engine and CLI (latest stable)
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
state: latest
- name: Ensure Docker is started and enabled
ansible.builtin.systemd:
name: docker
state: started
enabled: true
- name: Add main user to docker group (skip root)
ansible.builtin.user:
name: "{{ ansible_user_id }}"
groups: docker
append: true
when: ansible_user_id != 'root'
Note: If you see E: Unable to locate package docker-ce
, double-check the repository line—{{ ansible_lsb.codename }}
must resolve (e.g., focal
for Ubuntu 20.04, jammy
for 22.04).
How to Apply the Playbook
- Populate your Ansible inventory
Example hosts
file:
[docker_hosts]
192.168.2.10
192.168.2.11
- Run the playbook (from your Ansible control node):
ansible-playbook -i hosts install-docker.yml
Expect standard Ansible output. On failure, typical SSH-related permission errors look like:
fatal: [192.168.2.10]: FAILED! => {"msg": "Authentication or permission failure...", ...}
Verification Steps
On any managed host, verify Docker:
docker --version # Expect: Docker version 24.0.x, build ...
docker run --rm hello-world
Output should include:
Hello from Docker!
This message shows that your installation appears to be working correctly.
If you observe Cannot connect to the Docker daemon
, the user may need to log out and back in for docker
group changes to apply.
Beyond the Basics
- Non-root execution: This configuration enables the specified user to run Docker commands sans
sudo
. Security policy may require more granular user management; consult your compliance guidelines. - Version pinning: In production, pin Docker versions for stability (e.g.,
docker-ce=5:20.10.21~3-0~ubuntu-focal
). Be prepared to update pins regularly as CVEs emerge. - Compose and plugins: Extend with
docker-compose-plugin
or additional Ansible roles, e.g.,geerlingguy.docker
, for enterprise workloads. - OS variants: For RHEL/CentOS, substitute
yum
/dnf
modules and maintain separate repo setup blocks. - Idempotence gotcha: If the playbook is interrupted during package install, re-running is safe. However, repository GPG key changes (rare) may require repo cache purges or manual intervention.
Practical Considerations
- System resource limits: Default Docker installations do not set cgroup or ulimit parameters; for high-density workloads, tune
/etc/docker/daemon.json
. - Firewalls: Docker manipulates iptables. If you're using UFW or custom firewalls, review rules post-install.
- Uninstallation: Ansible’s
apt
task can remove Docker—but dangling volumes and/var/lib/docker
content remain unless explicitly wiped.
Example: Custom Daemon Configuration
To configure Docker with an internal registry mirror and log driver, add a task:
- name: Configure Docker daemon.json
ansible.builtin.copy:
dest: /etc/docker/daemon.json
content: |
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" },
"registry-mirrors": ["https://my.mirror.local"]
}
notify: Restart Docker
handlers:
- name: Restart Docker
ansible.builtin.systemd:
name: docker
state: restarted
In Summary
Ansible-based Docker installation ensures uniform environments and eliminates fragile, error-prone manual steps—essential for resilient build or test server fleets. While production deployments may justify more advanced playbook modularization (roles, tags, molecule tests), the above template is robust for both exploratory and real-world adoption.
There are alternative approaches—cloud-native container services, baked-in machine images—but direct Ansible automation remains broadly compatible and infrastructure-agnostic.
Side note: For air-gapped environments, pre-stage the Docker deb packages and adapt installation to use apt
’s deb
parameter.