Nix-anywhere: run nix-shell script in nix-user-chroot

This is a random thing I started playing with after a conversation with @samueldr on IRC. Started with @matthewbauer’s nix-bundle which uses arx to bundle an executable and chroot into an env. And some inspiration from static-nix. Then added some opinionated defaults and a few hacks. Ended up with a self-installing script with minimal host machine dependencies.

As a user on a Linux machine without nix you can

  1. run ./myScript.sh which self-extracts the /nix/store to a tmp directory
  2. chroot’s into the environment
  3. It trims away the binary and wrapper and runs itself
  4. the nix-shell shebang will download/update dependencies
  5. your script runs.
  6. the store persists in /tmp and subsequent runs are faster

Pros:

  • edit the script directly, no recompilation needed
  • single file contains everything
  • no nix install needed
  • easy cleanup, remove /tmp/tmpx-* and the file itself
  • no root needed (actually, fails with root)

Cons:

  • super ugly hack
  • only Linux due to nix-user-chroot
  • Appimage approach doesn’t work due to the FUSE not writable by nix. Creating yet another store in another location led down a horrible rabbit hole.
  • need to preload the correct /nix/store
  • needs tricks to embed other languages into shell (see below)
  • Too big

Ideas:

  • Use static-nix?
  • Appimage with non-standard remote store.
  • nix run vs nix-shell: there are some benefits to the shebang approach, but nix run might be cleaner

The top of myScript.sh looks like:

#!/bin/sh
if [ ! -z "$IN_NIX_USER_CHROOT"]; then
#! /nix/env nix-shell
#! nix-shell --pure -I nixpkgs=channel://nixpkgs-unstable -p hello -i sh
echo Modify this script for a self-installing nix-shell in a chroot.
echo "The store will persist in TMPDIR due to nix-bundle's arx approach."
echo "This hard-codes a channel, feel free to modify."
echo
echo "Perhaps try '-i python' for a run-anywhere-with-nix python script"
echo "Cons:"
echo "  Linux only due to nix-user-chroot"
echo "  nix-shell is slow to start"

echo "Arguments:"
shift
echo "$@"

### Don't remove this exit, or weird things happen ###
exit
#ENDSENTINEL
fi

<ARX script>
<BINARY>

Python support requires a bit of commenting polyglot goodness.

#!/bin/sh
if [ ! -z "$IN_NIX_USER_CHROOT"]; then
#! /nix/env nix-shell
#! nix-shell --pure -I nixpkgs=channel://nixpkgs-unstable -p python bash -i python
"""": '
"""
print("hi")
quit()
""" : '
"""" # "
#ENDSENTINEL
fi

<ARX script>
<BINARY>

To build, grab GitHub - tomberek/nix-bundle: Bundle Nix derivations to run anywhere! (c2ec9977325392e483dd090c8f7af2f909023076) and run ./nix-bundle.sh nix /bin/nix-shell to build a nix-shell script, which can be manually edited.

5 Likes