# harmonia: serving your /nix/store as a binary cache over HTTP

> harmonia is a Rust implementation of a Nix binary cache that publishes the local store over HTTP, with HTTP range requests for streaming NAR files, transparent zstd compression, optional TLS, and optional signing so other machines can trust what you serve. It reads the Nix SQLite database directly rather than going through nix-daemon, which is what makes the range support possible.

**nix-community/harmonia** — Nix binary cache implemented in rust (maintainer: @Mic92)

- Repository: https://github.com/nix-community/harmonia
- Stars: 635 · Forks: 39
- Language: Rust
- License: NOASSERTION
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/nix-community-harmonia

## A cache that publishes the local store instead of copying artefacts

harmonia is described as a binary cache for nix that serves your /nix/store as a binary cache over http, written in Rust for speed. The problem it solves is the ordinary Nix one: someone builds a package, and everyone else on the network rebuilds the same thing. A binary cache is the mechanism Nix provides for sharing build results, and the usual implementations are shell scripts over a web server with a CGI or a small daemon in the middle. harmonia is that mechanism rewritten, with the store itself as the source of the served content.

The project lives under the nix-community organisation with a maintainer listed in the repository description, the licence is MIT according to the workspace package metadata, and the releases are versioned with a harmonia- prefix, with harmonia-v3.3.0 published on 2026-09-18, harmonia-v3.2.0 on 2026-08-15 and harmonia-v3.1.0 on 2026-04-27. The last push to the repository was on 2026-10-18, so the project is being worked on now, and the workspace version is 3.3.0.

## HTTP ranges, streaming logs and the .ls file

The feature list is where the engineering shows. There is http-ranges support for nar file streaming, which is the mechanism a client uses to fetch part of a large archive rather than the whole thing, and streaming build logs so a build in progress is observable rather than opaque. There is .ls file streaming, with a caveat in the README that it does not contain narOffset in the json response but that this is not needed for nix-index. There is a /serve/<narhash>/ endpoint that allows serving the content of a package directly, and it also discovers index.html so that a website can be served straight out of the nix store, which is how a static site built with Nix becomes a fetchable URL.

Content is compressed transparently with zstd, so the client negotiates it and the server compresses on the fly without the operator turning anything on. And there is built-in TLS, so when no frontend webserver is used, harmonia can provide the encryption itself. That last point matters for the deployment shape, because the common NixOS setup puts nginx in front for ACME and certificate management, and a cache that can terminate TLS removes the need for that in simpler cases.

## Reading the Nix database directly, with no nix-daemon

The mechanism that makes the rest possible is described in the configuration comments rather than in a paragraph of prose. Harmonia reads store metadata directly from the Nix SQLite database, with no nix-daemon connection used, and the path is derived from the store layout by default so it only needs overriding for a non-standard state directory.

```toml
# Path to the nix SQLite database. Harmonia reads store metadata directly from
# this file (no nix-daemon connection is used). Derived from the store layout
# by default; override only for non-standard state directories.
# nix_db_path = "/nix/var/nix/db/db.sqlite"
```

Reading the database directly is what allows the HTTP range support and the .ls streaming to work against the real store paths, since no protocol round trip to a daemon is needed to work out what a path contains. The same reasoning explains the two store overrides in the default configuration: virtual_nix_store to change the store path advertised in /nix-cache-info, and real_nix_store to serve the nix store from a different physical location, with the comment pointing at nix copy --store /guest as the way to populate such a store.

## The default configuration is a TOML file passed by environment variable

Configuration is done via a toml file, and the hint in the README is that you do not need to interface with the configuration directly if you are using the NixOS module. The location of the configuration file should be passed as the environment variable CONFIG_FILE, and if no config file is passed, the defaults apply.

```toml
# default ip:hostname to bind to
bind = "[::]:5000"
# unix socket are also supported
# bind = "unix:/run/harmonia/socket"
# Sets number of workers to start in the webserver
workers = 4
# Sets the per-worker maximum number of concurrent connections.
max_connection_rate = 256
# binary cache priority that is advertised in /nix-cache-info
priority = 30
```

The two performance knobs are workers, the number of webserver workers, and max_connection_rate, the per-worker maximum number of concurrent connections. The priority value is what a substituter uses to choose between caches when several offer the same path, and 30 is the default here. The commented unix socket binding is the option to use when the cache should not be reachable from the network at all, for example behind a reverse proxy on the same host.

## By default nothing is signed, which is the point to check first

The security posture of a binary cache comes down to whether it signs its narinfo, and the README is explicit that per default harmonia will not sign any narinfo because there is no secret key, and that enabling this feature means providing a path to a private key generated with nix-store --generate-binary-cache-key cache.example.com-1 /etc/nix/cache.secret /etc/nix/cache.pub.

```toml
# nix binary cache signing key
sign_key_paths = [ "/run/secrets/cache.secret" ]
```

There is a second route, the environment variable SIGN_KEY_PATHS, which holds paths to secret keys separated by spaces, and the README says all paths provided by the sign_key_paths config option and by SIGN_KEY_PATHS will be used for signing. So a key can come from the config file, from the environment, or from both.

This is the setting that decides whether the cache is usable by other machines. A substituter only accepts a path from a cache if it can verify the signature, so an unsigned cache serves your own machine and nothing else, which is a reasonable default for a first run and a silent failure for a shared one.

## TLS without a reverse proxy, and the certificate rules

TLS is configured with two paths in the TOML file.

```toml
# Path to TLS certificate (PEM format)
tls_cert_path = "/path/to/cert.pem"
# Path to TLS private key (PEM format)
tls_key_path = "/path/to/key.pem"
```

The README lists the requirements, and they are the sort of detail that otherwise causes a failed startup. The certificate must be in PEM format and must be X.509 v3, because rustls does not support older versions. The private key can be in either PKCS#8 or RSA format, and both files must be readable by the harmonia process. The self-signed certificate example uses openssl with a 2048 bit RSA key and a 365 day lifetime.

```bash
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes \
  -subj "/C=US/ST=State/O=Organization/CN=cache.example.com"
```

And there is a behaviour change to be aware of: when TLS is enabled, harmonia will only accept HTTPS connections on the configured port, so a configuration that enables TLS and leaves plain HTTP clients pointed at the same port will stop working rather than silently downgrading.

## The NixOS module, the flake, and how clients trust the cache

The README documents two ways to run it on NixOS. There is a module for harmonia in nixpkgs, and the example sets up a public binary cache using nginx as a frontend webserver with https encryption, with services.harmonia.cache.enable set to true, a signKeyPaths pointing at the secret, the ACME email and the virtual host proxying to http://127.0.0.1:5000, which is the port the default bind uses. The alternative is the flake, which imports inputs.harmonia.nixosModules.harmonia and enables services.harmonia-dev.cache.enable, with the note that all other nginx configuration remains the same as in the stable example.

On the client side, the machine that consumes the cache needs two settings, a substituter URL and a trusted public key.

```nix
{
  nix.settings = {
    substituters = [ "https://cache.yourdomain.tld" ];
    # FIXME replace the key with the content of /var/lib/secrets/harmonia.pub
    trusted-public-keys = [ "cache.yourdomain.tld-1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" ];
  };
}
```

The key format carries the cache name and a base64 key, and it is the same key pair the server signs with, which is why the server-side configuration comments spend so much effort on generating one with nix-store --generate-binary-cache-key and keeping the secret out of the repository.

## Conclusion

harmonia fits a Nix user or a small organisation that wants to publish its own build artefacts so other machines can substitute them instead of rebuilding, and that wants the cache to be a single Rust binary with its own configuration file rather than a set of shell scripts. It does not fit a user who only wants to consume caches, since the public ones already exist, and it does not fit a deployment that cannot expose a signed key, because an unsigned cache gives substituters nothing to verify. Before you run it, read the store metadata access note, since harmonia reads the Nix SQLite database directly and no nix-daemon connection is used, and generate a binary cache key pair as the configuration comments instruct, because the default configuration signs nothing at all.

## FAQ

### What is harmonia and what does it do?

harmonia is a binary cache for nix that serves your /nix/store as a binary cache over http, written in Rust for speed. It supports http-ranges for nar file streaming, streaming build logs, .ls file streaming, a /serve/<narhash>/ endpoint that can also serve websites from the nix store, transparent zstd compression and built-in TLS.

### How do I configure harmonia?

Through a toml file whose location is passed in the CONFIG_FILE environment variable. The defaults bind to [::]:5000 with 4 workers, a per-worker maximum of 256 concurrent connections, a cache priority of 30 advertised in /nix-cache-info and transparent zstd compression enabled, and a unix socket bind is available as a commented alternative.

### Does harmonia sign the cache contents by default?

No. The README says that per default it will not sign any narinfo because there is no secret key. You enable it by providing a path to a private key generated with nix-store --generate-binary-cache-key, either through the sign_key_paths config option or through the SIGN_KEY_PATHS environment variable, and all paths from both are used for signing.

### How do other machines trust a harmonia binary cache?

The client sets substituters to the cache URL and lists the public key in trusted-public-keys, in the form cache.yourdomain.tld-1 followed by the base64 key content from the generated .pub file. The README marks the placeholder key as something to replace with the real one.

### How does harmonia read the Nix store?

It reads store metadata directly from the Nix SQLite database, with no nix-daemon connection used, and the path is derived from the store layout by default. The configuration comments note that nix_db_path only needs overriding for a non-standard state directory.

## Sources

- [Issues](https://github.com/nix-community/harmonia/issues)
- [nix-community/harmonia on GitHub](https://github.com/nix-community/harmonia)
- [README](https://github.com/nix-community/harmonia/blob/main/README.md)
- [Releases](https://github.com/nix-community/harmonia/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/nix-community-harmonia
