{ description = "My flake"; inputs = { nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, nixpkgs, flake-utils, }: flake-utils.lib.eachDefaultSystem ( system: let pkgs = import nixpkgs { inherit system; }; in { bundlers = rec { proot-bundler = drv: pkgs.stdenvNoCC.mkDerivation { name = "myApp.run"; nativeBuildInputs = [ pkgs.nix pkgs.coreutils pkgs.findutils pkgs.gnutar pkgs.gzip pkgs.patchelf ]; # patch proot as you described prootPatched = pkgs.proot.overrideAttrs (_: { postFixup = '' exe=$out/bin/proot ${pkgs.patchelf}/bin/patchelf \ --set-interpreter ."$(${pkgs.patchelf}/bin/patchelf --print-interpreter $exe)" \ --set-rpath "$(${pkgs.patchelf}/bin/patchelf --print-rpath $exe | sed 's|/nix/store/|./nix/store/|g')" \ $exe ''; }); buildCommand = '' set -euo pipefail # 1) Make payload dir that mirrors the bundle runtime layout PAY="$PWD/payload" mkdir -p "$PAY/nix/store" # include app + patched proot in the payload closure app=${drv} proot=${ self.bundlers.${system}.selfExtracting.prootPatched or "" } # not callable; we want the attr above proot=${ pkgs.proot.overrideAttrs (_: { postFixup = "${pkgs.patchelf}/bin/patchelf --set-interpreter .\"$(${pkgs.patchelf}/bin/patchelf --print-interpreter $out/bin/proot)\" --set-rpath \"$( ${pkgs.patchelf}/bin/patchelf --print-rpath $out/bin/proot | sed 's|/nix/store/|./nix/store/|g')\" $out/bin/proot"; }) } # copy closure of app + proot into ./nix/store paths=$( ${pkgs.nix}/bin/nix-store --query --requisites "$app" "$proot" ) for p in $paths; do cp -a --no-preserve=ownership "$p" "$PAY/nix/store/" done # discover targets for the launcher APP_BIN=$(find "$app/bin" -maxdepth 1 -type f -perm -111 | head -n1) APP_REL="/nix/store/$(basename "$(dirname "$APP_BIN")")/$(basename "$APP_BIN")" PROOT_REL="/nix/store/$(basename "$proot")/bin/proot" # 2) Tar.gz the payload ( cd "$PAY" && tar -czf "$PWD/payload.tar.gz" . ) # 3) Assemble a self-extracting script at $out cat > $out <<'SH' #!/bin/sh set -euf # extract to temp dir : "''${TMPDIR:=/tmp}" EXTRACT_DIR="$(mktemp -d "''${TMPDIR%/}/nxbdl.XXXXXX")" cleanup() { [ -n "''${KEEP_BUNDLE:-}" ] || rm -rf "$EXTRACT_DIR"; } trap cleanup EXIT INT TERM # locate embedded archive (line after marker) ARCHIVE_LINE=$(awk '/^__ARCHIVE_BELOW__/ {print NR+1; exit 0}' "$0") tail -n +$ARCHIVE_LINE "$0" | tar -xzf - -C "$EXTRACT_DIR" cd "$EXTRACT_DIR" # vars substituted by bundler at build time: APP_REL='__APP_REL__' PROOT_REL='__PROOT_REL__' # run via proot BUNDLE_PWD="''${BUNDLE_PWD:-$PWD}" exec ".$PROOT_REL" \ -b ./nix:nix \ -R / \ -w "$BUNDLE_PWD" \ ".$APP_REL" "$@" __ARCHIVE_BELOW__ SH # substitute the program + proot paths into the stub sed -i \ -e "s|__APP_REL__|$APP_REL|g" \ -e "s|__PROOT_REL__|$PROOT_REL|g" \ $out chmod +x $out # 4) Append the payload bytes after the marker cat payload.tar.gz >> $out ''; }; default = proot-bundler; }; } ); }