My nix-darwin and NixOS config
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

macmini: add Time Machine activation with local disk + server fallback

- Add myConfig.darwin.externalDisk.timeMachineVolumeUUID option for
declaring a local APFS volume as a Time Machine destination
- Rework timeMachineDestination activation script to support a priority
chain: local APFS volume → SMB server → skip
- Local volume is auto-mounted if present but unmounted at activation time
- Server destination is skipped gracefully if host is unreachable
- Set weekly backup interval (604800s) via tmutil setbackupinterval
- Set timeMachineVolumeUUID for macmini (CT2000X9SSD9 (Micron Crucial X9 2TB) external disk)

+92 -21
+12
hosts/macmini/default.nix
··· 27 27 # SMB/NetBIOS hostname (used by network discovery and file sharing) 28 28 system.defaults.smb.NetBIOSName = "macmini"; 29 29 30 + # ── External disk (CT2000X9SSD9, APFS container on disk4s2) ─────────────── 31 + # After setting up APFS volumes on the external disk (see README), set: 32 + # 33 + # Time Machine (local, priority 1 — fills in after one-time volume creation): 34 + # myConfig.darwin.externalDisk.timeMachineVolumeUUID = "<uuid from diskutil>"; 35 + # 36 + # Server Time Machine (fallback, priority 2 — enable once server has netatalk): 37 + # myConfig.server.timemachine.enable = true; 38 + # 39 + # Leave both at their defaults (null / false) to skip Time Machine entirely. 40 + myConfig.darwin.externalDisk.timeMachineVolumeUUID = "9217DB34-722B-4596-8ADD-20C8060FC257"; 41 + 30 42 # Timezone — driven from myConfig.timeZone 31 43 time.timeZone = cfg.timeZone; 32 44
+66 -21
modules/darwin/system.nix
··· 25 25 26 26 # ── Time Machine destination ────────────────────────────────────────────── 27 27 # No native nix-darwin option exists for tmutil setdestination, so we use an 28 - # activation script. It is idempotent: skipped if the URL is already registered. 28 + # activation script. Priority order: 29 + # 1. Local APFS volume on external disk (myConfig.darwin.externalDisk.timeMachineVolumeUUID) 30 + # 2. SMB server share (myConfig.server.timemachine.enable = true) 31 + # 3. Skip — no Time Machine configured 29 32 # 30 - # First-time setup (one-off, interactive — stores password in macOS keychain): 33 + # Server first-time setup (one-off, interactive — stores password in macOS keychain): 31 34 # sudo tmutil setdestination -p smb://<user>@server/TimeMachine 32 35 # After that, nrs registers it automatically on every rebuild without prompting. 33 - system.activationScripts.timeMachineDestination = lib.mkIf cfg.server.timemachine.enable { 34 - text = 35 - let 36 - shareUrl = "smb://${cfg.user.username}@server/${cfg.server.timemachine.shareName}"; 37 - in 38 - '' 39 - shareUrl='${shareUrl}' 40 - echo "Checking Time Machine destination ($shareUrl)..." 41 - if /usr/bin/tmutil destinationinfo 2>/dev/null | /usr/bin/grep -qF "$shareUrl"; then 42 - echo " already registered, skipping" 43 - else 44 - echo " registering..." 45 - # -a adds alongside existing destinations rather than replacing them. 46 - # Credentials come from the macOS keychain — never stored in the Nix store. 47 - /usr/bin/tmutil setdestination -a "$shareUrl" 2>&1 || \ 48 - echo " WARNING: could not register (share may be unreachable right now)" 49 - fi 50 - ''; 51 - }; 36 + system.activationScripts.timeMachineDestination = lib.mkIf 37 + (cfg.darwin.externalDisk.timeMachineVolumeUUID != null || cfg.server.timemachine.enable) 38 + { 39 + text = 40 + let 41 + localUUID = cfg.darwin.externalDisk.timeMachineVolumeUUID; 42 + hasLocal = localUUID != null; 43 + serverUrl = "smb://${cfg.user.username}@server/${cfg.server.timemachine.shareName}"; 44 + hasServer = cfg.server.timemachine.enable; 45 + in 46 + '' 47 + _register_tm() { 48 + local dest="$1" 49 + if /usr/bin/tmutil destinationinfo 2>/dev/null | /usr/bin/grep -qF "$dest"; then 50 + echo " Time Machine: $dest already registered, skipping" 51 + else 52 + echo " Time Machine: registering $dest" 53 + # -a adds alongside existing destinations rather than replacing. 54 + # For SMB, credentials must already be in the macOS keychain. 55 + /usr/bin/tmutil setdestination -a "$dest" 2>&1 || \ 56 + echo " WARNING: could not register $dest" 57 + fi 58 + } 59 + 60 + ${lib.optionalString hasLocal '' 61 + # ── Priority 1: local APFS volume ────────────────────────────────── 62 + echo "Checking local Time Machine volume (UUID=${localUUID})..." 63 + _info=$(/usr/sbin/diskutil info "${localUUID}" 2>/dev/null) 64 + if [ $? -eq 0 ]; then 65 + _mount=$(echo "$_info" | /usr/bin/awk '/Mount Point/ { for(i=3;i<=NF;i++) printf "%s ", $i; print "" }' | /usr/bin/sed 's/ *$//') 66 + if [ -z "$_mount" ] || [ "$_mount" = "Not applicable (no file system)" ]; then 67 + echo " Volume exists but not mounted — mounting..." 68 + /usr/sbin/diskutil mount "${localUUID}" 2>&1 69 + _mount=$(/usr/sbin/diskutil info "${localUUID}" 2>/dev/null | /usr/bin/awk '/Mount Point/ { for(i=3;i<=NF;i++) printf "%s ", $i; print "" }' | /usr/bin/sed 's/ *$//') 70 + fi 71 + if [ -n "$_mount" ] && [ "$_mount" != "Not applicable (no file system)" ]; then 72 + _register_tm "$_mount" 73 + # Local disk found and registered — skip server check. 74 + return 0 2>/dev/null || exit 0 75 + else 76 + echo " Could not mount local TM volume; falling back to server." 77 + fi 78 + else 79 + echo " Local disk not found (UUID=${localUUID}); falling back to server." 80 + fi 81 + ''} 82 + 83 + # ── Backup schedule: weekly (604800 seconds) ─────────────────────────── 84 + /usr/bin/tmutil setbackupinterval 604800 85 + 86 + ${lib.optionalString hasServer '' 87 + # ── Priority 2: SMB server share ─────────────────────────────────── 88 + echo "Checking server Time Machine share (${serverUrl})..." 89 + if /sbin/ping -c1 -W1 server &>/dev/null; then 90 + _register_tm "${serverUrl}" 91 + else 92 + echo " Server unreachable — skipping Time Machine setup." 93 + fi 94 + ''} 95 + ''; 96 + }; 52 97 }
+14
modules/options.nix
··· 661 661 662 662 # ── Darwin ──────────────────────────────────────────────────────────────── 663 663 darwin = { 664 + 665 + # ── External storage ────────────────────────────────────────────────────── 666 + externalDisk = { 667 + timeMachineVolumeUUID = mkOption { 668 + type = nullStr; 669 + default = null; 670 + description = ''Volume UUID of the APFS volume to use for local Time Machine backups. 671 + Obtain after running: 672 + sudo diskutil apfs addVolume disk5 APFS "Time Machine" 673 + diskutil info "/Volumes/Time Machine" | grep "Volume UUID" 674 + ''; 675 + }; 676 + }; 677 + 664 678 keyboard = { 665 679 enableKeyMapping = mkOption { 666 680 type = bool;