{ pkgs, lib, stdenv }: /* Create a systemd portable service image https://systemd.io/PORTABLE_SERVICES/ Example: … pkgs.portableService { name = "hello"; version = "2.4.3"; description = "hello portable"; units = [ hello-service ./files/hello.socket ]; symlinks = [ { object = "${pkgs.cacert}/etc/ssl"; symlink = "/etc/ssl"; } { object = "${pkgs.bash}/bin/bash"; symlink = "/bin/sh"; } ]; } … */ { name , version ? "dev" , description ? null , homepage ? null , units ? [ ] , symlinks ? [ ] , contents ? [ ] , squashfsTools ? pkgs.squashfsTools , squash-compression ? "xz -Xdict-size 100%" , squash-block-size ? "1M" }: let image-name = "${name}_${version}"; os-release-params = lib.filterAttrs (n: v: v != null) { PORTABLE_ID = name; PORTABLE_PRETTY_NAME = description; HOME_URL = homepage; ID = "nixos"; PRETTY_NAME = "NixOS"; BUILD_ID = "rolling"; }; os-release = pkgs.writeText "os-release" (lib.generators.toKeyValue {} os-release-params); getUnitName = u: if lib.isDerivation u then u.name else if builtins.isPath u then baseNameOf u else throw "unit must be either derivation or path"; rootfs = stdenv.mkDerivation { name = "rootfs"; serviceName = name; builder = ./build-rootfs.sh; inherit units; unitNames = map getUnitName units; osRelease = os-release; objects = map (x: x.object) symlinks; targets = map (x: x.symlink) symlinks; }; in stdenv.mkDerivation { name = "${image-name}.raw"; nativeBuildInputs = [ squashfsTools ]; buildCommand = '' closureInfo=${pkgs.closureInfo { rootPaths = contents ++ [rootfs]; }} mkdir -p nix/store for i in $(< $closureInfo/store-paths); do cp -a "$i" "''${i:1}" done mksquashfs nix ${rootfs}/* $out \ -quiet -noappend \ -keep-as-directory \ -all-root -root-mode 755 \ -b ${squash-block-size} -comp ${squash-compression} ''; }