From a997469d6e2de4e40741e1055306fd1b7c8fc9c6 Mon Sep 17 00:00:00 2001 From: jakub Date: Sun, 30 Aug 2026 01:49:04 +0200 Subject: [PATCH] Use official Portainer updater --- roles/update_portainer/defaults/main.yml | 6 +- roles/update_portainer/tasks/main.yml | 113 ++++++++--------------- 2 files changed, 43 insertions(+), 76 deletions(-) diff --git a/roles/update_portainer/defaults/main.yml b/roles/update_portainer/defaults/main.yml index 5a16fe9..b016d0c 100644 --- a/roles/update_portainer/defaults/main.yml +++ b/roles/update_portainer/defaults/main.yml @@ -1,5 +1,5 @@ --- -# Watchtower is used only as a one-shot updater. It recreates the selected -# Portainer containers with their existing configuration and then removes itself. -portainer_watchtower_image: containrrr/watchtower:latest +# Portainer's official updater recreates the server container while preserving +# its existing Docker configuration and persistent data. +portainer_updater_image: portainer/portainer-updater:latest portainer_target_tag: latest diff --git a/roles/update_portainer/tasks/main.yml b/roles/update_portainer/tasks/main.yml index ca83b3d..6c22975 100644 --- a/roles/update_portainer/tasks/main.yml +++ b/roles/update_portainer/tasks/main.yml @@ -8,7 +8,7 @@ changed_when: false failed_when: false -- name: Find Docker containers +- name: Find and update Portainer when: portainer_docker_info.rc == 0 block: - name: List all container IDs @@ -22,83 +22,50 @@ register: portainer_all_containers changed_when: false - - name: Inspect container images - ansible.builtin.command: - argv: - - docker - - container - - inspect - - --format - - "{% raw %}{{.Config.Image}}{% endraw %}" - - "{{ item }}" - loop: "{{ portainer_all_containers.stdout_lines }}" - register: portainer_container_inspections - changed_when: false - - - name: Select Portainer CE and Business Edition server containers - ansible.builtin.set_fact: - portainer_container_ids: >- - {{ portainer_container_inspections.results - | selectattr('stdout', 'match', - '^(?:[^/]+/)?portainer/(?:portainer|portainer-(?:ce|ee))(?::|@|$)') - | map(attribute='item') - | list }} - - - name: Pull the latest release for each installed Portainer edition - ansible.builtin.command: - argv: - - docker - - image - - pull - - >- - {{ 'portainer/portainer-ee:' ~ portainer_target_tag - if 'portainer-ee' in item.stdout - else 'portainer/portainer-ce:' ~ portainer_target_tag }} - loop: "{{ portainer_container_inspections.results }}" - loop_control: - label: "{{ item.stdout }}" - register: portainer_image_pulls - changed_when: "'Downloaded newer image' in portainer_image_pulls.stdout" - when: - - item.stdout is match('^(?:[^/]+/)?portainer/(?:portainer|portainer-(?:ce|ee))(?::|@|$)') - - not ansible_check_mode - - - name: Point the existing image reference at the latest release - ansible.builtin.command: - argv: - - docker - - image - - tag - - >- - {{ 'portainer/portainer-ee:' ~ portainer_target_tag - if 'portainer-ee' in item.stdout - else 'portainer/portainer-ce:' ~ portainer_target_tag }} - - "{{ item.stdout }}" - loop: "{{ portainer_container_inspections.results }}" - loop_control: - label: "{{ item.stdout }}" - changed_when: false - when: - - item.stdout is match('^(?:[^/]+/)?portainer/(?:portainer|portainer-(?:ce|ee))(?::|$)') - - not ansible_check_mode - - - name: Update discovered Portainer containers + - name: Inspect all container images ansible.builtin.command: argv: >- - {{ ['docker', 'run', '--rm', '--pull', 'always', - '--volume', '/var/run/docker.sock:/var/run/docker.sock', - portainer_watchtower_image, '--run-once', '--cleanup', '--no-pull', - '--include-stopped'] - + portainer_container_ids }} - register: portainer_watchtower - changed_when: >- - (portainer_watchtower.stdout ~ portainer_watchtower.stderr) - is regex('Updated=[1-9][0-9]*') + {{ ['docker', 'container', 'inspect'] + + portainer_all_containers.stdout_lines }} + register: portainer_container_inspection + changed_when: false + when: portainer_all_containers.stdout_lines | length > 0 + + - name: Select installed Portainer editions + ansible.builtin.set_fact: + portainer_installed_images: >- + {{ (portainer_container_inspection.stdout | default('[]') | from_json) + | map(attribute='Config.Image') + | select('match', + '^(?:[^/]+/)?portainer/(?:portainer|portainer-(?:ce|ee))(?::|@|$)') + | unique + | list }} + + - name: Update Portainer to the newest release + ansible.builtin.command: + argv: + - docker + - run + - --rm + - --pull + - always + - --volume + - /var/run/docker.sock:/var/run/docker.sock + - "{{ portainer_updater_image }}" + - >- + --image={{ 'portainer/portainer-ee:' ~ portainer_target_tag + if 'portainer-ee' in item + else 'portainer/portainer-ce:' ~ portainer_target_tag }} + loop: "{{ portainer_installed_images }}" + loop_control: + label: "{{ item }}" + register: portainer_update + changed_when: true when: - - portainer_container_ids | length > 0 + - portainer_installed_images | length > 0 - not ansible_check_mode - name: Report when no Portainer server is installed ansible.builtin.debug: msg: No Portainer CE or Business Edition server container found; skipping. - when: portainer_container_ids | length == 0 + when: portainer_installed_images | length == 0