Skip to content

Commit

Permalink
review pass
Browse files Browse the repository at this point in the history
Co-authored-by: wamirez <wamirez@protonmail.com>
  • Loading branch information
fricklerhandwerk and wamirez committed Aug 31, 2024
1 parent 40e5e3f commit efbbb12
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 104 deletions.
4 changes: 4 additions & 0 deletions source/guides/recipes/add-binary-cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

Nix can be configured to use a binary cache with the [`substituters`](https://nix.dev/manual/nix/2.21/command-ref/conf-file.html#conf-substituters) and [`trusted-public-keys`](https://nix.dev/manual/nix/2.21/command-ref/conf-file.html#conf-trusted-public-keys) settings, either exclusively or in addition to cache.nixos.org.

:::{tip}
Follow the tutorial to [set up an HTTP binary cache](setup-http-binary-cache) and create a key pair for signing store objects.
:::

For example, given a binary cache at `https://example.org` with public key `My56...Q==%`, and some derivation in `default.nix`, make Nix exclusively use that cache once by passing [settings as command line flags](https://nix.dev/manual/nix/2.21/command-ref/conf-file#command-line-flags):

```shell-session
Expand Down
207 changes: 103 additions & 104 deletions source/tutorials/nixos/binary-cache-setup.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
---
myst:
html_meta:
"description lang=en": "Setting up a binary cache to reuse builds"
"description lang=en": "Setting up a binary cache for store objects"
"keywords": "Nix, caching"
---

# Setting up a binary cache
(setup-http-binary-cache)=
# Setting up an HTTP binary cache

A binary cache stores prebuilt [Nix store objects](https://nix.dev/manual/nix/latest/store/store-object) and provides them to other machines over the network.
This way, one machine can download prebuilt packages from another instead of rebuilding them.
Any machine with a Nix store can be a binary cache for other machines.
A binary cache stores pre-built [Nix store objects](https://nix.dev/manual/nix/latest/store/store-object) and provides them to other machines over the network.
Any machine with a Nix store can be an binary cache for other machines.

## Introduction

In this tutorial you will set up a Nix binary cache that will serve store objects from a NixOS machine over HTTP or HTTPS.

### What will you learn?

You'll learn how to
You'll learn how to:
- Set up signing keys for your cache
- Enable the right services on the NixOS machine serving the cache
- Check that the setup works as intended

### Prerequisites
### What do you need?

- A machine that runs NixOS
- A working [Nix installation](<install-nix>) on your local machine

If you're new to NixOS, learn about the [](module-system-deep-dive) and [](nixos-vms) to configure your first system.
- SSH access to a NixOS machine to use as a cache

If you're new to NixOS, learn about the [module system](module-system-deep-dive) and configure your first system with [](nixos-vms).

- (optional) A public IP and DNS domain

Expand All @@ -43,11 +45,11 @@ For a publicly accessible cache, we assume:

### How long will it take?

~10 minutes
- 25 minutes

## Set up services

Create a new NixOS configuration module file `binary-cache.nix` in the same folder where your `configuration.nix` is:
For the NixOS machine hosting the cache, create a new configuration module in `binary-cache.nix`:

```{code-block} nix
{ config, ... }:
Expand All @@ -61,7 +63,7 @@ Create a new NixOS configuration module file `binary-cache.nix` in the same fold
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts."cache.example.com" = {
virtualHosts.cache = {
locations."/".proxyPass = "http://${config.services.nix-serve.bindAddress}:${toString config.services.nix-serve.port}";
};
};
Expand All @@ -72,15 +74,19 @@ Create a new NixOS configuration module file `binary-cache.nix` in the same fold
}
```

The attributes under `services.nix-serve.*` install and enable the binary cache service.
See also [the `services.nix-serve` options reference on search.nixos.org][nix-serve-options].
The options under [`services.nix-serve`] configure the binary cache service.

`nix-serve` doesn't support IPv6 or SSL/HTTPS.
The [`services.nginx`] options are used to set up a proxy, which does support IPv6, to handle requests to the hostname `cache`.

[`services.nix-serve`]: https://search.nixos.org/options?query=services.nix-serve
[`services.nginx`]: https://search.nixos.org/options?query=services.nginx

`nix-serve` does not serve via IPv6 and does not support SSL/HTTPS.
For this reason, this tutorial configures `services.nginx.*`.
Nginx listens on the HTTP port and forwards all connections to `nix-serve`.
There is an optional HTTPS section in the end of this tutorial.
:::{important}
There is an [optional HTTPS section](https-binary-cache) at the end of this tutorial.
:::

Add the new NixOS module to your existing `configuration.nix`:
Add the new NixOS module to the existing machine configuration:

```{code-block} nix
{ config, ... }:
Expand All @@ -94,145 +100,138 @@ Add the new NixOS module to your existing `configuration.nix`:
}
```

Activate the new configuration as root:
From your local machine, deploy the new configuration:

```shell-session
nixos-rebuild switch
nixos-rebuild switch --no-flake --target-host root@cache
```

:::{note}
The binary cache daemon will report errors because there is no secret key file, yet.
:::

## Generate key pair
## Generate a signing key pair

A pair of private and public keys is important to ensure that the store objects in the cache can be trusted.
The private key is either used to sign store objects right after are built, or the binary cache signs the store objects while serving them.
A pair of private and public keys is required to ensure that the store objects in the cache are authentic.

<!-- TODO: link to the remote builds tutorial for the case where store objects are signed after building them -->

To generate a key pair for the binary cache, replace the example hostname `cache.example.com` with your hostname:

```shell-session
mkdir /var/secrets
cd /var/secrets
nix-store --generate-binary-cache-key cache.example.com cache-private-key.pem cache-public-key.pem
```

`cache-private-key.pem` will be used by the binary cache daemon to sign the binaries as they are served.
Copy it to the location configured in `services.nix-serve.secretKeyFile` on the machine hosting the cache:

```shell-session
scp cache-private-key.pem root@cache:/var/secrets/cache-private-key.pem
```

Up until now, the binary cache daemon was in a restart loop due to the missing secret key file.
It should now work correctly, which can be checked with the command `systemctl status nix-serve.service`.
Check that it now works correctly:

Distribute `cache-public-key.pem` to all machines that should be able to access the binary cache.
```shell-session
ssh root@cache systemctl status nix-serve.service
```

:::{note}
The location `/var/secrets/` for keeping the key pair is not a requirement and can be chosen differently.
:::{important}
[](custom-binary-cache) using `cache-public-key.pem` on your local machine.
:::

## Test availability

The setup is complete.
The following steps check if everything is set up correctly and may help identifying problems.
The following steps check if everything is set up correctly and may help with identifying problems.

### 1. Check general availability
### Check general availability

Test if the binary cache, HTTP(s) reverse proxy, and firewall rules work correctly by running this command on a client machine:
Test if the binary cache, reverse proxy, and firewall rules work as intended by querying the cache:

```shell-session
$ curl http://cache.example.com/nix-cache-info
$ curl http://cache/nix-cache-info
StoreDir: /nix/store
WantMassQuery: 1
Priority: 30
```

### 2. Check binary signing

Test if binaries are signed correctly with the following two steps.
### Check store object signing

On the binary cache host, run this command:
To test if store objects are signed correctly, inspect the metadata of a sample derivation.
On the binary cache host, build the `hello` package and get the `.narinfo` file from the cache:

```shell-session
$ curl "http://cache.example.com/$(nix-build '<nixpkgs>' -A pkgs.hello | awk -F '/' '{print $4}' | awk -F '-' '{print $1}').narinfo" | grep "Sig: "
$ hash=$(nix-build '<nixpkgs>' -A pkgs.hello | awk -F '/' '{print $4}' | awk -F '-' '{print $1}')
$ curl "http://cache/$hash.narinfo" | grep "Sig: "
...
Sig: build01.nix-consulting.de:GyBFzocLAeLEFd0hr2noK84VzPUw0ArCNYEnrm1YXakdsC5FkO2Bkj2JH8Xjou+wxeXMjFKa0YP2AML7nBWsAg==
Sig: cache.example.org:GyBFzocLAeLEFd0hr2noK84VzPUw0ArCNYEnrm1YXakdsC5FkO2Bkj2JH8Xjou+wxeXMjFKa0YP2AML7nBWsAg==
```

It is important that the output contains this line prefixed with `Sig:` with the previously generated public key.
Make sure that the output contains this line prefixed with `Sig:` and shows the public key you generated.

:::{note}
This one-liner builds a package and extracts its hash to calculate a valid URL in the cache.
(From `/nix/store/<hash>-<name>-<version>`, it derives `http://cache.example.com/<hash>.narinfo`.)
Querying this URL exposes information about the cached nix store path.
:::
(https-binary-cache)=
### Serving the binary cache via HTTPS

## Outlook
If the binary cache is publicly accessible, it is possible to enforce HTTPS with [Let's Encrypt](https://letsencrypt.org/) SSL certificates.
Edit your `binary-cache.nix` like this and make sure to replace the example URL and mail address with yours:

You can now distribute the hostname and public key to anyone who wants access to your new binary cache.
Configure clients with [this guide about binary cache client configuration](custom-binary-cache).
```{code-block} diff
services.nginx = {
enable = true;
recommendedProxySettings = true;
- virtualHosts.cache = {
+ virtualHosts."cache.example.com" = {
+ enableACME = true;
+ forceSSL = true;
locations."/".proxyPass = "http://${config.services.nix-serve.bindAddress}:${toString config.services.nix-serve.port}";
};
};
+ security.acme = {
+ acceptTerms = true;
+ certs = {
+ "cache.example.com".email = "you@example.com";
+ };
+ };
networking.firewall.allowedTCPPorts = [
config.services.nginx.defaultHTTPListenPort
+ config.services.nginx.defaultSSLListenPort
];
```

If your binary cache is already a [remote build machine][remote-build-machine], it will serve all binaries in its nix store.
Rebuild the system to deploy these changes:

Other hosts can be configured to automatically push binaries to the binary cache using [the `post-build-hook` feature (Guide)](post-build-hooks).
```shell-session
nixos-rebuild switch --no-flake --target-host root@cache.example.com
```

## Next steps

- [](custom-binary-cache)
- [](post-build-hooks)

To save space, please refer to the following NixOS configuration attributes:
If your binary cache is already a [remote build machine](https://nix.dev/manual/nix/latest/advanced-topics/distributed-builds), it will serve all store objects in its Nix store.

- [`nix.gc.*`][nix-gc]: Automatic periodic garbage collection Settings
- [`nix.optimise.*`][nix-optimise]: Automatic periodic nix store optimisation
- [](custom-binary-cache) using the binary cache's hostname and the generated public key
- [](post-build-hooks) to upload store objects to the binary cache

### Serving via HTTPS from a public address
To save storage space, please refer to the following NixOS configuration attributes:

If the binary cache is publicly accessible, it is possible to enforce HTTPS with [Let's encrypt](https://letsencrypt.org/) SSL certificates.
Edit your `binary-cache.nix` like this and make sure to replace the example URL and mail address with yours:

```{code-block} nix
{ config, ... }:
{
# ...
- [`nix.gc`](https://search.nixos.org/options?query=nix.gc): Options for automatic garbage collection
- [`nix.optimise`](https://search.nixos.org/options?query=nix.optimise): Options for periodic Nix store optimisation

services.nginx = {
# ...
virtualHosts."cache.example.com" = {
enableACME = true;
forceSSL = true;
# ...
};
};
networking.firewall.allowedTCPPorts = [
config.services.nginx.defaultHTTPListenPort
config.services.nginx.defaultSSLListenPort
];
security.acme = {
acceptTerms = true;
certs = {
"cache.example.com".email = "you@example.com";
};
};
}
```

Rebuild the system to activate these changes.
## Alternatives

- The [SSH Store](https://nix.dev/manual/nix/latest/store/types/ssh-store), [Experimental SSH Store](https://nix.dev/manual/nix/latest/store/types/experimental-ssh-store), and the [S3 Binary Cache Store](https://nix.dev/manual/nix/latest/store/types/s3-binary-cache-store) can also be used to serve a cache.
There are many commercial providers for S3-compatible storage, for example:
- Amazon S3
- Tigris
- Cloudflare R2

## Alternatives
- [attic](https://github.com/zhaofengli/attic): Nix binary cache server backed by an S3-compatible storage provider

- [Cachix](https://www.cachix.org): Nix Binary Cache as a Service
- Amazon S3: Nix supports pushing to and pulling from S3 buckets (see [Nix manual about S3][nix-s3])
- Tigris: An alternative to S3
- Cloudflare R2: Another alternative to S3
- [attic](https://github.com/zhaofengli/attic): Alternative to `nix-serve` (open source)
- [Cachix](https://www.cachix.org): Nix binary cache as a service

## References

- [Nix Manual on HTTP Binary Cache Store](https://nix.dev/manual/nix/latest/store/types/http-binary-cache-store)
- [`services.nix-serve` module definition](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/nix-serve.nix)

[nix-serve-options]: https://search.nixos.org/options?query=services.nix-serve
[nginx-ssl]: https://nixos.org/manual/nixos/stable/#module-security-acme
[nixos-ipv6]: https://nixos.org/manual/nixos/stable/#sec-ipv6
[nix-gc]: https://search.nixos.org/options?query=nix.gc.
[nix-optimise]: https://search.nixos.org/options?query=nix.optimise.
[remote-build-machine]: https://nix.dev/manual/nix/latest/advanced-topics/distributed-builds
- [`services.nix-serve` module options][`services.nix-serve`]
- [`services.nginx` module options][`services.nginx`]
6 changes: 6 additions & 0 deletions source/tutorials/nixos/provisioning-remote-machines.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@ nixos-rebuild switch --no-flake --target-host root@target-host

`nixos-anywhere` is not needed any more, unless you want to change the disk layout.


# Next steps

- [](binary-cache-setup)
- [](post-build-hooks)

## References

- [`nixos-anywhere` project page][`nixos-anywhere`]
Expand Down

0 comments on commit efbbb12

Please sign in to comment.