- name: Setup ZFS RAID 1 Pool for Proxmox Storage hosts: hypervisor become: true tasks: - name: Verify Proxmox VE is running command: pveversion register: pve_version_check changed_when: false failed_when: pve_version_check.rc != 0 - name: Update package cache apt: update_cache: yes cache_valid_time: 3600 - name: Install ZFS utilities package: name: - zfsutils-linux - zfs-initramfs state: present - name: Load ZFS kernel module modprobe: name: zfs - name: Ensure ZFS module loads at boot lineinfile: path: /etc/modules line: zfs state: present - name: Check if ZFS pool already exists command: zpool list {{ zfs_pool_name }} register: zfs_pool_exists failed_when: false changed_when: false - name: Check if disks are in use shell: | for disk in {{ zfs_disk_1 }} {{ zfs_disk_2 }}; do if mount | grep -q "^$disk"; then echo "ERROR: $disk is mounted" exit 1 fi if lsblk -n -o MOUNTPOINT "$disk" | grep -v "^$" | grep -q .; then echo "ERROR: $disk has mounted partitions" exit 1 fi done register: disk_usage_check failed_when: disk_usage_check.rc != 0 changed_when: false - name: Create ZFS RAID 1 pool with optimized settings command: > zpool create {{ zfs_pool_name }} -o ashift=12 -O mountpoint=none mirror {{ zfs_disk_1 }} {{ zfs_disk_2 }} when: zfs_pool_exists.rc != 0 register: zfs_pool_create_result - name: Check if ZFS dataset already exists command: zfs list {{ zfs_pool_name }}/vm-storage register: zfs_dataset_exists failed_when: false changed_when: false - name: Create ZFS dataset for Proxmox storage command: zfs create {{ zfs_pool_name }}/vm-storage when: zfs_dataset_exists.rc != 0 register: zfs_dataset_create_result - name: Set ZFS dataset properties for Proxmox command: zfs set {{ item.property }}={{ item.value }} {{ zfs_pool_name }}/vm-storage loop: - { property: "mountpoint", value: "{{ zfs_pool_mountpoint }}" } - { property: "compression", value: "lz4" } - { property: "atime", value: "off" } - { property: "xattr", value: "sa" } - { property: "acltype", value: "posixacl" } - { property: "dnodesize", value: "auto" } when: zfs_dataset_exists.rc != 0 - name: Set ZFS pool properties for Proxmox command: zpool set autotrim=off {{ zfs_pool_name }} when: zfs_pool_exists.rc != 0 - name: Set ZFS pool mountpoint for Proxmox command: zfs set mountpoint={{ zfs_pool_mountpoint }} {{ zfs_pool_name }} when: zfs_pool_exists.rc == 0 - name: Export and re-import ZFS pool for Proxmox compatibility shell: | zpool export {{ zfs_pool_name }} zpool import {{ zfs_pool_name }} when: zfs_pool_exists.rc != 0 register: zfs_pool_import_result - name: Ensure ZFS services are enabled systemd: name: "{{ item }}" enabled: yes state: started loop: - zfs-import-cache - zfs-import-scan - zfs-mount - zfs-share - zfs-zed - name: Check if ZFS pool storage already exists in Proxmox config stat: path: /etc/pve/storage.cfg register: storage_cfg_file - name: Check if storage name exists in Proxmox config shell: "grep -q '^zfspool: {{ zfs_pool_name }}' /etc/pve/storage.cfg" register: storage_exists_check failed_when: false changed_when: false when: storage_cfg_file.stat.exists - name: Set storage not configured when config file doesn't exist set_fact: storage_exists_check: rc: 1 when: not storage_cfg_file.stat.exists - name: Debug storage configuration status debug: msg: | Config file exists: {{ storage_cfg_file.stat.exists }} Storage check result: {{ storage_exists_check.rc }} Pool exists: {{ zfs_pool_exists.rc == 0 }} Will add storage: {{ zfs_pool_exists.rc == 0 and storage_exists_check.rc != 0 }} # Registration is add-only on purpose. There used to be a "Remove existing # storage if it exists" task here that ran `pvesm remove` whenever the # storage WAS present, paired with an add that only ran when it was ABSENT. # The two conditions are mutually exclusive, so a real run against a # correctly-configured hypervisor removed the storage entry backing every VM # and never put it back. It would also have dropped `mountpoint /var/lib/vz`, # which the live entry has and which `pvesm add` below does not set. # # If the storage entry ever needs its options changed, edit # /etc/pve/storage.cfg or use `pvesm set` - do not re-register it from here. - name: Add ZFS pool storage to Proxmox using pvesm command: > pvesm add zfspool {{ zfs_pool_name }} --pool {{ zfs_pool_name }} --content rootdir,images --sparse 1 when: - zfs_pool_exists.rc == 0 - storage_exists_check.rc != 0 register: pvesm_add_result - name: Verify ZFS pool is healthy command: zpool status {{ zfs_pool_name }} register: final_zfs_status changed_when: false - name: Fail if ZFS pool is not healthy fail: msg: "ZFS pool {{ zfs_pool_name }} is not in a healthy state" when: "'ONLINE' not in final_zfs_status.stdout" # ───────────────────────────────────────────────────────────────────────────── # The monthly scrub. # # The ZFS HEALTH CHECK that used to share this play is gone: it is now the # zfs-health check in infra/400_host_monitoring.yml, which carries the same five # conditions - pool state, device states, resilver in progress, read/write/ # checksum errors, and errors from the last scan - but reports to Gatus like # every other host check instead of owning its own push plumbing. # # The scrub itself stays here, because it is not monitoring: it is the # maintenance that gives the health check something true to report. A pool that # is never scrubbed has no idea whether it is healthy. # ───────────────────────────────────────────────────────────────────────────── - name: Schedule the monthly ZFS scrub hosts: hypervisor become: true vars_files: - ../../infra_vars.yml vars: zfs_systemd_scrub_service_name: zfs-monthly-scrub tasks: - name: Create systemd service for ZFS monthly scrub template: src: templates/zfs-monthly-scrub.service.j2 dest: "/etc/systemd/system/{{ zfs_systemd_scrub_service_name }}.service" owner: root group: root mode: '0644' - name: Create systemd timer for monthly ZFS scrub template: src: templates/zfs-monthly-scrub.timer.j2 dest: "/etc/systemd/system/{{ zfs_systemd_scrub_service_name }}.timer" owner: root group: root mode: '0644' - name: Enable and start the monthly scrub timer systemd: name: "{{ zfs_systemd_scrub_service_name }}.timer" enabled: yes state: started daemon_reload: yes - name: Report the scrub schedule debug: msg: >- Monthly scrub of {{ zfs_pool_name }}: last day of each month at 04:00. Health is reported separately by the zfs-health check (infra/400_host_monitoring.yml).