- name: Safe Nextcloud upgrade on Rocky Linux (Fixed and Updated) hosts: all become: yes vars: # --- Configuration Variables --- nextcloud_dir: /var/www/nextcloud web_user: apache php_cli: /usr/bin/php service_name: httpd # List of core apps to ensure we don't try to disable them 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!" # ---------------------------------------------------------------------- # 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 # FIX: Only fail if Nextcloud is NOT installed/ready for upgrade 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 1.5: Disable 3rd party apps (Best Practice Addition) # ---------------------------------------------------------------------- - name: Get list of enabled apps command: "{{ php_cli }} occ app:list --installed" 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: TMPDIR: /tmp loop: "{{ app_list.stdout_lines | select('match', '^[ ]*-[ ](.*):[ ]enabled$') | map('regex_replace', '^[ ]*-[ ](.*):[ ]enabled$', '\\1') | list }}" register: app_disable_result # Only process apps that are not in the core_apps list when: item not in core_apps ignore_errors: yes - 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 2: Enable maintenance mode # ---------------------------------------------------------------------- - name: Enable maintenance mode command: "{{ php_cli }} occ maintenance:mode --on" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: TMPDIR: /tmp # ---------------------------------------------------------------------- # Step 3: Run updater safely with retry (Fixed and Simplified) # ---------------------------------------------------------------------- - name: Run Nextcloud CLI updater (non-interactive) with retry logic command: "{{ php_cli }} updater/updater.phar --no-interaction" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: TMPDIR: /tmp register: updater_result # FIX: Use Ansible's retry mechanism for transient errors (e.g., signature fails, temporary network issue) 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 # Final failure condition failed_when: - updater_result.rc != 0 - "'Nextcloud is already latest version' not in updater_result.stdout" # ---------------------------------------------------------------------- # Step 4: Run database upgrade + cleanup # ---------------------------------------------------------------------- - name: Run occ upgrade command: "{{ php_cli }} occ upgrade" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: TMPDIR: /tmp - name: Add missing indices (optional cleanup) command: "{{ php_cli }} occ db:add-missing-indices" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: TMPDIR: /tmp 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: TMPDIR: /tmp ignore_errors: yes # ---------------------------------------------------------------------- # Step 5: Disable maintenance mode # ---------------------------------------------------------------------- - name: Disable maintenance mode command: "{{ php_cli }} occ maintenance:mode --off" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: TMPDIR: /tmp ignore_errors: yes # ---------------------------------------------------------------------- # Step 5.5: Re-enable disabled apps (Best Practice Addition) # ---------------------------------------------------------------------- - name: Re-enable previously disabled 3rd party apps command: "{{ php_cli }} occ app:enable {{ item }}" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: TMPDIR: /tmp loop: "{{ disabled_apps | default([]) }}" when: disabled_apps is defined and disabled_apps | length > 0 ignore_errors: yes # ---------------------------------------------------------------------- # Step 6: Restart web service # ---------------------------------------------------------------------- - name: Restart Apache httpd service: name: "{{ service_name }}" state: restarted # ---------------------------------------------------------------------- # Step 7: Verify upgrade result # ---------------------------------------------------------------------- - name: Check new Nextcloud version command: "{{ php_cli }} occ status" args: chdir: "{{ nextcloud_dir }}" become_user: "{{ web_user }}" environment: TMPDIR: /tmp register: occ_status_after - name: Display new version info debug: var: occ_status_after.stdout