Recommended Free Tools
The reliable pattern is to let Ansible configure Docker from Docker’s official repository, install the Engine and its companion packages, enable the service, install the community.docker collection and Python SDK in the correct execution context, then manage containers declaratively. The playbook below separates Debian-family and RHEL-family tasks, verifies the daemon with a disposable test container, and includes the firewall, version-pinning and troubleshooting details that commonly determine whether a rollout is safe.
Antihost offers VPS plans with one-click Docker, NVMe storage and a 14-day free trial.
What this setup produces
After a successful run, each target server has Docker Engine installed from a repository you control, the daemon enabled at boot and running now, and an Ansible environment capable of calling Docker’s API. You can then create and update containers with community.docker.docker_container or deploy a multi-container application with the collection’s Compose v2 modules.
As an Amazon Associate I earn from qualifying purchases.
Free tools Windows power users keep installed
One-click scans. No signup required.
- Repository and signing-key configuration is repeatable.
- The packages
docker-ce,docker-ce-cli,containerd.io,docker-buildx-pluginanddocker-compose-pluginare installed. - The service is enabled and started through Ansible rather than an ad-hoc shell command.
- A
hello-worldrun confirms that the daemon can pull and start an image. - Collection, Python-SDK and interpreter requirements are explicit.
Check support before writing tasks
Docker publishes a Linux support matrix because repository paths, package names and CPU architectures differ by distribution. Confirm the exact distribution release and architecture reported by each host before applying a play. Debian’s current installation guidance lists Debian Trixie 13 and Bookworm 12; Docker’s RHEL guidance lists maintained RHEL 8, 9 and 10. Derivatives may need the codename of their upstream release rather than the derivative’s own name.
| Target family | Package tool | Repository workflow | Important qualification |
|---|---|---|---|
| Debian and Ubuntu | apt |
Install the keyring, add an apt source, refresh metadata, then install Docker packages. | Use the codename and architecture accepted by Docker’s current Linux support matrix. Derivatives can require a matching upstream codename. |
| RHEL | dnf |
Add Docker’s RPM repository, refresh metadata and install the Engine package set. | The current Docker manual covers RHEL 8, 9 and 10; verify architecture support for your release. |
| Other Linux distributions | Distribution-specific | Keep repository URLs, package names and architecture mappings in variables. | Do not assume that a Debian or RHEL task applies to another distribution. |
Use facts gathered by Ansible to fail early when a host is outside your tested matrix. For example:
#1 Best Overall
- name: Stop on unsupported operating systems
ansible.builtin.assert:
that:
- ansible_facts.os_family in ['Debian', 'RedHat']
fail_msg: 'This play supports only Debian-family and RHEL-family hosts.'
- name: Display release and architecture for review
ansible.builtin.debug:
msg:
- 'Distribution: {{ ansible_facts.distribution }} {{ ansible_facts.distribution_version }}'
- 'Release codename: {{ ansible_facts.lsb.codename | default("not reported") }}'
- 'Architecture: {{ ansible_facts.architecture }}'
Map every architecture explicitly in your variables. Support varies by platform, so an x86_64 value that works on one release is not evidence that an ARM value is available on another.
Choose repository and version policies
Official repository versus distribution packages
Docker’s repository installation is the documented path for ongoing installation and update workflows. Distribution repositories can be older or split the package set differently. If you intentionally use distribution packages, change the package list and validation to match that distribution; do not mix packages from both sources on one host.
Latest packages versus pinned packages
An unqualified package state such as present follows whatever version the configured repository currently offers. That is convenient for a new server but can introduce an unplanned Engine or containerd change. For reproducible environments, pin package versions in variables, record the chosen versions in inventory or group variables, and upgrade them in a scheduled change. A pin applies only when that exact version is still available in the repository.
Repository values as variables
Keep the repository line, key URL, keyring path, architecture mapping and release name in variables rather than embedding them throughout tasks. Populate those values from Docker’s current installation page for the target release. This avoids silently using an obsolete codename or repository layout when a distribution moves to a new release.
docker_apt_repo_line: "deb [arch={{ docker_apt_arch }} signed-by=/etc/apt/keyrings/docker.asc] {{ docker_apt_repository }} {{ docker_apt_release }} stable"
docker_apt_key_url: '{{ docker_key_url_from_current_docker_documentation }}'
docker_rpm_repository: '{{ docker_rpm_repo_from_current_docker_documentation }}'
docker_package_state: present
The double-curly-brace values above are Ansible variables, not literal strings to pass to a package manager. Define them in inventory or group variables for each supported release.
Prepare the Ansible control environment
Inventory and privilege escalation
Place target addresses and the SSH user in inventory, and use privilege escalation for repository, package and service tasks. A minimal inventory can look like this:
[docker_hosts]
node01 ansible_host=203.0.113.21
node02 ansible_host=203.0.113.22
[docker_hosts:vars]
ansible_user=admin
ansible_become=true
Use a dedicated administrative account with a narrowly managed SSH key. Confirm that Python is available on each target because Ansible modules execute there unless you deliberately use a different connection or interpreter.
Collection and core-version prerequisites
The Docker modules are not part of ansible-core. Install the separate collection before running a play:
ansible-galaxy collection install community.docker
Current community.docker documentation identifies collection version 5.4.0 and requires ansible-core 2.17.0 or newer. Check the installed versions in your execution environment and pin the collection in a requirements.yml file when repeatability matters:
collections:
- name: community.docker
version: 5.4.0
Use a virtual environment or an execution image so that the collection version and its dependencies are the same in CI and on an operator’s workstation.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #2
Install the Docker SDK for Python in the right place
Most community.docker modules require the Docker SDK for Python on the machine where the module runs. With normal SSH execution, that is the target server, and the SDK must be installed into the Python interpreter selected by ansible_python_interpreter. Ansible plugins run in Ansible’s own context, so a plugin can instead require the SDK on the controller.
Make this explicit in inventory when a host has more than one Python installation:
docker_hosts:
hosts:
node01:
ansible_python_interpreter: /usr/bin/python3
Install the SDK using the package-management method approved for your operating system or a controlled virtual environment. Avoid installing it into one interpreter while Ansible invokes another; a module failure that says the SDK is missing is often an interpreter mismatch rather than a Docker problem.
Install Docker on Debian and Ubuntu
The Debian-family branch follows Docker’s documented order: remove conflicting packages when present, create /etc/apt/keyrings, install the signing key with restrictive permissions, add the repository, refresh apt metadata, install the five Docker packages, and enable the service.
1. Remove conflicting packages deliberately
Only remove packages that Docker identifies as conflicting for the release you are deploying. Keep this list in a variable so a host-specific exception is visible during review:
- name: Remove packages that conflict with Docker Engine
ansible.builtin.apt:
name: '{{ docker_conflicting_packages }}'
state: absent
purge: true
update_cache: false
when:
- ansible_facts.os_family == 'Debian'
- docker_conflicting_packages | length > 0
Do not use a broad package purge pattern. Removing an unrelated container runtime can interrupt existing workloads, so drain or migrate those workloads before applying this task.
2. Create the keyring and repository
- name: Install repository prerequisites
ansible.builtin.apt:
name:
- ca-certificates
- curl
state: present
update_cache: true
- name: Create apt keyring directory
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
owner: root
group: root
mode: '0755'
- name: Download Docker signing key
ansible.builtin.get_url:
url: '{{ docker_apt_key_url }}'
dest: /etc/apt/keyrings/docker.asc
owner: root
group: root
mode: '0644'
- name: Configure Docker apt repository
ansible.builtin.apt_repository:
repo: '{{ docker_apt_repo_line }}'
filename: docker
state: present
update_cache: true
The keyring path and permissions matter: apt must be able to read the key, while the file remains owned by root. Ensure the release and architecture in docker_apt_repo_line match the host facts you validated earlier.
3. Install Engine, CLI, containerd and plugins
- name: Install Docker Engine and plugins
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: '{{ docker_package_state }}'
update_cache: true
If you pin versions, assign each package its exact repository version instead of assuming all five packages share the same version string. Keep the selected versions together in variables so upgrades are intentional.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 114. Enable and start the daemon
- name: Enable and start Docker
ansible.builtin.service:
name: docker
enabled: true
state: started
Install Docker on RHEL
RHEL uses the same high-level sequence but with dnf and Docker’s RPM repository. The current Docker documentation covers RHEL 8, 9 and 10; verify the exact repository path and architecture before rollout.
1. Remove conflicting packages and add the RPM repository
- name: Remove conflicting RPM packages
ansible.builtin.dnf:
name: '{{ docker_conflicting_packages }}'
state: absent
when:
- ansible_facts.os_family == 'RedHat'
- docker_conflicting_packages | length > 0
- name: Configure Docker RPM repository
ansible.builtin.yum_repository:
name: docker-ce-stable
description: Docker CE Stable
baseurl: '{{ docker_rpm_repository }}'
enabled: true
gpgcheck: true
gpgkey: '{{ docker_rpm_key_url }}'
state: present
Use the repository and key values published for your RHEL release. A repository task is idempotent: it changes the file only when its declared content differs.
Rank #3
2. Install packages and start Docker
- name: Install Docker Engine and plugins on RHEL-family hosts
ansible.builtin.dnf:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: '{{ docker_package_state }}'
update_cache: true
- name: Enable and start Docker on RHEL-family hosts
ansible.builtin.service:
name: docker
enabled: true
state: started
If your organization uses a mirror, preserve signature verification and document the mirror’s synchronization policy. Do not silently fall back to an unverified package source.
Verify the installation
Check service state on the host
Run the equivalent checks manually when diagnosing a failed play:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
systemctl status docker
systemctl is-enabled docker
The first command should show an active service; the second should report that it is enabled for boot. If the service is inactive, inspect the unit log before rerunning Ansible:
journalctl -u docker --no-pager -n 100
Run Docker’s disposable test container
docker run hello-world
Docker states that this command downloads a test image, creates and runs a container, prints a confirmation message and exits. It tests image retrieval, the daemon, container creation and basic runtime execution without leaving a long-running workload.
You can make the check part of the play while preserving a useful failure message:
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minute- name: Run Docker smoke test
ansible.builtin.command: docker run --rm hello-world
register: docker_hello_world
changed_when: true
- name: Show Docker smoke-test output
ansible.builtin.debug:
var: docker_hello_world.stdout
Use --rm for a clean test container. A failure to pull points toward DNS, proxy or registry egress; a failure to connect points toward the service, socket permissions or the selected Python/API context.
Manage containers with Ansible
Use community.docker.docker_container for individual services. The module supports check mode and requires Docker API 1.25 or newer. Declare the desired state instead of scripting a sequence of docker run and docker rm commands.
- name: Run an example web service
community.docker.docker_container:
name: example-web
image: nginx:stable
state: started
restart_policy: unless-stopped
published_ports:
- '8080:80'
pull: true
Run the play in check mode before changing a production host:
ansible-playbook -i inventory site.yml --check --diff
Check mode predicts changes but cannot validate every runtime behavior. Follow it with a controlled apply and inspect the resulting container:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →docker ps
docker inspect example-web
Keep image tags and environment values in variables. For production, prefer a reviewed immutable image reference or digest, define resource and health settings appropriate to the workload, and decide how secrets are injected rather than placing credentials directly in a playbook.
Rank #4
Use Compose modules for multi-container applications
A database, application and reverse proxy that share networks and volumes are a multi-container workload. Use the collection’s Compose v2 modules for that relationship instead of creating each container independently and attempting to reproduce networking by hand. Keep the Compose definition in source control, pass only the intended project directory, and run it through Ansible so project state is repeatable.
Choose individual container tasks when services have independent lifecycles or when you need fine-grained per-container checks. Choose Compose when the application’s value comes from its declared networks, volumes, dependencies and coordinated configuration.
Make the playbook safe to rerun
- Use declarative modules for files, repositories, packages, services and containers. Avoid shell commands except for a narrowly defined smoke test or an operation with no suitable module.
- Set repository files and keyring permissions explicitly so drift is corrected on the next run.
- Keep package and collection versions in variables or requirements files when reproducibility matters.
- Run check mode in CI, then apply to a disposable host before production.
- Separate installation from workload deployment with tags such as
docker_installandcontainers. - Schedule upgrades deliberately. Test Engine, containerd, Compose and image changes together when compatibility is important.
A second run should report no changes for already-correct repository, package, service and container state. Unexpected changes usually indicate an unpinned repository, a changing image tag, an interpreter mismatch or a task that is not idempotent.
Firewall and port-exposure security
Docker warns that published container ports can bypass host rules managed only through uncomplicated firewall or firewalld abstractions. Before publishing a port, review the host firewall policy and the path Docker uses for packet filtering. Place custom filtering in the supported iptables or ip6tables path, including the DOCKER-USER chain where appropriate, rather than editing Docker-generated chains that the daemon may rewrite.
- Publish only ports that clients genuinely need.
- Bind an application to a specific host address when it must not listen on every interface.
- Restrict administrative ports at the network edge and in host policy.
- Test access from an untrusted network, not only from localhost.
- Document every published port in the same variables that define the container.
A successful docker run does not prove that exposure is safe; it proves only that the daemon can start the container.
Troubleshoot common failures
Repository or key errors
If apt reports that a repository has no installation candidate, check the release codename, architecture selector, keyring path and repository file content. On RHEL, inspect the enabled repository and its GPG configuration. A derivative distribution may need its upstream codename, as Docker’s Debian instructions note.
Package conflicts
List installed container-runtime packages before removing anything. Stop dependent workloads, remove only the documented conflicts, refresh metadata and rerun the package task. Do not purge a package merely because its name resembles a Docker component.
Daemon starts and then exits
Read journalctl -u docker and verify storage availability, daemon configuration syntax and the containerd package. A service task can be correct while a local configuration file prevents startup.
“Docker SDK is not installed”
Print the interpreter Ansible selected, install the SDK into that interpreter and rerun a module in verbose mode. Remember that normal modules execute on targets, while plugins use Ansible’s own context.
API-version or module errors
Confirm that the Docker API exposed by the daemon is at least 1.25 and that the installed collection matches your ansible-core version. Reinstall the pinned collection in the same environment that runs the playbook.
Container starts but is unreachable
Inspect the published-port mapping, container logs, listening address inside the container and host firewall rules. A port can be correctly published yet blocked by policy, or the process can be listening only on loopback inside the container.
Free tools Windows power users keep installed
One-click scans. No signup required.
Operational checklist
- Record each host’s distribution release, architecture and intended repository values.
- Confirm that the release appears in Docker’s current support documentation.
- Install and pin the
community.dockercollection; verifyansible-core2.17.0 or newer for the current collection documentation. - Install the Docker SDK for Python in the interpreter used by target-side modules.
- Run the Debian-family or RHEL-family repository tasks, never both on one host.
- Install
docker-ce,docker-ce-cli,containerd.io,docker-buildx-pluginanddocker-compose-plugin. - Enable and start the service, then verify with
systemctl status docker. - Run
docker run hello-worldas the smoke test. - Apply container definitions in check mode, review changes, then deploy.
- Review every published port against the host firewall and the
DOCKER-USERpolicy. - Record package, collection and image versions, and upgrade them through a planned change.
Final decision
Ansible is a strong fit for Docker server setup when repository configuration, package state, service lifecycle, Python dependencies and workloads are all declared in one repeatable workflow. Use the official repository branch that matches the host family, validate release and architecture first, keep versions intentional, and treat published ports as a firewall design decision rather than a container-only setting.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

