Table of contents
- The simplest thing first
- UART Console
- Reading the manual (wiki)
- Initial configuration
- That's Nix though, I want to run Lix
- Not-so-brief storage optimisation detour
- Actually installing Lix now?
I'm running 3 Odroid HC4 storage servers, they're really neat, especially if you don't use the provided toaster case. For the past few weeks I have been slowly migrating all my computers to NixOS and I'd very much like to migrate these aarch64 SBCs as well, especially since I've been running Armbian on them which is missing a lot of packages I need. I was scared of installing NixOS on ARM because the docs seem to be written for people who know what they're doing, and I still feel like I don't know anything about NixOS.
The simplest thing first
Starting off with
NixOS on ARM/Installation
wiki page I downloaded a prebuilt image from
hydra.
Specifically nixos-sd-image-24.11pre650378.655a58a72a66-aarch64-linux.img
which was the latest one when I did this.
Then, after making a backup of it, I tried flashing the image directly to the SD card and (somewhat expectedly) nothing happened, the "alive" LED stayed off, which means no kernel has booted, or that there are missing drivers.
UART Console
To be able to debug this I'll need a console to see what the firmware/uboot is doing. The board's UART port is documented on the odroid-hc4:hardware page of the Hardkernel/Odroid wiki. It has this cute ascii art drawing of it:
_____UART____
|Pin 4 - GND|
|Pin 3 - RXD|
|Pin 2 - TXD|
|Pin 1 - VCC|
\___________|
CON5
3.3V LVTTL
I've connected a USB-UART adapter to GND/RX/TX pins of the connector and on
the second try when I got the RX/TX order right running sterm /dev/ttyUSB0
gave me some output, but it was a bunch of unicode symbol placeholders and some
nonsensical chinese characters :(
Reading the manual (wiki)
Then I noticed there are steps for patching the default image for Odroid HC4 on its own NixOS on ARM/ODROID-HC4 wiki page on the NixOS wiki. The most interesting bit is this shell snippet. (I've already removed Petitboot when installing Armbian years ago and I don't use either HDMI nor the builtin fan.)
# Clone content of samueldr's wip/odroidc4 branch, edit the defconfig file, and build
git clone https://github.com/samueldr/nixpkgs --depth 1 -b wip/odroidc4 && cd nixpkgs
test "$(uname)" '==' 'Darwin' && sed -i '' 's/defconfig = "odroid-c4_defconfig"/defconfig = "odroid-hc4_defconfig"/' pkgs/misc/uboot/default.nix || sed -i 's/defconfig = "odroid-c4_defconfig"/defconfig = "odroid-hc4_defconfig"/' pkgs/misc/uboot/default.nix
nix-build -I "nixpkgs=$PWD" -A pkgsCross.aarch64-multiplatform.ubootOdroidC4
sudo dd if=result/u-boot.bin of=PATH/TO/nixos-sd-image-21.05.XXXX.XXXXXXXX-aarch64-linux.img conv=fsync,notrunc bs=512 seek=1
Just in case it gets lost (like the original PR nixpkgs/#101454 this branch is a backup of) I've setup my own mirror.
The wierd test && sed
shell invocation results in this diff (after changing
the ==
to =
to make it run in fish):
--- a/pkgs/misc/uboot/default.nix
+++ b/pkgs/misc/uboot/default.nix
@@ -273,7 +273,7 @@ in {
};
ubootOdroidC4 = buildUBoot {
- defconfig = "odroid-c4_defconfig";
+ defconfig = "odroid-hc4_defconfig";
postBuild = ''
${buildPackages.meson64-tools}/bin/pkg --type bl30 --output bl30_new.bin \
Running the nix-build invocation requires allowing unfree code, presumably to build Hardkernel firmware.
set -gx NIXPKGS_ALLOW_UNFREE 1
nix-build -I "nixpkgs=$PWD" -A pkgsCross.aarch64-multiplatform.ubootOdroidC4
The result directory also contains a script sd_fusing.sh
which contains the
same dd invocation as the wiki page above.
dd if=result/u-boot.bin of=nixos-sd-image-24.11pre650378.655a58a72a66-aarch64-linux.img conv=fsync,notrunc bs=512 seek=1
2229+1 records in
2229+1 records out
1141616 bytes (1,1 MB, 1,1 MiB) copied, 0,02349 s, 48,6 MB/s
Huh, that's not much data, what did it touch?
Disk /dev/mmcblk0: 7,49 GiB, 8044675072 bytes, 15712256 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x2178694e
Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 16384 77823 61440 30M b W95 FAT32
/dev/mmcblk0p2 * 77824 15712255 15634432 7,5G 83 Linux
We skipped the first 1 block/512 bytes of the partition table and then wrote 2229 blocks, but the weirdly tiny 30MB BOOT (?) partition doesn't even start until block 16384 (8MB in). Is that where the board looks for a bootloader?
Anyway, I flashed the patched image and now it boots! Sort of, I still get a bunch of garbage in the UART but the "alive" LED starts to blink, suggesting a linux kernel with the right drivers has started up.
OH so the default parameters of sterm
must be wrong, because minicom
works
and I can see some output:
U-Boot 2022.01 (Jan 10 2022 - 18:46:34 +0000) odroid-hc4
Model: Hardkernel ODROID-HC4
SoC: Amlogic Meson SM1 (S905X3) Revision 2b:c (10:2)
DRAM: 3.8 GiB
MMC: sd@ffe05000: 0
Loading Environment from nowhere... OK
In: serial
Out: serial
Err: serial
Board variant: hc4
Net: eth0: ethernet@ff3f0000
Hit any key to stop autoboot: 0
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:2...
Found /boot/extlinux/extlinux.conf
Retrieving file: /boot/extlinux/extlinux.conf
1: NixOS - Default
Retrieving file: /boot/extlinux/../nixos/ghywydzklfd8hn3cwazdchyfypb7155s-initrd-linux-6.9.8-initrd
Retrieving file: /boot/extlinux/../nixos/k4jjifq6lnc830xpmdb49wcjgas3p1ck-linux-6.9.8-Image
append: init=/nix/store/53nsmqr561s0a7d9ry6zd547c71ywrg9-nixos-system-nixos-24.11pre650378.655a58a72a66/init console=ttyS0,115200n8 console=ttyAMA0,115200n8 console=tty0 loglevel=7
Retrieving file: /boot/extlinux/../nixos/k4jjifq6lnc830xpmdb49wcjgas3p1ck-linux-6.9.8-dtbs/amlogic/meson-sm1-odroid-hc4.dtb
Moving Image from 0x8080000 to 0x8200000, end=cb20000
## Flattened Device Tree blob at 08008000
Booting using the fdt blob at 0x8008000
Loading Ramdisk to 3e8d8000, end 3ffff8fe ... OK
Loading Device Tree to 000000003e8c2000, end 000000003e8d7842 ... OK
Starting kernel ...
Then nothing, looks like serial is not enabled in Linux - I know it has to be booted thanks to the "alive" LED.
Out of curiosity I tried starting it up again after removing the SD card to see
if there are sensible characters with no boot device with minicom, there indeed
were. The bit after the LOOP:n
repeats indefinitely every few seconds. It
looks like a loop searching for bootable devices.
SM1:BL:511f6b:81ca2f;FEAT:A0F83180:20282000;POC:A;RCY:0;SPINOR:0;CHK:1F;EMMC:800;NAND:81;SD?:20000;USB:8;LOOP:1;SPINOR:0;CHK:1F;EMMC:800;NAND:81;SD?:20000;USB:8;LOOP:2;
Looking into the BOOT FIRMWARE partition (that explains why it's so small, there are no kernels here) are a lot of files that seem irrelevant for us.
size filename
512 armstub8-gic.bin
55808 bcm2711-rpi-400.dtb
55804 bcm2711-rpi-4-b.dtb
56444 bcm2711-rpi-cm4.dtb
53215 bcm2711-rpi-cm4s.dtb
52476 bootcode.bin
946 config.txt
3210 fixup4cd.dat
5436 fixup4.dat
8423 fixup4db.dat
8425 fixup4x.dat
3210 fixup_cd.dat
7303 fixup.dat
10266 fixup_db.dat
10266 fixup_x.dat
809884 start4cd.elf
3754312 start4db.elf
2257216 start4.elf
3004808 start4x.elf
809884 start_cd.elf
4826216 start_db.elf
2981376 start.elf
3728456 start_x.elf
595072 u-boot-rpi3.bin
644328 u-boot-rpi4.bin
Several U-Boot images for various SBCs, Raspberry Pi DTBs, and a bunch of
unrecognizable to me .dat
files. But there is also a config.txt
file like in
Raspberry Pi images (and unlike the Armbian image that's currently running on my
other HC4). I don't know if it's even used on the odroid, but it seems the UART
console is enabled:
[all]
# Boot in 64-bit mode.
arm_64bit=1
# U-Boot needs this to work, regardless of whether UART is actually used or not.
# Look in arch/arm/mach-bcm283x/Kconfig in the U-Boot tree to see if this is still
# a requirement in the future.
enable_uart=1
Next thing to check is the kernel console=
parameter. We can
helpfully see them in the U-Boot log above we can see NixOS tries to set
console=ttyS0,115200n8
, console=ttyAMA0,115200n8
and console=tty0
. However
checking /proc/cmdline on my running HC4 shows console=ttyAML0,115200
and
console=tty1
, we need to change that.
Since it doesn't seem like the kernel command line is set in the FIRMWARE
partition (even rg -uuu -F console=
found nothing in those binary files) let's
look at the larger NIXOS_SD partition. Searching for console=
returned a lot
of results from the nix store which I can't touch but also
/boot/extlinux/extlinux.conf
which looks like the perfect target! I just
have to remember to also change the value in configuration.nix
before running
nixos-rebuild the first time.
# Generated file, all changes will be lost on nixos-rebuild!
# Change this to e.g. nixos-42 to temporarily boot to an older configuration.
DEFAULT nixos-default
MENU TITLE ------------------------------------------------------------
TIMEOUT 50
LABEL nixos-default
MENU LABEL NixOS - Default
LINUX ../nixos/k4jjifq6lnc830xpmdb49wcjgas3p1ck-linux-6.9.8-Image
INITRD ../nixos/ghywydzklfd8hn3cwazdchyfypb7155s-initrd-linux-6.9.8-initrd
APPEND init=/nix/store/53nsmqr561s0a7d9ry6zd547c71ywrg9-nixos-system-nixos-24.11pre650378.655a58a72a66/init console=ttyS0,115200n8 console=ttyAMA0,115200n8 console=tty0 loglevel=7
FDTDIR ../nixos/k4jjifq6lnc830xpmdb49wcjgas3p1ck-linux-6.9.8-dtbs
- APPEND init=/nix/store/53nsmqr561s0a7d9ry6zd547c71ywrg9-nixos-system-nixos-24.11pre650378.655a58a72a66/init console=ttyS0,115200n8 console=ttyAMA0,115200n8 console=tty0 loglevel=7
+ APPEND init=/nix/store/53nsmqr561s0a7d9ry6zd547c71ywrg9-nixos-system-nixos-24.11pre650378.655a58a72a66/init console=ttyAML0,115200 console=tty1 loglevel=7
And it works, I have a NixOS bash console over UART!
Initial configuration
Unfortunately /etc/nixos
is empty, it doesn't contain the configuration which
was used to generate the installation image, so I'll have to make a working
config from scratch-ish. Or do I?
NixOS on ARM/Initial Configuration
says to use nixos-generate-config
and it seems to magically generate a config
that looks like it could work? At least it sets the bootloader to extlinux. I'll
need to look into how nixos-generate-config
even works some time.
I've used nix-env -iA nixos.helix
to install helix, which looked very funky
over the serial console but it worked, and edited the generated file with what I
thought I'd need. Most notably:
# bootloader, as generated by nixos-generate-config, i left that in place
boot.loader.grub.enable = false;
boot.loader.generic-extlinux-compatible.enable = true;
# use latest kernel and add UART console config
boot.kernelPackages = pkgs.linuxPackages_latest;
# with an experiment i've determined only this console is needed
boot.kernelParams = ["console=ttyAML0,115200"];
# systemd-networkd configuration with DHCP on all ports
networking.networkmanager.enable = false;
networking.useNetworkd = true;
networking.useDHCP = true;
# systemd-resolved
services.resolved.enable = true;
# my user
users.users."max" = {
isNormalUser = true;
extraGroups = ["wheel"];
};
security.sudo.wheelNeedsPassword = false;
# some basic packages i'll need for further configuration
environment.systemPackages = with pkgs; [ git helix ];
Then sudo nixos-rebuild boot
and then reboot. And it works again! I shouldn't
be surprised when NixOS works, it's almost always worked so far, but it's still
a little suspicious to me when a computer works the first time.
After a bit of gentle editting it's now part of my nix configuration.
That's Nix though, I want to run Lix
So if you for some reason read all the relevant code from the link above,
you may have noticed that I use Lix for all my other machines, but I made an
exception for this one. The reason is that I have to compile Lix on the target
system, I don't have any more powerful aarch64 machines I could use as a remote
builder and this one would both take forever and definitely run out of disk
space since I only have 8GB root and it's getting full. Right after running
nix-collect-garbage --delete-old
we're sitting at 71% usage!
Filesystem Size Used Avail Use% Mounted on
devtmpfs 188M 0 188M 0% /dev
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 937M 8.3M 929M 1% /run
tmpfs 1.9G 2.2M 1.9G 1% /run/wrappers
/dev/mmcblk0p2 7.3G 4.9G 2.1G 71% /
tmpfs 375M 4.0K 375M 1% /run/user/1001
Not-so-brief storage optimisation detour
There is a handy page on the (old, unofficial) NixOS wiki on
Storage optimization.
Thanks to nix-store --gc --print-roots
I found that I left behind a result
symlink to the pre-flake system in /etc/nixos
which almost doubled the space
needed /o\ Removing it saved me ~1.5GB. Then nix-store --optimize
saved
another 200MB. Now we're sitting at 3.1GB - 44% disk usage, that's not lean by
any measure but at least less catastrophic.
Using du
the next biggest space hog I found is /var/log
, since we're booting
off of a MicroSD card it'd be a good idea to move it to tmpfs anyway, so let's
do that and save a bit of space too.
zram-generator failure in NixOS
I have wanted to use the services.zram-generator
configuration to setup
3 zram devices: one backing /var/log
, one backing /tmp
and one for
swap. Unfortunately there seems to be an issue somewhere (in the NixOS
module?) and I couldn't get zram devices for anything that wasn't swap setup
using zram-generator. I can set them up and mount them manually but using
the zram-generator services always resulted in the device getting double
initialized, which breaks the service (even though the device is there and
initialized) the systemd dependency chain for the mounts so nothing useful comes
out of it. Here some quick info in case someone (future me probably) wants to
debug this properly.
systemctl status systemd-zram-setup@zram1.service
[...]
systemd[1]: Starting Create swap on /dev/zram1...
zram-generator[760]: Error: Failed to configure disk size into /sys/block/zram1/disksize
zram-generator[760]: Caused by:
zram-generator[760]: Device or resource busy (os error 16)
systemd[1]: systemd-zram-setup@zram1.service: Main process exited, code=exited, status=1/FAILURE
systemd[1]: systemd-zram-setup@zram1.service: Failed with result 'exit-code'.
systemd[1]: Failed to start Create swap on /dev/zram1.
dmesg
[...]
zram: Cannot change disksize for initialized device
echo 1 > /sys/block/zram1/disksize
-bash: echo: write error: Device or resource busy
Manually trying to run the command of the service works if I reset the device first.
systemctl cat systemd-zram-setup@zram1.service
[...]
ExecStart=/nix/store/64hb4vgqx2qqv901w9z7x60yxdmrs74h-zram-generator-1.1.2/lib/systemd/system-generators/zram-generator --setup-device '%i'
ExecStop=/nix/store/64hb4vgqx2qqv901w9z7x60yxdmrs74h-zram-generator-1.1.2/lib/systemd/system-generators/zram-generator --reset-device '%i'
# works
/nix/store/64hb4vgqx2qqv901w9z7x60yxdmrs74h-zram-generator-1.1.2/lib/systemd/system-generators/zram-generator --reset-device zram1
/nix/store/64hb4vgqx2qqv901w9z7x60yxdmrs74h-zram-generator-1.1.2/lib/systemd/system-generators/zram-generator --setup-device zram1
# doesn't work ???
/nix/store/64hb4vgqx2qqv901w9z7x60yxdmrs74h-zram-generator-1.1.2/lib/systemd/system-generators/zram-generator --reset-device zram1
systemctl restart systemd-zram-setup@zram1.service
There must be some other unit initializing the zram device just before the one I'm looking at above, but I couldn't find it and I've burned enough time on this already. So for now using tmpfs will do.
systemd.mounts = [{
what = "tmpfs";
where = "/tmp";
type = "tmpfs";
mountConfig.Options = "mode=1777,strictatime,rw,nosuid,nodev,size=50%";
} {
what = "tmpfs";
where = "/var/log";
type = "tmpfs";
mountConfig.Options = "rw,nodev,size=100M";
}];
zramSwap.enable = true;
And this effort saved us... 100MB, well exactly as much as was in /tmp
and
/var/log
, it was expected. The main benefit of this is less wear on the SD
card.
Can I boot it from btrfs?? (and enable compression)
One friend suggested on mastodon, well we sure can try?
I believe the easiest way to swap the filesystem would be to first come up with
UUIDs (uuidgen
), write the new filesystem structure in nix config in a way
that assumes the future structure, run nixos-rebuild boot --flake .
, turn the
board off and then repartition the sd card offline on my laptop, using the UUIDs
I made up for those partitions earlier. Booting the new config afterwards should
work? On a non-NixOS system I'd edit the /etc/fstab
manually after
repartitioning but that seems like a recipe for disaster on NixOS.
--- a/hosts/lemon/hardware.nix
+++ b/hosts/lemon/hardware.nix
@@ -22,7 +22,12 @@
fileSystems = {
"/" = {
- device = "/dev/disk/by-uuid/44444444-4444-4444-8888-888888888888";
+ device = "/dev/disk/by-uuid/78fae9ea-182e-41e7-a390-45ab1a153c4d";
+ fsType = "btrfs";
+ options = ["rw" "relatime" "lazytime" "compress=zstd:3" "subvol=@"];
+ };
+ "/boot" = {
+ device = "/dev/disk/by-uuid/14e841ee-a326-4168-b6c3-5893c781e134";
fsType = "ext4";
};
};
After making a backup of both the fully working ext4 setup using
dd if=/dev/ mmcblk0
as a raw image - in case I need an easy way to restore
later. And the prepared-to-boot-from-btrfs setup using rsync -a
. Now we can
start poking the partitions. This is what the original layout looked like:
Disk /dev/mmcblk0: 7,49 GiB, 8044675072 bytes, 15712256 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x2178694e
Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 16384 77823 61440 30M b W95 FAT32
/dev/mmcblk0p2 * 77824 15712255 15634432 7,5G 83 Linux
Considering how much the files take up,
23M FIRMWARE
161M BOOT
5,7G NIXOS
I'll keep the 30MB firmware partition untouched, and give the BOOT partition
500MB to fit ~3 kernel builds. The rest will be taken up by the new btrfs root
partition. Using fdisk
's CLI:
> d
> 2
Partition 2 has been deleted.
> n
> p
> 2
> 77824
> +500M
Created a new partition 2 of type 'Linux' and of size 500 MiB.
> a
> 2
The bootable flag on partition 2 is enabled now.
> F
Unpartitioned space /dev/mmcblk0: 6,97 GiB, 7487881216 bytes, 14624768 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
Start End Sectors Size
2048 16383 14336 7M
1101824 15712255 14610432 7G
> n
> p
> 3
> 1101824
> 15712255
Created a new partition 3 of type 'Linux' and of size 7 GiB.
> p
Disk /dev/mmcblk0: 7,49 GiB, 8044675072 bytes, 15712256 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x2178694e
Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 16384 77823 61440 30M b W95 FAT32
/dev/mmcblk0p2 * 77824 1101823 1024000 500M 83 Linux
/dev/mmcblk0p3 1101824 15712255 14610432 7G 83 Linux
Now we need to recreate the presumed filesystems.
mkfs.ext4 -U 14e841ee-a326-4168-b6c3-5893c781e134 -L BOOT /dev/mmcblk0p2
mkfs.btrfs -U 78fae9ea-182e-41e7-a390-45ab1a153c4d -L NIXOS /dev/mmcblk0p3
mount /dev/mmcblk0p3 /mnt/sd
btrfs subvolume create /mnt/sd/@
umount /mnt/sd
mount -o rw,relatime,lazytime,compress=zstd:3,subvol=@ /dev/mmcblk0p3 /mnt/sd
mkdir /mnt/sd/boot
mount /dev/mmcblk0p2 /mnt/sd/boot
And restore using rsync -a
again. Now comes the moment of thruth, did my
"definitely the easiest way" work at all? It boots!!
And how much space did we save?
Filesystem Size Used Avail Use% Mounted on
devtmpfs 188M 0 188M 0% /dev
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 937M 8.4M 929M 1% /run
tmpfs 1.9G 2.2M 1.9G 1% /run/wrappers
/dev/mmcblk0p3 7.0G 1.6G 4.8G 26% /
tmpfs 1.9G 0 1.9G 0% /tmp
tmpfs 100M 2.8M 98M 3% /var/log
/dev/mmcblk0p2 459M 160M 271M 38% /boot
tmpfs 375M 4.0K 375M 1% /run/user/1001
Nice! Filesystem compression is pretty overpowered, I should do this with every SBC. Now it's time to, uhh, try Lix? Maybe all the build deps will fit if they also compress this well. We've effectively made our 8GB card into a 16GB one.
Actually installing Lix now?
In the meantime I've also enabled my desktop elderberry
as a remote aarch64
builder, it's a 4 core A53 chil with only 4GB of memory, it's going to take
forever if I build it locally.
--- a/hosts/elderberry/default.nix
+++ b/hosts/elderberry/default.nix
@@ -20,6 +20,9 @@
boot.kernelModules = ["kvm-amd"];
boot.extraModulePackages = [];
+ # enable building aarch64 packages from remote hosts
+ boot.binfmt.emulatedSystems = ["aarch64-linux"];
+
hardware.graphics = {
enable = true;
enable32Bit = true;
--- a/hosts/lemon/default.nix
+++ b/hosts/lemon/default.nix
@@ -26,8 +26,18 @@
time.timeZone = "Europe/Prague";
- nix.settings = {
- experimental-features = ["nix-command" "flakes"];
- trusted-users = ["root" "@wheel"];
+ nix = {
+ distributedBuilds = true;
+ buildMachines = [{
+ hostName = "elderberry";
+ system = "aarch64-linux";
+ maxJobs = 4;
+ }];
+ settings = {
+ experimental-features = ["nix-command" "flakes"];
+ trusted-users = ["root" "@wheel"];
+ max-jobs = 1;
+ cores = 2;
+ };
};
}
Now add the Lix module to lemon
, pray and run nixos-rebuild boot --flake .
.
--- a/flake.nix
+++ b/flake.nix
@@ -223,6 +223,7 @@
system = "aarch64-linux";
specialArgs = { inherit inputs; };
modules = [
+ lix-module.nixosModules.default
./modules/nebula.nix
./modules/openssh.nix
./hosts/lemon
Unfortunately I was not able to build Lix like that, neither through the remote (thanks to some issues with the qemu-user builder, some lix functional tests didn't finish) nor locally (there is not enough space on the tmpfs /tmp). So for now we'll have to settle for using Lix from nixpkgs.
--- a/flake.nix
+++ b/flake.nix
@@ -223,6 +223,7 @@
system = "aarch64-linux";
specialArgs = { inherit inputs; };
modules = [
- lix-module.nixosModules.default
+ ({ pkgs, ... }: { nix.package = pkgs.lix; })
./modules/nebula.nix
./modules/openssh.nix
./hosts/lemon