#Infrastructure

How to install docker using ansible on a Ubuntu Server

How to install docker using ansible?

If you manage more than one server, installing Docker manually becomes a repetitive (and error-prone) checklist. This tutorial shows how to install docker using ansible on an Ubuntu Server by creating a reusable role that follows Docker’s official Ubuntu installation steps: configure the official APT repository, install the latest packages, and verify everything works.

We’ll start from zero: install Ansible on your local machine, set up a basic inventory, create a role, and run it against a remote Ubuntu host.

TL;DR (what you’ll build)

  • An Ansible role named docker that:
    • Installs prerequisites
    • Adds Docker’s official GPG key (keyring)
    • Adds Docker’s official APT repository (based on distro codename)
    • Installs the latest docker-ce, containerd.io, and plugins via apt
    • Ensures the Docker service is enabled and running
    • Optionally adds your user to the docker group
  • A playbook you can reuse across environments.

Prerequisites

  • A local machine (Linux/macOS recommended) with:
    • Python 3
    • SSH client
  • An Ubuntu Server (20.04/22.04/24.04) reachable via SSH
  • A user on the server with sudo privileges

Note: This guide uses the official Docker APT repo approach (recommended) instead of Ubuntu’s default docker.io package, which is often behind on versions.

Quick intro: what Ansible is (in practical terms)

Ansible is an automation tool that runs tasks over SSH to configure servers. You describe the desired state (packages installed, services running, files present), and Ansible makes it happen in an idempotent way, meaning you can run it multiple times and it won’t keep changing things unnecessarily.

For this tutorial, Ansible will:

  • Connect to your Ubuntu host via SSH
  • Configure Docker’s official repository and keyring
  • Install Docker packages via apt

Step 1: Install Ansible on your local machine

Option A: Install with pip (works almost everywhere)

Install Ansible with pip (local user) and verify
python3 -m pip install --user ansible

# Verify
ansible --version
Bash

Option B: Install on Ubuntu/Debian via APT

Install Ansible using apt
sudo apt update
sudo apt install -y ansible

ansible --version
Bash

Either option is fine. Although, if you want more predictable versions across machines/CI, pip tends to be easier to standardize.

Step 2: Create a minimal Ansible project structure

Create a folder for your project:

Create a simple Ansible project layout
mkdir -p ansible-docker-ubuntu && cd ansible-docker-ubuntu
mkdir -p roles group_vars
Bash

Create an inventory file (inventory.ini):

Example inventory.ini
cat > inventory.ini <<'EOF'
[docker_hosts]
ubuntu1 ansible_host=203.0.113.10 ansible_user=ubuntu
EOF
Bash

Replace:

  • 203.0.113.10 with your server IP
  • ansible_user with your SSH username (common values: ubuntu, ec2-user, root)

SSH access check

Before doing anything with Ansible, make sure SSH works:

Sanity check: SSH into the server
ssh [email protected]
Bash

If you need to use an SSH key explicitly, you can do it in Ansible too:

Inventory using a specific SSH key
cat > inventory.ini <<'EOF'
[docker_hosts]
ubuntu1 ansible_host=203.0.113.10 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa
EOF
Bash

Step 3: Bootstrap Ansible config (recommended)

Create an ansible.cfg in the project root to avoid repeating flags:

ansible.cfg
cat > ansible.cfg <<'EOF'
[defaults]
inventory = inventory.ini
host_key_checking = False
interpreter_python = auto_silent

[privilege_escalation]
become = True
become_method = sudo
become_ask_pass = False
EOF
INI

Gotcha: Disabling host_key_checking is convenient for tutorials and ephemeral hosts. In production, you may want it enabled to prevent MITM-style issues.

Step 4: Create the Docker installation role

Generate the role skeleton:

Create a role scaffold
ansible-galaxy role init roles/docker
Bash

We’ll implement the tasks in roles/docker/tasks/main.yml and keep variables in roles/docker/defaults/main.yml.

Role defaults (customizable variables)

Edit roles/docker/defaults/main.yml:

roles/docker/defaults/main.yml
# roles/docker/defaults/main.yml

# Add a user to the docker group (so they can run docker without sudo)
docker_add_user_to_group: true

docker_user: "{{ ansible_user | default('ubuntu') }}"

# Docker packages from the official repository
docker_packages:
  - docker-ce
  - docker-ce-cli
  - containerd.io
  - docker-buildx-plugin
  - docker-compose-plugin
YAML

Main tasks: follow Docker’s official repo steps

Now the important part: implement roles/docker/tasks/main.yml to match the official documentation flow.

roles/docker/tasks/main.yml (install Docker using official repo)
---
# roles/docker/tasks/main.yml

- name: Ensure APT cache is updated
  ansible.builtin.apt:
    update_cache: true
    cache_valid_time: 3600

- name: Install packages required to use Docker's APT repository
  ansible.builtin.apt:
    name:
      - ca-certificates
      - curl
      - gnupg
    state: present

- name: Ensure /etc/apt/keyrings exists
  ansible.builtin.file:
    path: /etc/apt/keyrings
    state: directory
    mode: "0755"

- name: Download Docker official GPG key
  ansible.builtin.get_url:
    url: https://download.docker.com/linux/ubuntu/gpg
    dest: /etc/apt/keyrings/docker.asc
    mode: "0644"

- name: Get dpkg architecture (amd64, arm64, ...)
  ansible.builtin.command: dpkg --print-architecture
  register: dpkg_arch
  changed_when: false

- name: Add Docker official APT repository
  ansible.builtin.apt_repository:
    repo: >-
      deb [arch={{ dpkg_arch.stdout }} signed-by=/etc/apt/keyrings/docker.asc]
      https://download.docker.com/linux/ubuntu
      {{ ansible_distribution_release }} stable
    state: present
    filename: docker

- name: Install Docker Engine from official repository
  ansible.builtin.apt:
    name: "{{ docker_packages }}"
    state: latest
    update_cache: true

- name: Ensure Docker service is enabled and started
  ansible.builtin.service:
    name: docker
    state: started
    enabled: true

- name: Add user to docker group (optional)
  ansible.builtin.user:
    name: "{{ docker_user }}"
    groups: docker
    append: true
  when: docker_add_user_to_group | bool

- name: Verify docker works (docker version)
  ansible.builtin.command: docker version
  register: docker_version
  changed_when: false

- name: Show docker version output
  ansible.builtin.debug:
    var: docker_version.stdout_lines
YAML

Why this approach? It mirrors the official steps: install prerequisites, place the key in /etc/apt/keyrings, configure the repo with signed-by, then install packages with APT.

Common pitfall: After adding the user to the docker group, the user must log out and back in (or you need to start a new SSH session) for group changes to apply.

Step 5: Create the playbook that uses the role

Create playbook.yml in the project root:

playbook.yml
---
# playbook.yml

- name: Install Docker on Ubuntu hosts
  hosts: docker_hosts
  become: true
  roles:
    - docker
YAML

Step 6: Run the playbook

Run it:

Run the Docker install playbook
ansible-playbook playbook.yml
Bash

If your sudo requires a password, you can run:

If sudo requires a password
ansible-playbook playbook.yml --ask-become-pass
Bash

Step 7: Validate on the server (real-world verification)

SSH into the host and run:

Validate Docker installation
docker --version
sudo systemctl status docker --no-pager

# Optional: quick test container
sudo docker run --rm hello-world
Bash

If you enabled the “add user to docker group” step, you can test without sudo after reconnecting:

Reconnect so docker group membership applies
exit
ssh [email protected]

docker ps
Bash

Best practices (things that save you later)

Pin versions only if you must

This role installs state: latest, which is great for keeping systems updated, but it can surprise you if a newer Docker release changes behavior. If you need stricter control, pin versions (or manage updates with a controlled pipeline).

Don’t disable host key checking in production blindly

For real environments, keep SSH host key checking enabled and manage known_hosts properly. It’s a real security control.

Prefer SSH keys and least privilege

  • Use SSH keys instead of passwords
  • Use a non-root user with sudo
  • Limit which hosts are targeted by inventory groups

Troubleshooting

“Permission denied” when running docker without sudo

  • Make sure the user was added to the docker group
  • Reconnect the SSH session (group changes require a new login)
  • Check with: id and confirm docker is listed

APT repo added but packages don’t update

  • Verify the repo file exists: /etc/apt/sources.list.d/docker.list
  • Verify the keyring exists: /etc/apt/keyrings/docker.asc
  • Run: sudo apt update and inspect errors

Architecture/codename mismatch

This role uses:

  • dpkg --print-architecture for arch=
  • {{ ansible_distribution_release }} for the Ubuntu codename (like jammy, focal)

If your host is not Ubuntu (or uses something unusual), the repo string needs adjustment.

Reference links

Conclusion

You now have a clean, reusable, and practical answer to how to install docker using ansible on Ubuntu Server. The key is using Docker’s official APT repository (not the distro package), and wrapping the steps in an idempotent role you can apply to any host group.

Next steps you can add without complicating the role too much:

  • Install and configure UFW rules for published ports
  • Configure Docker daemon options (/etc/docker/daemon.json)
  • Add a separate role for deploying your containers (Compose, Swarm, or Kubernetes)

How to install docker using ansible on a Ubuntu Server

Claude Cowork: Anthropic Introduces an AI Agent

Leave a comment

Your email address will not be published. Required fields are marked *