Skip to content

Install docker and docker-compose using Ansible

Srijan Choudhary
3 min read
Install docker and docker-compose using Ansible

Table of Contents

I wanted a simple, but optimal (and fast) way to install docker and docker-compose using Ansible. I found a few ways online, but I was not satisfied.

My requirements were:

  • Support Debian and Ubuntu
  • Install docker using apt repositories
  • Do not even perform an apt-get update if docker is already installed using this method (to make it fast)
  • Install docker-compose by downloading from it’s website
  • But, don’t download if current version >= the minimum version required

I feel trying to achieve these requirements gave me a very good idea of how powerful ansible can be.

The final role and vars files can be seen in this gist. But, I’ll go through each section below to explain what makes this better / faster.

File structure

| -- docker
|    | -- defaults
|    |    | -- main.yml
|    | -- tasks
|    |    | -- main.yml
|    |    | -- docker_setup.yml

The tasks/main.yml file just imports tasks from tasks/docker_setup.yml


First, we’ve defined some variables in defaults/main.yml. These will control which release channel of docker and version of docker-compose will get installed.

docker_apt_release_channel: stable
docker_apt_arch: amd64
docker_apt_repository: "deb [arch={{ docker_apt_arch }}]{{ ansible_distribution | lower }} {{ ansible_distribution_release }} {{ docker_apt_release_channel }}"
docker_apt_gpg_key:{{ ansible_distribution | lower }}/gpg
docker_compose_version: "1.24.0"

Docker Setup

This task is divided into the following sections:

Install dependencies

- name: Install packages using apt
        - apt-transport-https
        - ca-certificates
        - curl
        - gnupg2
        - software-properties-common
    state: present
    update_cache: no

Here the state: present makes sure that these packages are only installed if not already installed. The update_cache: no can cause a failure if apt-get update has never been run on this system, but I think we can take that risk.

Add docker repository

- name: Add Docker GPG apt Key
    url: "{{ docker_apt_gpg_key }}"
    state: present

- name: Add Docker Repository
    repo: "{{ docker_apt_repository }}"
    state: present
    update_cache: true

Here, the state: present and update_cache: true make sure that the cache is only updated if this state was changed. So, apt-get update is not run if the docker repo is already present.

Install and enable docker

- name: Update apt and install docker-ce
    name: docker-ce
    state: present
    update_cache: false

- name: Run and enable docker
    name: docker
    state: started
    enabled: true

Again, due to state: present and update_cache: false, there are no extra cache fetches if docker is already installed.

Docker Compose Setup

This task has two sections:

Check if docker-compose is installed and it’s version

- name: Check current docker-compose version
  command: docker-compose --version
  register: docker_compose_vsn
  changed_when: false
  failed_when: false
  check_mode: no

- set_fact:
    docker_compose_current_version: "{{ docker_compose_vsn.stdout | regex_search('(\\d+(\\.\\d+)+)') }}"
    - docker_compose_vsn.stdout is defined

The first block saves the output of docker-compose --version into a variable docker_compose_vsn. The failed_when: false ensures that this does not call a failure even if the command fails to execute. (See error handling in ansible).

Sample output when docker-compose is installed: docker-compose version 1.26.0, build d4451659

The second block parses this output and extracts the version number using a regex (see ansible filters). There is a when condition which causes the second block to skip execution if the first block failed (See playbook conditionals).

Install or upgrade docker-compose if required

- name: Install or upgrade docker-compose
    url : "{{ docker_compose_version }}/docker-compose-Linux-x86_64"
    dest: /usr/local/bin/docker-compose
    mode: 'a+x'
    force: yes
  when: >
    docker_compose_current_version is not defined
    or docker_compose_current_version is version(docker_compose_version, '<')

This just downloads the required docker-compose binary and saves it to /usr/local/bin/docker-compose, but it has a conditional that this will only be done if either docker-compose is not already installed, or if the installed version is less than the required version. To do version comparison, it uses ansible’s built-in version comparison function.

So, we used a few ansible features to achieve what we wanted. I’m sure there are a lot of other things we can do to make this even better and more fool-proof. Maybe a post for another day.

devops docker ansible

Related Posts

Exploring conflicting oneshot services in systemd

Exploring ways to make two systemd services using a shared resource work with each other

Exploring conflicting oneshot services in systemd

Download a file securely from GCS on an untrusted system

Download files from google cloud storage using temporary credentials or time-limited access URLs

Slackbot using google cloud serverless functions

Slack bot using Google Cloud Functions to post a roundup of recently created channels

Slackbot using google cloud serverless functions