From 66ab6d8db31278f8f931b8fe212306a05499205f Mon Sep 17 00:00:00 2001 From: Sergei Poljanski Date: Mon, 12 Oct 2026 05:03:11 +0400 Subject: [PATCH] s3fs: allow turning the mounts off, off on asxpio-ops On the 38 GB ops disk the s3fs cache reserve (ensure_diskfree) never fit, so all three mounts failed at every boot since 2026-09-22. Backups now go to Hetzner S3 with rclone instead. Add s3fs_enabled (default true); when false the role unmounts the buckets and drops their fstab entries, keeping the credentials file that the rclone wrapper reads. Assisted-by: Claude:opus-5 --- ansible/inventory/host_vars/asxpio-ops.yaml | 3 + ansible/roles/s3fs/defaults/main.yaml | 4 + ansible/roles/s3fs/tasks/main.yaml | 136 ++++++++++++-------- 3 files changed, 87 insertions(+), 56 deletions(-) diff --git a/ansible/inventory/host_vars/asxpio-ops.yaml b/ansible/inventory/host_vars/asxpio-ops.yaml index 1f0feb3..2ec30e1 100644 --- a/ansible/inventory/host_vars/asxpio-ops.yaml +++ b/ansible/inventory/host_vars/asxpio-ops.yaml @@ -6,4 +6,7 @@ ssh_allow_public_git: true ssh_git_user: git +# Off: the cache reserve never fit the 38 GB disk, so the mounts failed at +# every boot. Backups go to Hetzner S3 with rclone instead. +s3fs_enabled: false s3fs_ensure_diskfree: 20000 # Total:40GB Keep:20GB free = ~15GB for Cache diff --git a/ansible/roles/s3fs/defaults/main.yaml b/ansible/roles/s3fs/defaults/main.yaml index be47b3c..dbcdfd1 100644 --- a/ansible/roles/s3fs/defaults/main.yaml +++ b/ansible/roles/s3fs/defaults/main.yaml @@ -9,6 +9,10 @@ s3_region: "hel1" s3_access_key: "" s3_secret_key: "" +# false: no mounts. Unmounts and drops the fstab entries; the credentials +# file stays (backups use it with rclone, /root/backups/rclone-hetzner.sh). +s3fs_enabled: true + # S3 Buckets to mount (non-replication buckets only) s3_buckets: - name: asxpio-terraform-main diff --git a/ansible/roles/s3fs/tasks/main.yaml b/ansible/roles/s3fs/tasks/main.yaml index 4a95226..ac1aa66 100644 --- a/ansible/roles/s3fs/tasks/main.yaml +++ b/ansible/roles/s3fs/tasks/main.yaml @@ -32,65 +32,89 @@ owner: root group: root -- name: Create mount points for S3 buckets - ansible.builtin.file: - path: "{{ item.mount_point }}" - state: directory - mode: "{{ item.mode }}" - owner: "{{ item.owner }}" - group: "{{ item.group }}" +- name: Mount S3 buckets + when: s3fs_enabled | bool + block: + - name: Create mount points for S3 buckets + ansible.builtin.file: + path: "{{ item.mount_point }}" + state: directory + mode: "{{ item.mode }}" + owner: "{{ item.owner }}" + group: "{{ item.group }}" + loop: "{{ s3_buckets }}" + loop_control: + label: "{{ item.name }} -> {{ item.mount_point }}" + + - name: Add S3 buckets to /etc/fstab + ansible.builtin.lineinfile: + path: /etc/fstab + line: "{{ item.name }} {{ item.mount_point }} fuse.s3fs _netdev,{{ s3fs_options | join(',') }} 0 0" + regexp: "^{{ item.name }}\\s+{{ item.mount_point }}" + state: present + loop: "{{ s3_buckets }}" + loop_control: + label: "{{ item.name }} -> {{ item.mount_point }}" + + - name: Mount all S3 buckets + ansible.builtin.command: + cmd: "mount {{ item.mount_point }}" + loop: "{{ s3_buckets }}" + loop_control: + label: "{{ item.name }} -> {{ item.mount_point }}" + register: mount_result + changed_when: mount_result.rc == 0 + failed_when: false + + - name: Verify S3 buckets are mounted + ansible.builtin.command: + cmd: "mountpoint -q {{ item.mount_point }}" + loop: "{{ s3_buckets }}" + loop_control: + label: "{{ item.name }} -> {{ item.mount_point }}" + register: verify_mount + changed_when: false + failed_when: verify_mount.rc != 0 + + - name: Display mounted S3 buckets + ansible.builtin.debug: + msg: | + ✅ S3 Buckets mounted successfully: + {% for bucket in s3_buckets %} + - {{ bucket.name }} -> {{ bucket.mount_point }} + {% endfor %} + + - name: Test write access to Immich bucket + ansible.builtin.copy: + content: "S3FS test file created on {{ ansible_date_time.iso8601 }}" + dest: "{{ s3_buckets | selectattr('name', 'equalto', 'asxpio-immich-main') | map(attribute='mount_point') | first }}/.s3fs-test" + mode: '0644' + when: "'asxpio-immich-main' in s3_buckets | map(attribute='name')" + register: write_test + + - name: Remove test file + ansible.builtin.file: + path: "{{ s3_buckets | selectattr('name', 'equalto', 'asxpio-immich-main') | map(attribute='mount_point') | first }}/.s3fs-test" + state: absent + when: write_test is changed + +- name: Unmount S3 buckets + ansible.builtin.command: + cmd: "umount {{ item.mount_point }}" loop: "{{ s3_buckets }}" loop_control: - label: "{{ item.name }} -> {{ item.mount_point }}" + label: "{{ item.mount_point }}" + register: umount_result + changed_when: umount_result.rc == 0 + failed_when: false + when: not (s3fs_enabled | bool) -- name: Add S3 buckets to /etc/fstab +- name: Remove S3 buckets from /etc/fstab ansible.builtin.lineinfile: path: /etc/fstab - line: "{{ item.name }} {{ item.mount_point }} fuse.s3fs _netdev,{{ s3fs_options | join(',') }} 0 0" - regexp: "^{{ item.name }}\\s+{{ item.mount_point }}" - state: present - loop: "{{ s3_buckets }}" - loop_control: - label: "{{ item.name }} -> {{ item.mount_point }}" - -- name: Mount all S3 buckets - ansible.builtin.command: - cmd: "mount {{ item.mount_point }}" - loop: "{{ s3_buckets }}" - loop_control: - label: "{{ item.name }} -> {{ item.mount_point }}" - register: mount_result - changed_when: mount_result.rc == 0 - failed_when: false - -- name: Verify S3 buckets are mounted - ansible.builtin.command: - cmd: "mountpoint -q {{ item.mount_point }}" - loop: "{{ s3_buckets }}" - loop_control: - label: "{{ item.name }} -> {{ item.mount_point }}" - register: verify_mount - changed_when: false - failed_when: verify_mount.rc != 0 - -- name: Display mounted S3 buckets - ansible.builtin.debug: - msg: | - ✅ S3 Buckets mounted successfully: - {% for bucket in s3_buckets %} - - {{ bucket.name }} -> {{ bucket.mount_point }} - {% endfor %} - -- name: Test write access to Immich bucket - ansible.builtin.copy: - content: "S3FS test file created on {{ ansible_date_time.iso8601 }}" - dest: "{{ s3_buckets | selectattr('name', 'equalto', 'asxpio-immich-main') | map(attribute='mount_point') | first }}/.s3fs-test" - mode: '0644' - when: "'asxpio-immich-main' in s3_buckets | map(attribute='name')" - register: write_test - -- name: Remove test file - ansible.builtin.file: - path: "{{ s3_buckets | selectattr('name', 'equalto', 'asxpio-immich-main') | map(attribute='mount_point') | first }}/.s3fs-test" + regexp: "^{{ item.name }}\\s+{{ item.mount_point }}\\s" state: absent - when: write_test is changed + loop: "{{ s3_buckets }}" + loop_control: + label: "{{ item.name }} -> {{ item.mount_point }}" + when: not (s3fs_enabled | bool)