Hey
I’m relatively new to Nix, but love the approach excited about what I’m learning!
I just published my first NixOS-based project to GitHub: Avunu/web_kiosk
The idea is to create a trimmed-down USB-booting dedicated web kiosk OS that’s preconfigured to connect to a specified wireless network and open a specified portal page.
Because Firefox 121 builds wayland support by default on Linux and is easily configured in a kiosk mode using a runtime switch, running Firefox in Cage on NixOS compiled to an ISO file made perfect sense for my scenario.
The heart of the project is these two files:
flake.nix
:
{
description = "A NixOS live image for a Firefox web kiosk";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
lib = pkgs.lib;
envConfig = import ./build.env.nix;
kioskConfig = import ./kiosk.nix { inherit lib pkgs envConfig; };
disableConfig = import ./disable.nix;
nixosConfig = {
imports = [
kioskConfig
disableConfig
"${nixpkgs}/nixos/modules/installer/cd-dvd/iso-image.nix"
];
};
kioskIso = nixpkgs.lib.nixosSystem {
inherit system;
modules = [ nixosConfig ];
};
in
{
packages.x86_64-linux.default = kioskIso.config.system.build.isoImage;
};
}
kiosk.nix
:
{ lib, pkgs, envConfig, ... }:
{
boot.initrd.systemd.network.wait-online.enable = true;
boot.kernelPackages = pkgs.linuxKernel.packages.linux_zen;
hardware.enableRedistributableFirmware = true;
isoImage.isoName = "kiosk.iso";
isoImage.makeEfiBootable = true;
isoImage.makeUsbBootable = true;
isoImage.squashfsCompression = "xz";
networking.useNetworkd = true;
networking.wireless = lib.optionalAttrs (envConfig.wifiSSID != "") {
enable = true;
networks."${envConfig.wifiSSID}".psk = envConfig.wifiPassword;
};
programs.firefox.enable = true;
services.cage.enable = true;
services.cage.program = "${pkgs.firefox}/bin/firefox -kiosk ${envConfig.startPage}";
services.cage.user = "kiosk";
services.getty.loginProgram = "${pkgs.coreutils}/bin/true";
system.stateVersion = "23.11";
systemd.network.enable = true;
time.timeZone = envConfig.timeZone;
users.users.kiosk.isNormalUser = true;
zramSwap.enable = true;
}
The thing is, I’m aiming for an image that’s as minimal as possible, and flashing a several GBs for a Linux OS with only Cage + Firefox feels really excessive to me.
I tried removing default bloat using the following disable.nix
module (I just went gung-ho with finding defaults that didn’t seem necessary and disabling them):
{ lib, ... }:
{
appstream.enable = false;
boot = {
initrd = {
services.lvm.enable = false;
systemd.enableTpm2 = false;
};
swraid.enable = lib.mkForce false;
};
environment.defaultPackages = [ ];
environment.systemPackages = [ ];
fonts.fontconfig.enable = false;
hardware = {
bluetooth.enable = false;
pulseaudio.enable = false;
};
programs.nano.enable = false;
networking = {
# dhcpcd.enable = false;
firewall.enable = false;
};
nixpkgs.overlays = [
(final: super: {
zfs = super.zfs.overrideAttrs (_: {
meta.platforms = [ ];
});
})
];
security = {
pam.services.su.forwardXAuth = lib.mkForce false;
# polkit.enable = lib.mkForce false;
sudo.enable = lib.mkForce false;
tpm2.applyUdevRules = false;
};
services = {
logrotate.enable = lib.mkForce false;
lvm.enable = false;
# nscd.enable = false;
openssh.enable = lib.mkForce false;
pipewire.enable = false;
rsyslogd.enable = false;
syslog-ng.enable = false;
# udev.enable = false;
udisks2.enable = false;
# upower.enable = false;
xserver.enable = false;
zfs.trim.enable = lib.mkForce false;
};
sound.enable = false;
system = {
extraDependencies = lib.mkForce [ ];
nssModules = lib.mkForce [ ];
switch.enable = false;
};
systemd = {
coredump.enable = false;
oomd.enable = false;
services = {
systemd-journal-flush.enable = false;
};
};
xdg = {
autostart.enable = false;
icons.enable = false;
menus.enable = false;
mime.enable = false;
portal.enable = false;
sounds.enable = false;
};
}
This reduces the image down to ~1.6GB, but I’m convinced it could be trimmed down further. In the world of debian-based OSes, this would probably cost 200-400mb…
Any ideas how to cut some more of the cruft?
Also, aficionado would probably get a kick out of how I’m handling “environment variables”. I tried to set up a private env.nix
file where individual users can set their unique configuration variables without tampering with the rest of the git tree or the ability to pull updates. Since .gitignore
effectively removes files from the flake build, I can’t include that file in the flake build, so I hackishly copy the local env.nix
to an otherwise empty (but tracked) file in build.sh
right before building:
#!/usr/bin/env bash
# copy the git ignored variables to the build environment
mv build.env.nix build.env.nix.tmp
mv env.nix build.env.nix
# build the project
nix build --impure --extra-experimental-features 'nix-command flakes'
# restore the git ignored variables
mv build.env.nix env.nix
mv build.env.nix.tmp build.env.nix
If anyone could direct me to a less-hackish approach, that would be great.
toodle-oo!