nix config
1# Disko configuration for box NAS
2# NVMe boot drive with LUKS + 3x 4TB ZFS RAIDZ1 pool (~8TB usable)
3# Unified encryption: LUKS passphrase unlocks root, ZFS uses keyfile inside encrypted root
4#
5# NOTE: Using RAIDZ1 (3 drives) temporarily due to DOA drive. Can migrate to RAIDZ2
6# with 4 drives later by creating a new pool and copying data.
7#
8# Installation steps:
9#
10# 1. Generate the ZFS keyfile and LUKS password:
11# dd if=/dev/urandom of=/tmp/tank.key bs=32 count=1
12# echo -n "your-luks-password" > /tmp/luks-password
13#
14# 2. Run disko-install:
15# sudo nix run 'github:nix-community/disko/latest#disko-install' -- \
16# --flake ~/helm#box \
17# --disk nvme /dev/disk/by-id/nvme-CT500P310SSD8_2544543B87C2 \
18# --disk zfs1 /dev/disk/by-id/ata-WDC_WD40EFPX-68C6CN0_WD-WX32D954A2J7 \
19# --disk zfs2 /dev/disk/by-id/ata-WDC_WD40EFPX-68C6CN0_WD-WX32D95FVZVL \
20# --disk zfs3 /dev/disk/by-id/ata-WDC_WD40EFPX-68C6CN0_WD-WX42D95M807R
21#
22# 3. Copy the keyfile and update keylocation:
23# sudo mkdir -p /mnt/etc/zfs
24# sudo cp /tmp/tank.key /mnt/etc/zfs/tank.key
25# sudo chmod 000 /mnt/etc/zfs/tank.key
26# sudo zfs set keylocation=file:///etc/zfs/tank.key tank
27{
28 disko.devices = {
29 disk = {
30 # Boot drive - 500GB NVMe with LUKS encryption
31 nvme = {
32 type = "disk";
33 device = "/dev/disk/by-id/nvme-placeholder"; # Override with --disk nvme /dev/disk/by-id/...
34 content = {
35 type = "gpt";
36 partitions = {
37 ESP = {
38 size = "512M";
39 type = "EF00";
40 content = {
41 type = "filesystem";
42 format = "vfat";
43 mountpoint = "/boot";
44 mountOptions = [ "umask=0077" ];
45 };
46 };
47 luks = {
48 size = "100%";
49 content = {
50 type = "luks";
51 name = "cryptroot";
52 settings = {
53 allowDiscards = true;
54 };
55 # Passphrase will be prompted during boot
56 passwordFile = "/tmp/luks-password"; # Only used during install, set this before running disko
57 content = {
58 type = "filesystem";
59 format = "ext4";
60 mountpoint = "/";
61 mountOptions = [ "noatime" ];
62 };
63 };
64 };
65 };
66 };
67 };
68
69 # ZFS pool drives - 3x 4TB in RAIDZ1
70 zfs1 = {
71 type = "disk";
72 device = "/dev/disk/by-id/placeholder-zfs1"; # Override with --disk zfs1 /dev/disk/by-id/...
73 content = {
74 type = "gpt";
75 partitions = {
76 zfs = {
77 size = "100%";
78 content = {
79 type = "zfs";
80 pool = "tank";
81 };
82 };
83 };
84 };
85 };
86 zfs2 = {
87 type = "disk";
88 device = "/dev/disk/by-id/placeholder-zfs2";
89 content = {
90 type = "gpt";
91 partitions = {
92 zfs = {
93 size = "100%";
94 content = {
95 type = "zfs";
96 pool = "tank";
97 };
98 };
99 };
100 };
101 };
102 zfs3 = {
103 type = "disk";
104 device = "/dev/disk/by-id/placeholder-zfs3";
105 content = {
106 type = "gpt";
107 partitions = {
108 zfs = {
109 size = "100%";
110 content = {
111 type = "zfs";
112 pool = "tank";
113 };
114 };
115 };
116 };
117 };
118 };
119
120 zpool = {
121 tank = {
122 type = "zpool";
123 mode = "raidz1";
124 options = {
125 ashift = "12";
126 cachefile = "none"; # Needed for disko
127 };
128 rootFsOptions = {
129 compression = "lz4";
130 atime = "off";
131 xattr = "sa";
132 acltype = "posixacl";
133 # ZFS native encryption
134 # During install: keyfile at /tmp/tank.key
135 # After install: install-box.sh copies to /etc/zfs/tank.key and updates keylocation
136 encryption = "aes-256-gcm";
137 keyformat = "raw";
138 keylocation = "file:///tmp/tank.key";
139 "com.sun:auto-snapshot" = "false";
140 };
141 # Don't mount the pool root directly
142 mountpoint = null;
143
144 datasets = {
145 # /nix is on the NVMe ext4 root, not on ZFS
146 # This simplifies boot dependencies - ZFS is purely for data storage
147
148 # Parent dataset for all data - inherits encryption
149 data = {
150 type = "zfs_fs";
151 options.mountpoint = "none";
152 };
153
154 # Media datasets
155 "data/media" = {
156 type = "zfs_fs";
157 options.mountpoint = "none";
158 };
159 "data/media/music" = {
160 type = "zfs_fs";
161 mountpoint = "/tank/media/music";
162 options.recordsize = "1M"; # Large files benefit from larger recordsize
163 };
164 "data/media/photos" = {
165 type = "zfs_fs";
166 mountpoint = "/tank/media/photos";
167 options.recordsize = "1M";
168 };
169 "data/media/movies" = {
170 type = "zfs_fs";
171 mountpoint = "/tank/media/movies";
172 options.recordsize = "1M";
173 };
174 "data/media/tv" = {
175 type = "zfs_fs";
176 mountpoint = "/tank/media/tv";
177 options.recordsize = "1M";
178 };
179
180 # Other data
181 "data/books" = {
182 type = "zfs_fs";
183 mountpoint = "/tank/books";
184 options."com.sun:auto-snapshot" = "true";
185 };
186 "data/podcasts" = {
187 type = "zfs_fs";
188 mountpoint = "/tank/podcasts";
189 };
190 "data/postgres" = {
191 type = "zfs_fs";
192 mountpoint = "/tank/postgres";
193 options = {
194 recordsize = "16K"; # Better for databases
195 "com.sun:auto-snapshot" = "true";
196 };
197 };
198 "data/syncthing" = {
199 type = "zfs_fs";
200 options.mountpoint = "none";
201 options."com.sun:auto-snapshot" = "true";
202 };
203 "data/syncthing/drawing" = {
204 type = "zfs_fs";
205 mountpoint = "/tank/syncthing/drawing";
206 };
207 "data/backup" = {
208 type = "zfs_fs";
209 mountpoint = "/tank/backup";
210 options."com.sun:auto-snapshot" = "true";
211 };
212 "data/ftp" = {
213 type = "zfs_fs";
214 mountpoint = "/tank/ftp";
215 };
216 "data/cache" = {
217 type = "zfs_fs";
218 mountpoint = "/tank/cache";
219 options."com.sun:auto-snapshot" = "false";
220 };
221 "data/playlists" = {
222 type = "zfs_fs";
223 mountpoint = "/tank/playlists";
224 };
225 "data/new-music" = {
226 type = "zfs_fs";
227 mountpoint = "/tank/new-music";
228 };
229 "data/archive" = {
230 type = "zfs_fs";
231 mountpoint = "/tank/archive";
232 options."com.sun:auto-snapshot" = "true";
233 };
234 };
235 };
236 };
237 };
238}