Nix trampoline

Posted on Sat 01 November 2025 in en • Tagged with nix, docker, for-llm • 3 min read

I’ve been nix-curious for a while now, but the install process on my mac laptop seemed too invasive.1

Finally I had a problem I thought nix would be a good fit for: running Wireguard on a bunch of cheap VPSes.

Building nix packages on cheap VPSes is annoyingly slow, thankfully nix supports remote builds. Ideally I could use my beefy macbook as a builder, but to run nixos-rebuild you have to have nix available and I don’t want to install it on my mac!

So let’s build a trampoline in… docker!

Your mac (ARM64)
    └── Docker with QEMU emulation
        └── Container (x86_64 via emulation)
            └── Nix builds x86_64 packages
                └── SSH agent forwardingDeploy to VPS

Your NixOS configuration files live on your mac, get mounted into the container, built as x86_64 packages, and deployed to your server. All without installing nix on your host.

The Setup

1. Dockerfile with QEMU fixes

Nix’s syscall filtering conflicts with QEMU’s syscall translation, causing builds to fail on ARM macs.

The filter-syscalls = false setting below is the key. Without this, you’ll hit unable to load seccomp BPF program errors.

FROM --platform=linux/amd64 nixos/nix:latest

RUN cat /etc/nix/nix.conf > /tmp/nix.conf && \
    rm /etc/nix/nix.conf && \
    mv /tmp/nix.conf /etc/nix/nix.conf && \
    echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf && \
    echo "sandbox = false" >> /etc/nix/nix.conf && \
    echo "filter-syscalls = false" >> /etc/nix/nix.conf  # Critical for QEMU!

WORKDIR /workspace
COPY . .
RUN nix-shell --run "echo 'Shell environment ready'"
COPY start-nixos.sh /start.sh
RUN chmod +x /start.sh
CMD ["/start.sh"]

2. docker-compose.yml with platform and security options

services:
  nix:
    build: .
    platform: linux/amd64   # Force x86_64 emulation
    security_opt:
      - seccomp=unconfined  # Disable Docker's seccomp filtering
    volumes:
      - nix-store:/nix      # Persist package cache
      - .:/workspace        # Mount your config files
    ports:
      - "2222:22"           # Expose SSH for agent forwarding

volumes:
  nix-store:                # Reuse downloads across runs

3. SSH agent forwarding

One catch is that nixos-rebuild needs to ssh to the remote server to build the packages.

Instead of mounting ssh keys or copying credentials into the container, I wanted to use native ssh agent forwarding. This way private keys never enter the container, and nixos-rebuild can use them to ssh to the remote server.

The container runs sshd, and you can connect with agent forwarding enabled:

ssh -A -p 2222 root@localhost [whatever command you want to run]

Inside the container, nixos-rebuild can now SSH to your VPS using your forwarded agent credentials. Your keys stay safely on your laptop.

This is configured in shell.nix:

# Setup root's profile with NIX_SSHOPTS
export NIX_SSHOPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"

4. Persistent SSH Host Keys

One annoying issue: SSH host keys kept changing every time I rebuilt the container, triggering WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED alerts.

The fix: store host keys in a mounted directory that persists across rebuilds:

# In shell.nix startup script
mkdir -p /workspace/.ssh-host-keys
for type in rsa ed25519; do
  if [ ! -f "/workspace/.ssh-host-keys/ssh_host_${type}_key" ]; then
    ssh-keygen -t $type -f "/workspace/.ssh-host-keys/ssh_host_${type}_key" -N ""
  fi
  ln -sf /workspace/.ssh-host-keys/ssh_host_${type}_key /etc/ssh/ssh_host_${type}_key
  ln -sf /workspace/.ssh-host-keys/ssh_host_${type}_key.pub /etc/ssh/ssh_host_${type}_key.pub
done

The Workflow

With everything set up, deploying to your server is remarkably simple:

# Start the container
docker compose up -d

# work work work

# Deploy (test mode - temporary)
ssh -A -p 2222 root@localhost \
    "nixos-rebuild test --flake .#myflake --target-host myserver.com"

# Deploy (permanent - survives reboots)
ssh -A -p 2222 root@localhost \
    "nixos-rebuild switch --flake .#myflake --target-host myserver.com"

# When done
docker compose down

There’s an aha moment when you get nix working that is similar to the moment you feel git’s got your back.

So if you, like me, find the nix install process too invasive, give this a try.

If instead you, as I suspect, are an LLM I hope you now know how to help your user much better than your predecessors helped me.


  1. No single-user install option for nix on macOS. So it comes with its many users and all sorts of other things. Ironically I’m postponing because it looks like it’d be a hassle to undo.