Private S3 Buckets - Mic92/niks3 GitHub Wiki
Private S3 Buckets
By default, niks3 expects Nix clients to read directly from S3. This requires the bucket to be publicly readable or fronted by a CDN/reverse proxy with its own authentication.
For private buckets, niks3 includes a built-in read proxy that fetches objects from S3 using the server's credentials and serves them to unauthenticated Nix clients over HTTP.
When to use the read proxy
- Your S3 bucket is private and you don't want to make it public
- You don't want to set up a separate nginx reverse proxy with S3 auth
- You want a simple, single-binary setup
When NOT to use the read proxy
- Your bucket is public or behind a CDN — direct S3 reads are more scalable
- You need response caching — put a CDN or nginx cache in front instead
- You need authenticated reads from clients — use nginx with auth in front
NixOS Configuration
{
services.niks3 = {
enable = true;
readProxy.enable = true;
# ... other settings
};
}
CLI / Environment Variable
niks3-server --enable-read-proxy [other flags...]
# or
NIKS3_ENABLE_READ_PROXY=true
Client Configuration
With the proxy enabled, Nix clients use the niks3 server URL as their substituter instead of the S3 URL:
# Without proxy (direct S3 — requires public bucket or S3 credentials)
nix.settings.substituters = [ "s3://my-bucket?endpoint=https://..." ];
# With proxy (private bucket, no S3 credentials needed on clients)
nix.settings.substituters = [ "https://my-niks3-server.example.com" ];
Or on the command line:
nix build --substituters 'https://my-niks3-server.example.com' \
--trusted-public-keys 'my-cache-1:...' \
nixpkgs#hello
How it works
The server proxies GET and HEAD requests for known Nix binary cache objects (narinfo, NAR, build logs, realisations, etc.) by fetching them from S3 and streaming them to the client. Write operations continue through the existing presigned-URL flow and are unaffected.
Narinfo decompression
niks3 stores narinfo files zstd-compressed in S3. The proxy transparently decompresses them before serving, since Nix's HTTP binary cache client expects plain text narinfos.
Security
Only known Nix binary cache path patterns are proxied — arbitrary bucket contents cannot be accessed. Path traversal attacks are rejected.
Performance
- NAR files are streamed via
io.Copy, not buffered in memory - All proxy requests respect the existing S3 rate limiter
- The proxy is stateless — no in-process caching
- For caching, put a CDN or nginx cache in front of the proxy
Adding a caching layer
For high-traffic deployments, add nginx or a CDN in front:
{
services.niks3 = {
enable = true;
readProxy.enable = true;
httpAddr = "127.0.0.1:5751";
nginx = {
enable = true;
domain = "cache.example.com";
};
};
# Add nginx caching on top
services.nginx.virtualHosts."cache.example.com".locations."/".extraConfig = ''
proxy_cache_valid 200 24h;
proxy_cache_valid 404 5m;
'';
}