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!