Compare commits
3 commits
396e8121d0
...
cf98cb7880
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf98cb7880 | ||
|
|
736557bb8a | ||
|
|
f035f1300f |
6 changed files with 115 additions and 109 deletions
18
README.md
18
README.md
|
|
@ -9,28 +9,38 @@ Machines are configured to act as build servers / binary caches for each other i
|
||||||
### Onboarding a device as a build client
|
### Onboarding a device as a build client
|
||||||
|
|
||||||
1. Generate a key pair on the device:
|
1. Generate a key pair on the device:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ssh-keygen -t ed25519 -f /etc/nix/distributed-build-key -N "" -C "$(hostname)-nix-builds" && sudo cat /etc/nix/distributed-build-key.pub
|
||||||
```
|
```
|
||||||
sudo ssh-keygen -t ed25519 -f /etc/nix/distributed-build-key -N "" -C "$(hostname)-nix-builds"
|
|
||||||
```
|
|
||||||
2. Add the public key to the device entry in `devices.nix`:
|
2. Add the public key to the device entry in `devices.nix`:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
distributedBuilds.clientPublicKey = "ssh-ed25519 AAAA... <hostname>-nix-builds";
|
distributedBuilds.clientPublicKey = "ssh-ed25519 AAAA... <hostname>-nix-builds";
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Rebuild all build machines so they pick up the new authorized key.
|
3. Rebuild all build machines so they pick up the new authorized key.
|
||||||
|
|
||||||
### Adding a build server
|
### Adding a build server
|
||||||
|
|
||||||
1. Add to its entry in `devices.nix`:
|
1. Add to its entry in `devices.nix`:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
distributedBuilds.isBuilder = true;
|
distributedBuilds.isBuilder = true;
|
||||||
distributedBuilds.hostPublicKey = "ssh-ed25519 AAAA..."; # from: ssh-keyscan -t ed25519 <hostname>
|
distributedBuilds.hostPublicKey = "ssh-ed25519 AAAA..."; # from: ssh-keyscan -t ed25519 "$(hostname)"
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Generate a store signing key on the builder:
|
2. Generate a store signing key on the builder:
|
||||||
```
|
|
||||||
|
```sh
|
||||||
sudo nix key generate-secret --key-name "$(hostname)" | sudo tee /etc/nix/signing-key.sec | sudo nix key convert-secret-to-public
|
sudo nix key generate-secret --key-name "$(hostname)" | sudo tee /etc/nix/signing-key.sec | sudo nix key convert-secret-to-public
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Add the printed public key to `devices.nix`:
|
3. Add the printed public key to `devices.nix`:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
distributedBuilds.storeSigningPublicKey = "<hostname>:<base64...>";
|
distributedBuilds.storeSigningPublicKey = "<hostname>:<base64...>";
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Rebuild all machines so they trust the new signing key.
|
4. Rebuild all machines so they trust the new signing key.
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,12 @@ in
|
||||||
};
|
};
|
||||||
forgejo-runner-1 = {
|
forgejo-runner-1 = {
|
||||||
system = "aarch64-linux";
|
system = "aarch64-linux";
|
||||||
|
distributedBuilds = {
|
||||||
|
isBuilder = true;
|
||||||
|
clientPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0NLgg0sFobBWz/bjYs9WkrMvlcvJC5F6+3jQ/b+AnD forgejo-runner-1-nix-builds";
|
||||||
|
hostPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIANGC89GiT5xCsFICwrharrbV3q7acWHqk6ZwOUXbtGT";
|
||||||
|
storeSigningPublicKey = "forgejo-runner-1:ln1FVLL8G5+IveQuBi/Kn3SaqFZ1gaiQrE3yPlMhCMA=";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
hetzner-vpn2 = {
|
hetzner-vpn2 = {
|
||||||
system = "aarch64-linux";
|
system = "aarch64-linux";
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,11 @@
|
||||||
niri = niri.overlays.niri;
|
niri = niri.overlays.niri;
|
||||||
};
|
};
|
||||||
|
|
||||||
nixosModules = importModuleDir ./nixosModules;
|
nixosModules = (importModuleDir ./nixosModules) // {
|
||||||
|
default = {
|
||||||
|
imports = builtins.attrValues (builtins.removeAttrs self.nixosModules [ "default" ]);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
homeModules = importModuleDir ./homeModules;
|
homeModules = importModuleDir ./homeModules;
|
||||||
homeConfigurations = {
|
homeConfigurations = {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
lib,
|
lib,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
devices = import ./devices.nix { inherit (inputs) self; };
|
allDevices = import ./devices.nix { inherit (inputs) self; };
|
||||||
inherit (inputs)
|
inherit (inputs)
|
||||||
self
|
self
|
||||||
home-manager
|
home-manager
|
||||||
|
|
@ -15,7 +15,7 @@ let
|
||||||
stylix
|
stylix
|
||||||
zerforschen-plus
|
zerforschen-plus
|
||||||
;
|
;
|
||||||
forDevice = f: lib.mapAttrs (device: value: f (value // { inherit device; })) devices;
|
forDevice = f: lib.mapAttrs (device: value: f (value // { inherit device; })) allDevices;
|
||||||
in
|
in
|
||||||
forDevice (
|
forDevice (
|
||||||
{
|
{
|
||||||
|
|
@ -24,10 +24,15 @@ forDevice (
|
||||||
home-manager-users ? { },
|
home-manager-users ? { },
|
||||||
nixosSystem ? inputs.nixpkgs.lib.nixosSystem,
|
nixosSystem ? inputs.nixpkgs.lib.nixosSystem,
|
||||||
...
|
...
|
||||||
}:
|
}@thisDevice:
|
||||||
let
|
let
|
||||||
specialArgs = inputs // {
|
specialArgs = inputs // {
|
||||||
inherit device home-manager-users devices;
|
inherit
|
||||||
|
device
|
||||||
|
home-manager-users
|
||||||
|
allDevices
|
||||||
|
thisDevice
|
||||||
|
;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
nixosSystem {
|
nixosSystem {
|
||||||
|
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
# keep-sorted start
|
|
||||||
./allowed-unfree-list.nix
|
|
||||||
./amd-graphics.nix
|
|
||||||
./autoupdate.nix
|
|
||||||
./distributed-builds.nix
|
|
||||||
./en-de.nix
|
|
||||||
./extra-caches.nix
|
|
||||||
./firmware-updates.nix
|
|
||||||
./git.nix
|
|
||||||
./globalinstalls.nix
|
|
||||||
./gnome.nix
|
|
||||||
./intel-graphics.nix
|
|
||||||
./kdeconnect.nix
|
|
||||||
./latex.nix
|
|
||||||
./lix-is-nix.nix
|
|
||||||
./modern-desktop.nix
|
|
||||||
./muede-desktop-settings.nix
|
|
||||||
./nix-ld.nix
|
|
||||||
./nixpkgs-overlays.nix
|
|
||||||
./openssh.nix
|
|
||||||
./podman.nix
|
|
||||||
./printing.nix
|
|
||||||
./prometheus-node.nix
|
|
||||||
./pxvirt-guest.nix
|
|
||||||
./quiet-boot.nix
|
|
||||||
./secure-boot.nix
|
|
||||||
./steam.nix
|
|
||||||
./stylix.nix
|
|
||||||
./systemd-boot.nix
|
|
||||||
./tailscale.nix
|
|
||||||
./user-muede.nix
|
|
||||||
./user-ronja.nix
|
|
||||||
./wine-gaming.nix
|
|
||||||
# keep-sorted end
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +1,27 @@
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
devices,
|
allDevices,
|
||||||
|
thisDevice,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
sshKeyPath = "/etc/nix/distributed-build-key";
|
clientSshKeyPath = "/etc/nix/distributed-build-key";
|
||||||
buildUser = "remotebuild";
|
buildUser = "remotebuild";
|
||||||
|
|
||||||
# Collect all per-device public keys that have been registered.
|
# Collect all per-device public keys that have been registered.
|
||||||
authorizedPublicKeys = lib.pipe devices [
|
allClientPublicKeys = lib.pipe allDevices [
|
||||||
(lib.filterAttrs (_: v: (v.distributedBuilds or { }) ? clientPublicKey))
|
(lib.filterAttrs (_: v: (v.distributedBuilds or { }) ? clientPublicKey))
|
||||||
(lib.mapAttrsToList (_: v: v.distributedBuilds.clientPublicKey))
|
(lib.mapAttrsToList (_: v: v.distributedBuilds.clientPublicKey))
|
||||||
];
|
];
|
||||||
|
|
||||||
|
isClient = (thisDevice.distributedBuilds or { }) ? clientPublicKey;
|
||||||
|
|
||||||
buildServerDevices = lib.filterAttrs (
|
buildServerDevices = lib.filterAttrs (
|
||||||
_: v: (v.distributedBuilds or { }).isBuilder or false
|
_: v: (v.distributedBuilds or { }).isBuilder or false
|
||||||
) devices;
|
) allDevices;
|
||||||
|
|
||||||
knownHosts = lib.pipe buildServerDevices [
|
buildServerKnownHosts = lib.pipe buildServerDevices [
|
||||||
(lib.filterAttrs (_: v: v.distributedBuilds ? hostPublicKey))
|
(lib.filterAttrs (_: v: v.distributedBuilds ? hostPublicKey))
|
||||||
(lib.mapAttrs (
|
(lib.mapAttrs (
|
||||||
_: v: {
|
_: v: {
|
||||||
|
|
@ -27,17 +30,21 @@ let
|
||||||
))
|
))
|
||||||
];
|
];
|
||||||
|
|
||||||
buildMachineList = lib.mapAttrsToList (
|
remoteBuildServerDevices = builtins.filter (
|
||||||
hostName: v:
|
m: m.hostName != config.networking.hostName
|
||||||
|
) (lib.mapAttrsToList (name: v: v // { hostName = name; }) buildServerDevices);
|
||||||
|
|
||||||
|
buildMachines = map (
|
||||||
|
m:
|
||||||
{
|
{
|
||||||
inherit hostName;
|
hostName = m.hostName;
|
||||||
systems = [ v.system ];
|
systems = [ m.system ];
|
||||||
sshUser = buildUser;
|
sshUser = buildUser;
|
||||||
sshKey = sshKeyPath;
|
sshKey = clientSshKeyPath;
|
||||||
protocol = "ssh-ng";
|
protocol = "ssh-ng";
|
||||||
}
|
}
|
||||||
// lib.optionalAttrs (v.distributedBuilds ? speedFactor) {
|
// lib.optionalAttrs (m.distributedBuilds ? speedFactor) {
|
||||||
speedFactor = v.distributedBuilds.speedFactor;
|
speedFactor = m.distributedBuilds.speedFactor;
|
||||||
}
|
}
|
||||||
// {
|
// {
|
||||||
supportedFeatures = [
|
supportedFeatures = [
|
||||||
|
|
@ -47,60 +54,73 @@ let
|
||||||
"benchmark"
|
"benchmark"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
) buildServerDevices;
|
) remoteBuildServerDevices;
|
||||||
|
|
||||||
remoteMachines = builtins.filter (m: m.hostName != config.networking.hostName) buildMachineList;
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.distributedBuilds.enable = lib.mkEnableOption "distributed Nix builds";
|
options.my.distributedBuilds.enable = lib.mkEnableOption "distributed Nix builds";
|
||||||
|
|
||||||
config = lib.mkIf config.my.distributedBuilds.enable {
|
config = lib.mkIf config.my.distributedBuilds.enable (
|
||||||
programs.ssh.knownHosts = knownHosts;
|
lib.mkMerge [
|
||||||
|
|
||||||
# Dedicated user for receiving distributed build connections
|
# All machines
|
||||||
users.users.${buildUser} = {
|
{
|
||||||
isSystemUser = true;
|
nix.settings = {
|
||||||
group = buildUser;
|
trusted-public-keys = lib.pipe buildServerDevices [
|
||||||
useDefaultShell = true;
|
(lib.mapAttrsToList (_: v: v.distributedBuilds.storeSigningPublicKey or null))
|
||||||
openssh.authorizedKeys.keys = map (
|
(builtins.filter (k: k != null))
|
||||||
k: ''command="nix daemon --stdio",restrict ${k}''
|
];
|
||||||
) authorizedPublicKeys;
|
max-jobs = (thisDevice.distributedBuilds or { }).maxJobs or "auto";
|
||||||
};
|
cores = 0;
|
||||||
users.groups.${buildUser} = { };
|
min-free = 10 * 1024 * 1024;
|
||||||
|
max-free = 200 * 1024 * 1024;
|
||||||
|
};
|
||||||
|
systemd.services.nix-daemon.serviceConfig = {
|
||||||
|
MemoryAccounting = true;
|
||||||
|
MemoryMax = "90%";
|
||||||
|
OOMScoreAdjust = 500;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
nix = {
|
# Server: accept incoming build connections
|
||||||
distributedBuilds = remoteMachines != [ ];
|
(lib.mkIf (thisDevice.distributedBuilds.isBuilder or false) {
|
||||||
buildMachines = remoteMachines;
|
users.users.${buildUser} = {
|
||||||
settings = {
|
isSystemUser = true;
|
||||||
trusted-users = [ buildUser ];
|
group = buildUser;
|
||||||
builders-use-substitutes = true;
|
useDefaultShell = true;
|
||||||
# Use build machines as binary caches so already-built paths are downloaded
|
openssh.authorizedKeys.keys = map (
|
||||||
# rather than rebuilt. Only machines with a storeSigningPublicKey are used.
|
k: ''command="nix daemon --stdio",restrict ${k}''
|
||||||
substituters = lib.pipe buildServerDevices [
|
) allClientPublicKeys;
|
||||||
(lib.filterAttrs (_: v: v.distributedBuilds ? storeSigningPublicKey))
|
};
|
||||||
(lib.mapAttrsToList (hostName: _: "ssh-ng://${buildUser}@${hostName}"))
|
users.groups.${buildUser} = { };
|
||||||
(lib.filter (s: s != "ssh-ng://${buildUser}@${config.networking.hostName}"))
|
nix.settings = {
|
||||||
];
|
trusted-users = [ buildUser ];
|
||||||
trusted-public-keys = lib.pipe buildServerDevices [
|
secret-key-files = [ "/etc/nix/signing-key.sec" ];
|
||||||
(lib.mapAttrsToList (_: v: v.distributedBuilds.storeSigningPublicKey or null))
|
};
|
||||||
(builtins.filter (k: k != null))
|
})
|
||||||
];
|
|
||||||
secret-key-files =
|
|
||||||
let
|
|
||||||
thisDevice = devices.${config.networking.hostName} or { };
|
|
||||||
in
|
|
||||||
lib.optional (thisDevice.distributedBuilds.isBuilder or false) "/etc/nix/signing-key.sec";
|
|
||||||
max-jobs = (devices.${config.networking.hostName}.distributedBuilds or { }).maxJobs or "auto";
|
|
||||||
cores = 0;
|
|
||||||
min-free = 10 * 1024 * 1024;
|
|
||||||
max-free = 200 * 1024 * 1024;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.nix-daemon.serviceConfig = {
|
# Client: connect to build servers for building and substitution
|
||||||
MemoryAccounting = true;
|
(lib.mkIf isClient {
|
||||||
MemoryMax = "90%";
|
programs.ssh = {
|
||||||
OOMScoreAdjust = 500;
|
knownHosts = buildServerKnownHosts;
|
||||||
};
|
extraConfig = ''
|
||||||
};
|
Host ${lib.concatStringsSep " " (lib.attrNames buildServerDevices)}
|
||||||
|
User ${buildUser}
|
||||||
|
IdentityFile ${clientSshKeyPath}
|
||||||
|
IdentitiesOnly yes
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
nix = {
|
||||||
|
distributedBuilds = buildMachines != [ ];
|
||||||
|
buildMachines = buildMachines;
|
||||||
|
settings = {
|
||||||
|
builders-use-substitutes = true;
|
||||||
|
substituters = map (m: "ssh-ng://${buildUser}@${m.hostName}") (
|
||||||
|
builtins.filter (m: m.distributedBuilds ? storeSigningPublicKey) remoteBuildServerDevices
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue