- name: Resilient Nextcloud upgrade on Rocky Linux hosts: all become: yes vars: # --- Configuration Variables --- nextcloud_dir: /var/www/nextcloud web_user: apache php_cli: /usr/bin/php service_name: httpd # --- Environment for occ commands --- # This avoids setting TMPDIR on every single task occ_env: TMPDIR: /tmp # --- Core Apps --- # Add any app you consider "core" and do NOT want to disable. # user_ldap is a good example if you use it for auth. core_apps: - activity - twofactor_nextcloud_notification - files - settings - dav - updater - workflowengine - theming - user_ldap - dashboard - federation - updatestatus - accessibility - serverinfo tasks: - name: Reminder - ensure backups exist debug: msg: "⚠️ CRITICAL: Ensure you have valid DB + data directory backups BEFORE PROCEEDING!" - name: Pause playbook for 10 seconds to allow backup cancellation pause: seconds: 10 prompt: "Press Ctrl+C and then 'A' to abort if you do not have backups." # ---------------------------------------------------------------------- # Step 1: Check current version # ---------------------------------------------------------------------- - name: Get current Nextcloud version command: "{{ php_cli }} occ status" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" register: occ_status changed_when: false failed_when: "'installed: true' not in occ_status.stdout" - name: Parse and show current version set_fact: current_version: "{{ occ_status.stdout | regex_search('versionstring: (.*)', '\\1') | first | default('unknown') }}" when: occ_status.stdout is defined - name: Show current version debug: msg: "Current Nextcloud version: {{ current_version }}" # ---------------------------------------------------------------------- # Step 2: Disable 3rd party apps # ---------------------------------------------------------------------- - name: Get list of installed apps command: "{{ php_cli }} occ app:list" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" register: app_list changed_when: false - name: Disable all non-core 3rd party apps command: "{{ php_cli }} occ app:disable {{ item }}" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: "{{ occ_env }}" loop: "{{ app_list.stdout_lines | select('match', '^[ ]*-[ ](.*):[ ]enabled$') | map('regex_replace', '^[ ]*-[ ](.*):[ ]enabled$', '\\1') | list }}" register: app_disable_result when: item not in core_apps ignore_errors: yes # Don't fail if an app can't be disabled - name: Save list of successfully disabled apps for re-enabling set_fact: disabled_apps: "{{ app_disable_result.results | selectattr('changed', 'defined') | selectattr('changed') | map(attribute='item') | list }}" when: app_disable_result is defined and app_disable_result.results is defined # ---------------------------------------------------------------------- # Step 3: Begin Upgrade Transaction Block # ---------------------------------------------------------------------- - name: Perform Nextcloud Upgrade (Transactional) block: - name: Check if maintenance mode is already active command: "{{ php_cli }} occ maintenance:mode" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" register: maint_status changed_when: false failed_when: false # Don't fail, just check output - name: Enable maintenance mode command: "{{ php_cli }} occ maintenance:mode --on" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: "{{ occ_env }}" when: "'maintenance mode is not active' in maint_status.stdout" - name: Run Nextcloud CLI updater (with retry logic) command: "{{ php_cli }} updater/updater.phar --no-interaction" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: "{{ occ_env }}" register: updater_result until: - updater_result.rc == 0 - "'Update successful' in updater_result.stdout or 'Nextcloud is already latest version' in updater_result.stdout" retries: 3 delay: 10 # This is the final failure check for the updater itself failed_when: - updater_result.rc != 0 - "'Nextcloud is already latest version' not in updater_result.stdout" - name: Run occ upgrade command: "{{ php_cli }} occ upgrade" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: "{{ occ_env }}" # CRITICAL: Do NOT ignore errors here. This is the main failure point. - name: Add missing indices (optional cleanup) command: "{{ php_cli }} occ db:add-missing-indices" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: "{{ occ_env }}" ignore_errors: yes - name: Convert filecache bigint fields (optional cleanup) command: "{{ php_cli }} occ db:convert-filecache-bigint --no-interaction" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: "{{ occ_env }}" ignore_errors: yes - name: Disable maintenance mode (SUCCESS) command: "{{ php_cli }} occ maintenance:mode --off" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: "{{ occ_env }}" - name: Re-enable previously disabled 3rd party apps command: "{{ php_cli }} occ app:enable {{ item }}" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: "{{ occ_env }}" loop: "{{ disabled_apps | default([]) }}" when: disabled_apps is defined and disabled_apps | length > 0 ignore_errors: yes # Don't let a single app fail the whole restart - name: Restart web service service: name: "{{ service_name }}" state: restarted - name: Check new Nextcloud version command: "{{ php_cli }} occ status" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: "{{ occ_env }}" register: occ_status_after - name: Display new version info debug: var: occ_status_after.stdout - name: ✅ Upgrade Complete debug: msg: "Nextcloud successfully upgraded!" # ---------------------------------------------------------------------- # Step 4: Rescue Block (This ONLY runs if the 'block' above fails) # ---------------------------------------------------------------------- rescue: - name: ❌ UPGRADE FAILED - REPORTING debug: msg: | !!!!!!!!!! UPGRADE FAILED !!!!!!!!!! The server is being LEFT IN MAINTENANCE MODE for manual inspection. Host: {{ inventory_hostname }} Please SSH to the host and check logs to fix manually. Logs to check: - {{ nextcloud_dir }}/data/updater.log - {{ nextcloud_dir }}/data/nextcloud.log Run '{{ php_cli }} occ maintenance:mode --off' manually when fixed. - name: Fail the playbook run fail: msg: "Nextcloud upgrade failed. Host is in maintenance mode."