···11+# Migrating /srv to RAID
22+33+This guide covers moving the server's `/srv` data disk from a single drive to a
44+software RAID array when new hardware arrives. No data should be lost if the
55+steps are followed in order.
66+77+---
88+99+## Choosing a RAID Level
1010+1111+| Level | Drives needed | Usable space | Fault tolerance | Use case |
1212+|---|---|---|---|---|
1313+| **RAID 1** | 2 | 50% | 1 drive failure | Best for a 2-drive home server — simple, safe |
1414+| **RAID 5** | 3+ | n−1 drives | 1 drive failure | Good space efficiency at 3+ drives |
1515+| **RAID 6** | 4+ | n−2 drives | 2 drive failures | Only worth it at 4+ drives |
1616+| **RAID 10** | 4 | 50% | 1 per mirrored pair | Fast + redundant, needs 4 drives |
1717+1818+**Recommendation for home use: RAID 1 with 2 drives.** It's the most
1919+straightforward to set up and recover from, and NixOS supports it natively
2020+via `mdadm`. If you end up with 3+ drives, RAID 5 is a reasonable step up.
2121+2222+---
2323+2424+## Overview of Changes
2525+2626+1. Back up all `/srv` data off-server
2727+2. Wipe and assemble the new RAID array with `mdadm`
2828+3. Update `modules/server/storage.nix` and `modules/options.nix` to point at
2929+ the new RAID device
3030+4. Restore data and rebuild
3131+3232+---
3333+3434+## Step 1 — Back Up /srv
3535+3636+Do this **before touching any disks**.
3737+3838+```bash
3939+# On the server — snapshot /srv to an external drive or remote destination
4040+# (adjust destination as needed)
4141+4242+# Option A: rsync to external USB drive mounted at /mnt/backup
4343+sudo rsync -aHAXv /srv/ /mnt/backup/srv-snapshot/
4444+4545+# Option B: rsync over SSH to your macmini
4646+rsync -aHAXz -e ssh ewan@server:/srv/ ~/srv-backup/
4747+4848+# Verify the backup looks complete
4949+ls -la /mnt/backup/srv-snapshot/
5050+du -sh /mnt/backup/srv-snapshot/
5151+```
5252+5353+Don't proceed until you're confident the backup is good.
5454+5555+---
5656+5757+## Step 2 — Identify the New Drives
5858+5959+Boot with the new drives attached and identify their device paths:
6060+6161+```bash
6262+lsblk -o NAME,SIZE,MODEL,SERIAL,TYPE
6363+```
6464+6565+You're looking for the two (or more) new blank drives. Note their paths —
6666+e.g. `/dev/sdb` and `/dev/sdc`. Double-check by size and model; **do not
6767+mistake the OS drive or your current `/srv` drive for a blank one**.
6868+6969+```bash
7070+# Confirm a drive is blank / has no important filesystem
7171+sudo wipefs /dev/sdb
7272+sudo wipefs /dev/sdc
7373+```
7474+7575+---
7676+7777+## Step 3 — Assemble the RAID Array
7878+7979+### RAID 1 (2 drives — recommended)
8080+8181+```bash
8282+# Create the array — this is destructive on /dev/sdb and /dev/sdc
8383+sudo mdadm --create /dev/md0 \
8484+ --level=1 \
8585+ --raid-devices=2 \
8686+ /dev/sdb /dev/sdc
8787+8888+# Monitor initial sync progress (takes minutes to hours depending on drive size)
8989+watch cat /proc/mdstat
9090+```
9191+9292+### RAID 5 (3+ drives)
9393+9494+```bash
9595+sudo mdadm --create /dev/md0 \
9696+ --level=5 \
9797+ --raid-devices=3 \
9898+ /dev/sdb /dev/sdc /dev/sdd
9999+100100+watch cat /proc/mdstat
101101+```
102102+103103+> You can use the array before the initial sync finishes — it just runs slower.
104104+105105+### Format and label the array
106106+107107+```bash
108108+sudo mkfs.ext4 -L srv /dev/md0
109109+```
110110+111111+> The `storage.nix` auto-format service checks for `TYPE=` from `blkid` and
112112+> skips formatting if the filesystem is already present, so it is safe to
113113+> format manually here. The service will see it's already formatted and do nothing.
114114+115115+### Get the RAID device UUID
116116+117117+```bash
118118+sudo blkid /dev/md0
119119+# → /dev/md0: LABEL="srv" UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="ext4"
120120+121121+# Also note the mdadm array UUID for the config:
122122+sudo mdadm --detail /dev/md0 | grep "UUID"
123123+```
124124+125125+---
126126+127127+## Step 4 — Update the NixOS Config
128128+129129+### 4a. Add the RAID device path to `options.nix`
130130+131131+In `modules/options.nix`, update the storage device default to point at the
132132+RAID device instead of the raw disk UUID:
133133+134134+```nix
135135+# modules/options.nix
136136+server.storage.srv = {
137137+ device = mkOption {
138138+ type = str;
139139+ # Was: "/dev/disk/by-uuid/<old-single-drive-uuid>"
140140+ default = "/dev/disk/by-label/srv"; # ← RAID array is labelled "srv"
141141+ description = "Block device for /srv (RAID md0 or raw disk).";
142142+ };
143143+ # fsType and options stay the same
144144+};
145145+```
146146+147147+Using `by-label` rather than `by-uuid` is fine here because the label `srv`
148148+is set on the RAID array itself, not an individual drive. Alternatively, use
149149+`/dev/disk/by-uuid/<md0-uuid>` if you prefer UUID-stable references.
150150+151151+### 4b. Enable mdadm in `modules/server/storage.nix`
152152+153153+Add mdadm configuration so NixOS assembles the array at boot:
154154+155155+```nix
156156+# modules/server/storage.nix
157157+{ config, pkgs, ... }:
158158+let
159159+ srv = config.myConfig.server.storage.srv;
160160+ device = srv.device;
161161+in
162162+{
163163+ # ── mdadm — assemble RAID at boot ──────────────────────────────────────────
164164+ boot.swraid = {
165165+ enable = true;
166166+ mdadmConf = ''
167167+ MAILADDR root
168168+ '';
169169+ };
170170+171171+ # ── 1. Auto-format (unchanged — skips if already formatted) ────────────────
172172+ systemd.services."srv-autoformat" = {
173173+ # ... (no changes needed here)
174174+ };
175175+176176+ # ── 2. /srv mount (unchanged) ──────────────────────────────────────────────
177177+ fileSystems."/srv" = {
178178+ # ... (no changes needed here)
179179+ };
180180+181181+ # ── 3. Subdirectory creation (unchanged) ────────────────────────────────────
182182+ systemd.tmpfiles.rules = [
183183+ # ... (no changes needed here)
184184+ ];
185185+}
186186+```
187187+188188+The only real addition is the `boot.swraid` block. Everything else in the
189189+module stays the same because it already references `device` from the option.
190190+191191+### 4c. (Optional) Add mdadm monitoring
192192+193193+For email alerts on drive failure, add to `modules/server/storage.nix`:
194194+195195+```nix
196196+# Requires postfix or another MTA to be configured separately
197197+environment.etc."mdadm.conf".text = ''
198198+ MAILADDR root
199199+ ARRAY /dev/md0 UUID=<array-uuid-from-step-3>
200200+'';
201201+202202+systemd.services.mdmonitor = {
203203+ wantedBy = [ "multi-user.target" ];
204204+};
205205+```
206206+207207+---
208208+209209+## Step 5 — Restore Data
210210+211211+Mount the new array, then rsync the backup back onto it:
212212+213213+```bash
214214+# Test-mount the new array
215215+sudo mkdir -p /mnt/newraid
216216+sudo mount /dev/md0 /mnt/newraid
217217+218218+# Restore from your backup (adjust source path)
219219+sudo rsync -aHAXv /mnt/backup/srv-snapshot/ /mnt/newraid/
220220+221221+# Check ownership is preserved correctly
222222+ls -la /mnt/newraid/forgejo
223223+ls -la /mnt/newraid/postgresql
224224+225225+sudo umount /mnt/newraid
226226+```
227227+228228+---
229229+230230+## Step 6 — Apply the Config and Reboot
231231+232232+```bash
233233+cd /home/ewan/.config/nix-config
234234+235235+# Rebuild and switch (this writes the new fstab entry and mdadm config)
236236+sudo nixos-rebuild switch --flake .#server
237237+238238+# Reboot so the array is assembled cleanly from the start
239239+sudo reboot
240240+```
241241+242242+After reboot, verify:
243243+244244+```bash
245245+# Array is up and healthy
246246+cat /proc/mdstat
247247+sudo mdadm --detail /dev/md0
248248+249249+# /srv is mounted correctly
250250+mount | grep /srv
251251+df -h /srv
252252+253253+# Services came back up
254254+systemctl status forgejo nextcloud bluesky-pds
255255+256256+# Data looks right
257257+ls -la /srv/
258258+```
259259+260260+---
261261+262262+## Ongoing Maintenance
263263+264264+### Check array health
265265+266266+```bash
267267+cat /proc/mdstat
268268+sudo mdadm --detail /dev/md0
269269+```
270270+271271+### Trigger a manual scrub (checks for silent corruption)
272272+273273+```bash
274274+echo check | sudo tee /sys/block/md0/md/sync_action
275275+watch cat /proc/mdstat
276276+```
277277+278278+It's worth adding a weekly scrub as a systemd timer — NixOS has
279279+`boot.swraid`-compatible options for this, or you can write a simple oneshot
280280+timer in `modules/server/maintenance.nix`.
281281+282282+### Replacing a failed drive
283283+284284+```bash
285285+# Mark the failed drive as faulty and remove it
286286+sudo mdadm /dev/md0 --fail /dev/sdb
287287+sudo mdadm /dev/md0 --remove /dev/sdb
288288+289289+# Physically replace the drive, then add the new one
290290+sudo mdadm /dev/md0 --add /dev/sdb
291291+292292+# Watch the rebuild
293293+watch cat /proc/mdstat
294294+```
295295+296296+---
297297+298298+## Updating This Config for a Fresh Deploy
299299+300300+If you ever rebuild the server from scratch on RAID hardware (rather than
301301+migrating from a single disk), the process is simpler:
302302+303303+1. Assemble and format the RAID array before running `nixos-install`
304304+2. Set `myConfig.server.storage.srv.device` in `hosts/server/default.nix` to
305305+ the correct device path
306306+3. Ensure `boot.swraid.enable = true` is in `storage.nix`
307307+4. The auto-format service will see the existing ext4 and skip formatting
308308+5. Proceed with the normal deploy runbook in `hosts-server.md`
309309+310310+---
311311+312312+## Resources
313313+314314+- [NixOS `boot.swraid` options](https://search.nixos.org/options?query=boot.swraid)
315315+- [Arch Wiki — mdadm](https://wiki.archlinux.org/title/RAID) — comprehensive mdadm reference
316316+- [mdadm man page](https://man.archlinux.org/man/mdadm.8)