nix-serve-ng is a faster, more reliable, drop-in replacement for nix-serve.
There are three main approaches you can use to configure a NixOS system to replace
the old nix-serve with nix-serve-ng:
- A: Set
services.nix-serve.package = pkgs.nix-serve-ng;in your NixOS configurationnix-serve-ngis packaged in nixpkgs already- There is no need to consume this repository directly
- B: Include
nix-serve-ng.nixosModules.defaultin your NixOS configurationnix-serve-ngrefers to this repository being a flake input- Requires consume this repository / this flake
- Overlays
pkgs.nix-servewithpkgs.nix-serve-ng
- C: Like B but not requiring a flake
We recommend approach A. Only use B or C if you need a bleeding edge upstream version of the project.
The code snippet below shows a flake.nix.
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs";
outputs = { nixpkgs, ... }: {
nixosConfigurations.default = nixpkgs.lib.nixosSystem {
modules = [
/* ... */
{
services.nix-serve.enable = true;
services.nix-serve.package = pkgs.nix-serve-ng;
/* ... */
}
/* ... */
];
};
};
}The code snippet below shows a flake.nix.
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs";
inputs.nix-serve-ng.url = "aristanetworks/nix-serve-ng";
outputs = { nixpkgs, nix-serve-ng, ... }: {
nixosConfigurations.default = nixpkgs.lib.nixosSystem {
modules = [
nix-serve-ng.nixosModules.default
/* ... */
{
services.nix-serve.enable = true;
/* ... */
}
/* ... */
];
};
};
}The code snippet below shows a NixOS module file.
{ config, pkgs, lib, ... }:
let
nix-serve-ng-src = builtins.fetchTarball {
# Replace the URL and hash with whatever you actually need
url = "https://github.com/aristanetworks/nix-serve-ng/archive/1937593598bb1285b41804f25cd6f9ddd4d5f1cb.tar.gz";
sha256 = "1lqd207gbx1wjbhky33d2r8xi6avfbx4v0kpsvn84zaanifdgz2g";
};
nix-serve-ng = import nix-serve-ng-src;
in
{
/* ... */
imports = [ nix-serve-ng.nixosModules.default ];
config = {
services.nix-serve.enable = true;
};
/* ... */
}Our requirements for this project were:
-
Improve reliability
… since
nix-servewould intermittently hang and require restarts -
Improve efficiency
… since
nix-servewas doing some obviously inefficient things which we felt we could improve upon -
Be backwards-compatible
Our replacement would need to be a drop-in replacement for the original
nix-serve, supporting the same command-line options and even sharing the same executable nameThe only exception is logging: we provide more detailed logging than before
Did we satisfy those requirements?
-
Reliability
We have test-driven this internally under heavy load with stable memory usage and without any failures but it's probably premature to declare victory.
In particular, we have not done the following things:
-
Memory leak detection
In other words, we haven't put our
nix-servethrough, say,valgrind -
Exploit detection
In other words, we haven't attempted to crash or sabotage the service with maliciously-crafted payload
-
-
Performance
We have improved significantly on efficiency, not only compared to
nix-servebut also compared to othernix-serverewrites. We are more efficient than:See the Benchmarks section below for more details
-
Backwards-compatibility
We have excellent backwards-compatibility, so in the vast majority of cases, you can simply replace
pkgs.nix-servewithpkgs.nix-serve-ngand make no other changes.-
Our executable shares the same name (
nix-serve) as the original program -
We support most the original command-line options
The options that we're aware of that we do not currently support fall into two categories:
-
Useless options which are only relevant to
starman:Upon request, we can still parse and ignore the following irrelevant options for extra backwards compatibility:
-
--workersWe do not use worker subprocess like
starmandoes. Instead we usewarpwhich internally uses Haskell green threads to service a much larger number of requests with less overhead and lower footprint when idle. -
--preload-appThis optimization is meaningless for a compiled Haskell executable.
-
--disable-proctitle
-
-
Useful options
We might accept requests to support the following options, but we might explore other alternatives first before supporting them:
-
--max-requestswarpitself is unlikely to be a bottleneck to servicing a large number of requests but there may still be Nix-specific or disk-specific reasons to cap the number of requests. -
--disable-keepalive -
--keepalive-timeout -
--read-timeout -
--user -
--group -
--pid -
--error-log
-
-
Because of this backwards-compatibility you only need to replace the old
nix-serveexecutable with thenix-serveexecutable built by this package (which is what the included NixOS module does).You don't need to define or use any new NixOS options. You continue to use the old
services.nix-serveoptions hierarchy to configure the upgraded service. -
The test environment is a large server machine:
- CPU: 24 × Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz
- RAM: 384 GB (24 × 16 GB @ 2133 MT/s)
- Disk (
/nix/store): ≈4 TB SSD
Legend:
- Fetch present NAR info ×10: Time to fetch the NAR info for 10 different files that are present
- Fetch absent NAR info ×1: Time to fetch the NAR info a single file that is absent
- Fetch empty NAR ×10: Time to fetch the NAR for the same empty file 10 times
- Fetch 10 MB NAR ×10: Time to fetch the NAR for the same 10 MB file 10 times
Raw numbers:
| Benchmark | nix-serve |
eris |
harmonia |
nix-serve-ng |
|---|---|---|---|---|
| Fetch present NAR info ×10 | 2.09 ms ± 66 μs | 41.5 ms ± 426 μs | 1.57 ms ± 91 μs | 1.32 ms ± 33 μs |
| Fetch absent NAR info ×1 | 212 μs ± 18 μs | 3.42 ms ± 113 μs | 139 μs ± 11 μs | 115 μs ± 6.2 μs |
| Fetch empty NAR ×10 | 164 ms ± 8.5 ms | 246 ms ± 20 ms | 279 ms ± 10 ms | 5.16 ms ± 368 μs |
| Fetch 10 MB NAR ×10 | 291 ms ± 8.7 ms | 453 ms ± 19 ms | 487 ms ± 41 ms | 86.9 ms ± 3.0 ms |
Speedups (compared to nix-serve):
| Benchmark | nix-serve |
eris |
harmonia |
nix-serve-ng |
|---|---|---|---|---|
| Fetch present NAR info ×10 | 1.0 | 0.05 | 1.33 | 1.58 |
| Fetch absent NAR info ×1 | 1.0 | 0.06 | 1.53 | 1.84 |
| Fetch empty NAR ×10 | 1.0 | 0.67 | 0.59 | 31.80 |
| Fetch 10 MB NAR ×10 | 1.0 | 0.64 | 0.60 | 3.35 |
We can summarize nix-serve-ng's performance like this:
- Time to handle a NAR info request: ≈ 100 μs
- Time to serve a NAR: ≈ 500 μs + 800 μs / MB
You can reproduce these benchmarks using the benchmark suite. See the
instructions in ./benchmark/Main.hs for running your
own benchmarks.
Caveats:
- We haven't used any of these services' tuning options, including:
- Tuning garbage collection (for
nix-serve-ng) - Tuning concurrency/parallelism/workers
- Tuning garbage collection (for
- We haven't benchmarked memory utilization