My nix-darwin and NixOS config
3
fork

Configure Feed

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

macmini: simplify Time Machine to local disk only, add setup doc

- Drop SMB server fallback from the activation script; local APFS
volume is the only supported destination
- Script skips gracefully if the disk isn't plugged in at rebuild time
- Add docs/time-machine.md covering one-time volume creation steps
- Clean up inline comments in macmini/default.nix and options.nix

+97 -76
+58
docs/time-machine.md
··· 1 + # Time Machine Setup 2 + 3 + Backups go to a dedicated APFS volume on the external CT2000X9SSD9 (USB, `disk4`/`disk5`). 4 + The `nrs` activation script handles registration and mounting automatically on every rebuild — 5 + this doc covers the one-time volume creation only. 6 + 7 + ## First-time setup 8 + 9 + ### 1. Create the Time Machine APFS volume 10 + 11 + ```bash 12 + sudo diskutil apfs addVolume disk5 APFS "Time Machine" 13 + ``` 14 + 15 + > `disk5` is the APFS container that lives on `disk4s2`. Run `diskutil list` to confirm 16 + > it's still the right identifier before running this. 17 + 18 + ### 2. Get the volume UUID 19 + 20 + ```bash 21 + diskutil info "/Volumes/Time Machine" | grep "Volume UUID" 22 + ``` 23 + 24 + ### 3. Set the UUID in the config 25 + 26 + In `hosts/macmini/default.nix`: 27 + 28 + ```nix 29 + myConfig.darwin.externalDisk.timeMachineVolumeUUID = "<uuid from step 2>"; 30 + ``` 31 + 32 + ### 4. Rebuild 33 + 34 + ```bash 35 + nrs 36 + ``` 37 + 38 + The activation script will mount the volume if needed and register it as a Time Machine 39 + destination. Backups run on a weekly interval (configured via `tmutil setbackupinterval`). 40 + 41 + ## How it works 42 + 43 + `modules/darwin/system.nix` runs a `system.activationScripts.timeMachineDestination` on 44 + every `nrs`. It: 45 + 46 + 1. Looks up the volume by UUID via `diskutil info` 47 + 2. Mounts it if it exists but isn't mounted 48 + 3. Registers it with `tmutil setdestination -a` if not already registered 49 + 4. Skips silently if the disk isn't plugged in 50 + 51 + Setting `myConfig.darwin.externalDisk.timeMachineVolumeUUID = null` disables the script 52 + entirely. 53 + 54 + ## Server Time Machine (not configured) 55 + 56 + A Samba/netatalk-based network Time Machine destination on the server is not currently set up. 57 + If desired in future, the relevant option is `myConfig.server.timemachine` — see 58 + `modules/options.nix` for the available sub-options.
+1 -9
hosts/macmini/default.nix
··· 28 28 system.defaults.smb.NetBIOSName = "macmini"; 29 29 30 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. 31 + # See docs/time-machine.md for first-time setup instructions. 40 32 myConfig.darwin.externalDisk.timeMachineVolumeUUID = "9217DB34-722B-4596-8ADD-20C8060FC257"; 41 33 42 34 # Timezone — driven from myConfig.timeZone
+31 -57
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. 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 28 + # activation script. Skipped entirely if timeMachineVolumeUUID is null. 32 29 # 33 - # Server first-time setup (one-off, interactive — stores password in macOS keychain): 34 - # sudo tmutil setdestination -p smb://<user>@server/TimeMachine 35 - # After that, nrs registers it automatically on every rebuild without prompting. 30 + # The volume is auto-mounted if it exists but isn't mounted yet. 31 + # Idempotent: skipped if the destination is already registered. 36 32 system.activationScripts.timeMachineDestination = lib.mkIf 37 - (cfg.darwin.externalDisk.timeMachineVolumeUUID != null || cfg.server.timemachine.enable) 33 + (cfg.darwin.externalDisk.timeMachineVolumeUUID != null) 38 34 { 39 35 text = 40 36 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; 37 + uuid = cfg.darwin.externalDisk.timeMachineVolumeUUID; 45 38 in 46 39 '' 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 - } 40 + echo "Checking local Time Machine volume (UUID=${uuid})..." 41 + _info=$(/usr/sbin/diskutil info "${uuid}" 2>/dev/null) 42 + if [ $? -ne 0 ]; then 43 + echo " Disk not found — skipping Time Machine setup." 44 + exit 0 45 + fi 59 46 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 - ''} 47 + _mount=$(echo "$_info" | /usr/bin/awk '/Mount Point/ { for(i=3;i<=NF;i++) printf "%s ", $i; print "" }' | /usr/bin/sed 's/ *$//') 48 + if [ -z "$_mount" ] || [ "$_mount" = "Not applicable (no file system)" ]; then 49 + echo " Volume not mounted — mounting..." 50 + /usr/sbin/diskutil mount "${uuid}" 2>&1 51 + _mount=$(/usr/sbin/diskutil info "${uuid}" 2>/dev/null | /usr/bin/awk '/Mount Point/ { for(i=3;i<=NF;i++) printf "%s ", $i; print "" }' | /usr/bin/sed 's/ *$//') 52 + fi 82 53 83 - # ── Backup schedule: weekly (604800 seconds) ─────────────────────────── 84 - /usr/bin/tmutil setbackupinterval 604800 54 + if [ -z "$_mount" ] || [ "$_mount" = "Not applicable (no file system)" ]; then 55 + echo " Could not mount volume — skipping Time Machine setup." 56 + exit 0 57 + fi 85 58 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 - ''} 59 + if /usr/bin/tmutil destinationinfo 2>/dev/null | /usr/bin/grep -qF "$_mount"; then 60 + echo " Already registered as Time Machine destination, skipping." 61 + else 62 + echo " Registering $_mount as Time Machine destination..." 63 + /usr/bin/tmutil setdestination -a "$_mount" 2>&1 || \ 64 + echo " WARNING: tmutil setdestination failed." 65 + fi 66 + 67 + # Weekly backup interval (604800 seconds = 7 days) 68 + /usr/bin/tmutil setbackupinterval 604800 95 69 ''; 96 70 }; 97 71 }
+7 -10
modules/options.nix
··· 663 663 darwin = { 664 664 665 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 - }; 666 + externalDisk.timeMachineVolumeUUID = mkOption { 667 + type = nullStr; 668 + default = null; 669 + description = '' 670 + Volume UUID of the APFS volume to use for local Time Machine backups. 671 + Set to null to disable. See docs/time-machine.md for setup instructions. 672 + ''; 676 673 }; 677 674 678 675 keyboard = {