This is the multi-machine configuration for my Darwin (macOS) and Linux systems, managed via Nix Flakes, nix-darwin, and home-manager.
- Git: Required to clone this repository.
- Homebrew (Darwin only): Used for managing some macOS applications.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"- Nix: Install Nix with Flakes support.
curl -fsSL https://install.determinate.systems/nix | sh -s -- install- Clone this repository into
~/.config/nix:
git clone https://github.com/thekorn/nix-config.git ~/.config/nix
cd ~/.config/nix- Apply the configuration for your host:
For a first-time installation:
nix run nix-darwin -- switch --flake .#<hostname>For subsequent updates:
darwin-rebuild switch --flake .#<hostname>sudo nixos-rebuild switch --flake .#<hostname>To deploy a Linux host remotely from another machine on the network:
nix run .#deploy-thekorn-server
nix run .#deploy-thekorn-server-2
nix run .#deploy-thekorn-vm
nix run .#deploy-thekorn-vm-desktopThis connects to the matching thekorn@<hostname>, builds on the target, and activates the matching NixOS configuration there.
thekorn-macbook(Personal MacBook)thekorn-studio(Personal Mac Studio)BFG-043556(Work Laptop, user:d438477)
thekorn-server(Main server)thekorn-server-2(Second server)thekorn-vm(VM)thekorn-vm-desktop(Desktop VM)
The server manages two NixOS QEMU/KVM guests with microvm.nix:
| VM / host systemd unit | Runner | SSH port on host loopback |
|---|---|---|
microvm@github-runner |
GitHub Actions: thekorn-server-bunte-app |
2221 |
microvm@amp-runner |
Amp: thekorn-amp-runner |
2222 |
Each guest has 4 vCPUs and a persistent 64 GiB root disk at
/var/lib/microvms/<vm>/root.img. The GitHub guest has 8 GiB RAM; the Amp guest
has 16 GiB. Allow 24 GiB RAM for the guests plus host
headroom, and disk space for both root disks and generated Nix store images.
Resource defaults live in hosts/linux/shared/runner-vms/base.nix, with Amp's
memory allocation in hosts/linux/shared/runner-vms/amp.nix; changing the
configured disk size does not resize an existing disk.
Import hosts/linux/shared/runner-vms.nix to enable either reusable runner VM:
custom.runnerVMs.amp = {
enable = true;
hostName = "my-amp-runner"; # Also used as the Amp runner ID.
sshPort = 2222; # Optional; GitHub defaults to 2221.
};custom.runnerVMs.github has the same options. Configure GitHub registrations
through microvm.vms.github-runner.config.services.github-runners, as shown in
hosts/linux/configurations/thekorn-server/runner-vms.nix. Guest overrides such
as microvm.vms.amp-runner.config.microvm.mem = lib.mkForce 4096 use the standard microVM
options. Nested KVM settings remain host-specific.
The guests use outbound QEMU user-mode NAT, key-only SSH and the same authorized
key as the host. There are no host filesystem shares. Each guest has its own
writable Nix store; Amp also has the server home-manager profile and Docker.
The guest user thekorn has passwordless sudo inside the guests only.
NAT is not an outbound security policy: jobs can still reach the host and LAN.
-
Drain GitHub jobs and finish Amp threads on the old host runners. Back up any working data you need. Deploy this branch with
nix run .#deploy-thekorn-server. This removes both host runner services and starts the VMs, but does not copy host credentials, repositories, or runner state into them. -
On the host, verify
cat /sys/module/kvm_amd/parameters/nestedreports1orY. The configuration enables nested AMD KVM; if the already-loaded module still has it disabled, reboot the server during a maintenance window. Do not unload KVM while VMs are running. Firmware virtualization must be enabled too. -
Connect through the server from your workstation (no SSH agent forwarding is needed):
ssh -J thekorn@thekorn-server.home -p 2221 thekorn@127.0.0.1 ssh -J thekorn@thekorn-server.home -p 2222 thekorn@127.0.0.1
-
Once the old runner is stopped, remove its offline registration from the repository's GitHub Actions runner settings: the guest keeps the same name and does not automatically replace existing registrations. Create a fresh registration token for
thekorn/bunte-app(tokens expire after one hour). Keep it in a protected local file, outside this repository. From the workstation, install it in the GitHub guest:ssh -J thekorn@thekorn-server.home -p 2221 thekorn@127.0.0.1 \ 'sudo install -d -m 0700 /var/lib/github-runner-secrets && sudo sh -c "umask 077; cat > /var/lib/github-runner-secrets/bunte-app.token"' \ < /path/to/fresh-registration-token ssh -J thekorn@thekorn-server.home -p 2221 thekorn@127.0.0.1 \ 'sudo systemctl start github-runner-bunte-app'
The service is skipped until that file exists. Its existing name, repository URL and labels (
nixos,thekorn-server) are preserved. Registration state persists in the guest, but configuration/token changes may require a fresh registration token and removal of the previous offline registration. Job working directories retain the NixOS module's cleanup-on-start behavior. -
In the Amp guest, run
sudo systemctl stop amp-runner, then runampasthekornand complete sign-in. Exit the interactive CLI, clone or securely copy the required repositories under/home/thekorn/devel, then runsudo systemctl start amp-runner. Provision Git and other tool credentials separately; do not copy the entire host home or share its Docker socket. Select the newthekorn-amp-runnerin Amp.
Host rebuilds update both guest systems and restart guests whose configuration changed. Drain jobs first: this is not a rolling or job-aware restart. Root disks survive rebuilds, restarts and host reboots; back them up with the VM stopped. The previous host runner data is left untouched. A NixOS generation rollback does not roll back guest disks or GitHub registrations.
On the host, inspect systemctl status microvm@github-runner microvm@amp-runner
and journalctl -u microvm@github-runner -u microvm@amp-runner. In each guest,
inspect systemctl status github-runner-bunte-app or systemctl status amp-runner.
Verify nix store ping --store daemon works in both guests. In the GitHub guest,
check /dev/kvm exists, then run a real Android emulator workflow to verify nested
acceleration. ADB and emulator ports are guest-local and no longer conflict with
host sessions. Confirm both runners reconnect and Amp repositories survive after
a controlled guest restart. Neither VM boot nor nested KVM can be tested in an
Amp orb without /dev/kvm.
Amp is configured in a VM through hosts/linux/shared/runner-vms.nix, with
guest hostname and runner ID thekorn-amp-runner-2. Intel VT-x is now enabled
and /dev/kvm is available. Only the Amp VM is enabled on this server:
custom.runnerVMs.amp = {
enable = true;
hostName = "thekorn-amp-runner-2";
};The guest defaults to host loopback SSH port 2222, 4 vCPUs, 16 GiB RAM, and a
64 GiB root disk at /var/lib/microvms/amp-runner/root.img. To migrate:
-
Finish active Amp threads and back up needed working data. Deploy with
nix run .#deploy-thekorn-server-2. Activation removes the hostamp-runnerservice and starts the guest; it does not copy credentials or repositories. -
Connect with a distinct SSH host-key alias for this guest:
ssh -J thekorn@thekorn-server-2.home -p 2222 \ -o HostKeyAlias=thekorn-amp-runner-2 thekorn@127.0.0.1
-
Follow the Amp provisioning steps above: stop the guest service, sign in as
thekorn, provision repositories under/home/thekorn/develand tool credentials, then start the service. Selectthekorn-amp-runner-2in Amp. -
Verify the host
amp-runner.serviceis absent,microvm@amp-runneris active, the guest runner reconnects, and repositories survive a guest restart.
Host files and USB/serial devices are not passed into the guest. Existing host repositories remain untouched; migrate required working copies explicitly.
Home Manager provides ~/.config/devenvs/flutter on every user profile. From
any Flutter app directory, run:
dv-flutter # Enter an interactive Zsh with Flutter, Dart and FVM
dv-flutter flutter doctor # Or run one command without staying in the shell
exit # Leave the interactive toolboxBoth Oh My Posh themes show devenv:flutter while the toolbox is active.
There is no auto-activation and no need for app-local devenv configuration.
The shortcut keeps .devenv/ and devenv.lock under
~/.local/state/devenvs/flutter (Home Manager's xdg.stateHome) and returns to
the app directory inside the activated environment. No devenv files are created
in the app directory. DEVENV_ROOT points to the global state directory, so
this toolbox is intended for packages, not project-relative tasks or services.
Flutter includes its matching Dart SDK; use fvm flutter / fvm dart for an
app's FVM-selected SDK. Existing SDKs in ~/.local/state/fvm are preserved.
Android Studio, Xcode and their platform SDKs remain separately configured.
Edit home/shared/devenvs/definitions/flutter.nix to change the toolbox.
Its nixpkgs revision follows this repository's flake.lock. Rebuild the host
after configuration changes. To omit the environment and shortcut on a host,
set custom.flutterDevenv.enable = false in its Home Manager configuration.
Flutter, FVM and CocoaPods are also installed in the normal macOS user profile, while the
dv-flutter toolbox remains available for an isolated development shell.
Run dv-ctf from any project directory to enter the CTF environment, or use
dv-ctf <command> to run a single command. It includes fnm, mongosh, the Docker
CLI, Node.js, awscli2, mkcert, mongodb-tools, jq, @microsoft/rush@5.166.0,
pnpm@10.27.0, and pm2. Docker requires an existing daemon (for example,
Docker Desktop on macOS).
Like the Flutter toolbox, it keeps devenv state outside the project, under
~/.local/state/devenvs/ctf. The Node tools are installed there on first entry
(requiring npm registry access) and reused on subsequent entries. pm2 is
unpinned and resolves to the current version at installation time. fnm is
initialized for manual Node version selection with fnm use.
Edit home/shared/devenvs/definitions/ctf.nix to change the toolbox and rebuild
the host to activate it. Set custom.ctfDevenv.enable = false in a host's Home
Manager configuration to omit it.
- Format code:
nix fmt . - Update inputs:
nix flake update
- Idea: move git commit hooks handling to lefthook https://lefthook.dev