Docker Instance Upgrades with Ansible

Docker Instance Upgrades with Ansible #

This guide shows how to automate Docker container upgrades using Ansible playbooks instead of manual updates. By automating this process, you ensure consistency, reduce errors, and save time when updating multiple Docker instances across your infrastructure.

Infrastructure Overview #

My homelab includes these Docker-based services:

  • Nginx Proxy Manager (proxy.home.arpa) - Reverse proxy with SSL
  • Healthchecks (health.home.arpa) - Monitoring service
  • Home Assistant (hass.home.arpa) - Home automation platform
  • Bitwarden (bit.home.arpa) - Password manager

Ansible Playbook Structure #

Host Configuration #

The playbook targets specific hosts with Docker services:

---
- name: Docker Instance Upgrades
  hosts: proxy.home.arpa, bit.home.arpa, health.home.arpa, hass.home.arpa
  become: true
  gather_facts: false
  any_errors_fatal: true
  vars_files:
    - "../group_vars/all/vault.yml"

Service-Specific Update Blocks #

Each service has its own update block with conditional execution:

Nginx Proxy Manager #

For standard Docker Compose services like Nginx Proxy Manager:

- name: Nginx Proxy Manager updates
  when: inventory_hostname == "proxy.home.arpa"
  block:
    - name: Pull latest images for Nginx Proxy Manager
      ansible.builtin.command:
        cmd: docker compose pull
        chdir: /var/containers/docker
      register: nginx_pull
      changed_when: "'Downloaded' in nginx_pull.stdout or 'Pulled' in nginx_pull.stdout"

    - name: Update Nginx Proxy Manager containers
      ansible.builtin.command:
        cmd: docker compose up -d
        chdir: /var/containers/docker
      register: nginx_up
      changed_when: "'Started' in nginx_up.stdout or 'Recreated' in nginx_up.stdout"

    - name: Clean up unused images for Nginx Proxy Manager
      ansible.builtin.command:
        cmd: docker system prune --all --force
      register: nginx_prune
      changed_when: "'Total reclaimed space' in nginx_prune.stdout"

Healthchecks #

Healthchecks follows the same pattern as Nginx Proxy Manager but with different paths:

- name: Healthchecks updates
  when: inventory_hostname == "health.home.arpa"
  block:
    - name: Pull latest images for Healthchecks
      ansible.builtin.command:
        cmd: docker compose pull
        chdir: /var/lib/containers/healthchecks/docker
      register: healthcheck_pull
      changed_when: "'Downloaded' in healthcheck_pull.stdout or 'Pulled' in healthcheck_pull.stdout"

    - name: Update Healthchecks containers
      ansible.builtin.command:
        cmd: docker compose up -d
        chdir: /var/lib/containers/healthchecks/docker
      register: healthcheck_up
      changed_when: "'Started' in healthcheck_up.stdout or 'Recreated' in healthcheck_up.stdout"

    - name: Clean up unused images for Healthchecks
      ansible.builtin.command:
        cmd: docker system prune --all --force
      register: healthcheck_prune
      changed_when: "'Total reclaimed space' in healthcheck_prune.stdout"

Home Assistant #

Home Assistant also uses the standard Docker Compose pattern:

- name: Home Assistant updates
  when: inventory_hostname == "hass.home.arpa"
  block:
    - name: Pull latest images for Home Assistant
      ansible.builtin.command:
        cmd: docker compose pull
        chdir: /var/containers/hass
      register: hass_pull
      changed_when: "'Downloaded' in hass_pull.stdout or 'Pulled' in hass_pull.stdout"

    - name: Update Home Assistant containers
      ansible.builtin.command:
        cmd: docker compose up -d
        chdir: /var/containers/hass
      register: hass_up
      changed_when: "'Started' in hass_up.stdout or 'Recreated' in hass_up.stdout"

    - name: Clean up unused images for Home Assistant
      ansible.builtin.command:
        cmd: docker system prune --all --force
      register: hass_prune
      changed_when: "'Total reclaimed space' in hass_prune.stdout"

Bitwarden (Special Case) #

Bitwarden uses its own update script that requires interactive confirmation:

- name: Bitwarden updates
  when: inventory_hostname == "bit.home.arpa"
  block:
    - name: Update Bitwarden script
      ansible.builtin.shell:
        cmd: echo "y" | ./bitwarden.sh updateself
        chdir: /opt/bitwarden
      register: bitwarden_self_update
      changed_when: bitwarden_self_update.rc == 0

    - name: Update Bitwarden instance
      ansible.builtin.shell:
        cmd: echo "y" | ./bitwarden.sh update
        chdir: /opt/bitwarden
      register: bitwarden_update
      changed_when: bitwarden_update.rc == 0

The echo "y" | pipes the confirmation response to handle the interactive prompt automatically.

Complete Playbook #

Click to expand the complete docker-upgrades.yml playbook
---
- name: Docker Instance Upgrades
  hosts: proxy.home.arpa, bit.home.arpa, health.home.arpa, hass.home.arpa
  become: true
  any_errors_fatal: true
  gather_facts: false
  vars_files:
    - "../group_vars/all/vault.yml"
  vars:
    ping_id: "a0ea986c-2557-4bfb-b1e8-a19aa0898035"
    ping_url: "https://health.nousiainen.xyz/ping/{{ ping_id }}"
  tasks:
    - name: Nginx Proxy Manager updates
      when: inventory_hostname == "proxy.home.arpa"
      block:
        - name: Pull latest images for Nginx Proxy Manager
          ansible.builtin.command:
            cmd: docker compose pull
            chdir: /var/containers/docker
          register: nginx_pull
          changed_when: "'Downloaded' in nginx_pull.stdout or 'Pulled' in nginx_pull.stdout"

        - name: Update Nginx Proxy Manager containers
          ansible.builtin.command:
            cmd: docker compose up -d
            chdir: /var/containers/docker
          register: nginx_up
          changed_when: "'Started' in nginx_up.stdout or 'Recreated' in nginx_up.stdout"

        - name: Clean up unused images for Nginx Proxy Manager
          ansible.builtin.command:
            cmd: docker system prune --all --force
          register: nginx_prune
          changed_when: "'Total reclaimed space' in nginx_prune.stdout"

    - name: Bitwarden updates
      when: inventory_hostname == "bit.home.arpa"
      block:
        - name: Update Bitwarden script
          ansible.builtin.shell:
            cmd: echo "y" | ./bitwarden.sh updateself
            chdir: /opt/bitwarden
          register: bitwarden_self_update
          changed_when: bitwarden_self_update.rc == 0

        - name: Update Bitwarden instance
          ansible.builtin.shell:
            cmd: echo "y" | ./bitwarden.sh update
            chdir: /opt/bitwarden
          register: bitwarden_update
          changed_when: bitwarden_update.rc == 0

    - name: Healthchecks updates
      when: inventory_hostname == "health.home.arpa"
      block:
        - name: Pull latest images for Healthchecks
          ansible.builtin.command:
            cmd: docker compose pull
            chdir: /var/lib/containers/healthchecks/docker
          register: healthcheck_pull
          changed_when: "'Downloaded' in healthcheck_pull.stdout or 'Pulled' in healthcheck_pull.stdout"

        - name: Update Healthchecks containers
          ansible.builtin.command:
            cmd: docker compose up -d
            chdir: /var/lib/containers/healthchecks/docker
          register: healthcheck_up
          changed_when: "'Started' in healthcheck_up.stdout or 'Recreated' in healthcheck_up.stdout"

        - name: Clean up unused images for Healthchecks
          ansible.builtin.command:
            cmd: docker system prune --all --force
          register: healthcheck_prune
          changed_when: "'Total reclaimed space' in healthcheck_prune.stdout"

    - name: Home Assistant updates
      when: inventory_hostname == "hass.home.arpa"
      block:
        - name: Pull latest images for Home Assistant
          ansible.builtin.command:
            cmd: docker compose pull
            chdir: /var/containers/hass
          register: hass_pull
          changed_when: "'Downloaded' in hass_pull.stdout or 'Pulled' in hass_pull.stdout"

        - name: Update Home Assistant containers
          ansible.builtin.command:
            cmd: docker compose up -d
            chdir: /var/containers/hass
          register: hass_up
          changed_when: "'Started' in hass_up.stdout or 'Recreated' in hass_up.stdout"

        - name: Clean up unused images for Home Assistant
          ansible.builtin.command:
            cmd: docker system prune --all --force
          register: hass_prune
          changed_when: "'Total reclaimed space' in hass_prune.stdout"

Conclusion #

I used to do these Docker instance upgrades manually by hand every once in a while but now it’s all automated, just like it should be!