{ config, pkgs, ... }: { # Enable ZFS support boot.supportedFilesystems = [ "zfs" ]; boot.zfs.enableUnstable = false; # ZFS system services services.zfs = { autoSnapshot = { enable = true; frequent = 4; # Keep 4 15-minute snapshots hourly = 24; # Keep 24 hourly snapshots daily = 7; # Keep 7 daily snapshots weekly = 4; # Keep 4 weekly snapshots monthly = 12; # Keep 12 monthly snapshots }; autoScrub = { enable = true; interval = "weekly"; # Scrub pools weekly }; }; # Your ZFS pool and datasets will be automatically imported # But we can specify mount points explicitly for clarity fileSystems = { "/mnt/ai" = { device = "datapool/ai"; fsType = "zfs"; }; "/mnt/movies" = { device = "datapool/movies"; fsType = "zfs"; }; "/mnt/tvshows" = { device = "datapool/tv_shows"; fsType = "zfs"; }; "/mnt/music" = { device = "datapool/music"; fsType = "zfs"; }; "/mnt/astrophotography" = { device = "datapool/astro"; fsType = "zfs"; }; "/mnt/downloads" = { device = "datapool/downloads"; fsType = "zfs"; }; "/mnt/photos" = { device = "datapool/photos"; fsType = "zfs"; }; "/mnt/stash" = { device = "datapool/stash"; fsType = "zfs"; }; "/mnt/ISOs" = { device = "datapool/isos"; fsType = "zfs"; }; "/mnt/audiobooks" = { device = "datapool/audiobooks"; fsType = "zfs"; }; "/mnt/VMs" = { device = "datapool/vms"; fsType = "zfs"; }; "/mnt/old_backups" = { device = "datapool/old_backups"; fsType = "zfs"; }; environment.etc."docker" = { device = "datapool/services"; fsType = "zfs"; }; }; # Install ZFS utilities environment.systemPackages = with pkgs; [ zfs zfstools ]; # If you want to keep compression settings boot.kernelParams = [ "zfs.zfs_compressed_arc_enabled=1" ]; systemd.services.zfs-permissions = { description = "Set correct permissions on ZFS datasets"; after = [ "zfs-mount.service" ]; wantedBy = [ "multi-user.target" ]; script = '' # Set ownership and permissions for each dataset # Astrophotography - menno:menno 770 chown menno:menno /mnt/astrophotography chmod 770 /mnt/astrophotography # docker - root:menno 775 chown root:menno /mnt/docker chmod 775 /mnt/docker # ai - menno:menno 770 chown menno:menno /mnt/ai chmod 770 /mnt/ai # Audiobooks - menno:users 760 chown menno:users /mnt/audiobooks chmod 760 /mnt/audiobooks # Downloads - menno:users 760 chown menno:users /mnt/downloads chmod 760 /mnt/downloads # ISOs - menno:libvirt 777 chown menno:libvirt /mnt/ISOs chmod 777 /mnt/ISOs # VMs - menno:libvirt 777 chown menno:libvirt /mnt/VMs chmod 777 /mnt/VMs # Movies - menno:users 760 chown menno:users /mnt/movies chmod 760 /mnt/movies # Music - menno:users 760 chown menno:users /mnt/music chmod 760 /mnt/music # old_backups - menno:users 760 chown menno:users /mnt/old_backups chmod 760 /mnt/old_backups # Photos - menno:menno 775 chown menno:menno /mnt/photos chmod 775 /mnt/photos # Stash - menno:menno 775 chown menno:menno /mnt/stash chmod 775 /mnt/stash # TV_Shows - menno:users 760 chown menno:users /mnt/tvshows chmod 760 /mnt/tvshows ''; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; }; # environment.etc."local/bin/zfs-backup.sh" = { # mode = "0755"; # text = '' # #!/bin/bash # set -euo pipefail # DATE=$(date +%Y%m%d-%H%M) # DATASETS="music astro photos stash isos ai audiobooks vms old_backups services" # RETAIN_SNAPSHOTS=24 # BACKUP_POOL="backup" # SOURCE_POOL="datapool" # log() { # echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" # } # ensure_backup_pool() { # if ! zpool list "$BACKUP_POOL" >/dev/null 2>&1; then # log "ERROR: Backup pool '$BACKUP_POOL' does not exist!" # return 1 # fi # } # check_dataset_exists() { # local pool=$1 # local dataset=$2 # zfs list "$pool/$dataset" >/dev/null 2>&1 # return $? # } # create_backup_dataset() { # local dataset=$1 # local source_pool="$SOURCE_POOL" # local backup_pool="$BACKUP_POOL" # # Get properties from source dataset # local props=$(zfs get -H -o property,value all "$source_pool/$dataset" | \ # grep -E '^(compression|recordsize|atime|relatime|xattr|acltype)' | \ # awk '{printf "-o %s=%s ", $1, $2}') # log "Creating backup dataset $backup_pool/$dataset with matching properties" # # shellcheck disable=SC2086 # zfs create -p ${props} "$backup_pool/$dataset" # # Set some backup-specific properties # zfs set readonly=on "$backup_pool/$dataset" # zfs set snapdir=visible "$backup_pool/$dataset" # log "Successfully created backup dataset $backup_pool/$dataset" # } # get_latest_snapshot() { # local pool=$1 # local dataset=$2 # local snapshot # snapshot=$(zfs list -t snapshot -H -o name "$pool/$dataset" 2>/dev/null | grep backup- | tail -n1) || true # echo "$snapshot" # } # # Ensure backup pool exists # ensure_backup_pool # for ds in $DATASETS; do # log "Processing dataset $ds" # # Check if source dataset exists # if ! check_dataset_exists "$SOURCE_POOL" "$ds"; then # log "Skipping $ds - source dataset $SOURCE_POOL/$ds does not exist" # continue # fi # # Create backup dataset if it doesn't exist # if ! check_dataset_exists "$BACKUP_POOL" "$ds"; then # log "Backup dataset $BACKUP_POOL/$ds does not exist" # create_backup_dataset "$ds" # fi # # Create new snapshot # local snapshot_name="$SOURCE_POOL/$ds@backup-$DATE" # log "Creating new snapshot $snapshot_name" # zfs snapshot "$snapshot_name" # LATEST_BACKUP=$(get_latest_snapshot "$BACKUP_POOL" "$ds") # if [ -z "$LATEST_BACKUP" ]; then # log "No existing backup found - performing full backup of $ds" # zfs send "$snapshot_name" | zfs receive -F "$BACKUP_POOL/$ds" # else # LATEST_SOURCE=$(get_latest_snapshot "$SOURCE_POOL" "$ds" | grep -v "backup-$DATE" | tail -n1) # if [ -n "$LATEST_SOURCE" ]; then # log "Performing incremental backup of $ds from $LATEST_SOURCE to backup-$DATE" # zfs send -i "$LATEST_SOURCE" "$snapshot_name" | zfs receive -F "$BACKUP_POOL/$ds" # else # log "No suitable source snapshot found for incremental backup - performing full backup of $ds" # zfs send "$snapshot_name" | zfs receive -F "$BACKUP_POOL/$ds" # fi # fi # log "Cleaning up old snapshots for $ds" # # Cleanup source snapshots # if snapshots=$(zfs list -t snapshot -H -o name "$SOURCE_POOL/$ds" | grep backup-); then # echo "$snapshots" | head -n -$RETAIN_SNAPSHOTS | while read -r snap; do # log "Removing source snapshot: $snap" # zfs destroy "$snap" # done # fi # # Cleanup backup snapshots # if snapshots=$(zfs list -t snapshot -H -o name "$BACKUP_POOL/$ds" | grep backup-); then # echo "$snapshots" | head -n -$RETAIN_SNAPSHOTS | while read -r snap; do # log "Removing backup snapshot: $snap" # zfs destroy "$snap" # done # fi # done # log "Backup completed successfully" # ''; # }; # systemd.services.zfs-backup = { # description = "ZFS Backup Service"; # requires = [ "zfs.target" ]; # after = [ "zfs.target" ]; # path = [ pkgs.zfs ]; # serviceConfig = { # Type = "oneshot"; # ExecStart = "/etc/local/bin/zfs-backup.sh"; # User = "root"; # }; # }; # systemd.timers.zfs-backup = { # description = "Run ZFS backup every 4 hours"; # wantedBy = [ "timers.target" ]; # timerConfig = { # OnBootSec = "15min"; # OnUnitActiveSec = "4h"; # RandomizedDelaySec = "5min"; # }; # }; }