Storage for Go, Nix, NPM, and Python packages.
- Read access: Serve NAR files and narinfo metadata from your local Nix store
- Upload support: Accept uploads of compressed NAR files (.nar, .nar.xz, .nar.gz, .nar.bz2) and narinfo metadata via HTTP PUT
- SSH Authentication: JWT-based authentication using SSH public keys with read/write permissions
- Proxy & Push: Built-in proxy and push commands for authenticated access to remote caches
- Compression support: Automatic decompression of uploaded NAR files
- NAR processing: Uses go-nix library for proper NAR file parsing and extraction
- Logging: Structured JSON logging with configurable verbosity
- Storage backends: Filesystem or S3-compatible storage (AWS S3, SeaweedFS, GCP Cloud Storage)
- Kubernetes deployment: See
./k8sfor example Kubernetes manifests.
nix key generate-secret --key-name local-depot-1 > signing.keyThe --key-name can be any identifier you choose (e.g., mycompany-cache, home-cache-1). The -1 suffix is a convention for key versioning. Use the same key name consistently when configuring clients to trust your cache.
depot serve --private-key signing.key --verboseExtract the public key from your signing key, or fetch the public key from the Nix cache info handler (http://localhost:8080/nix/nix-cache-info).
# Convert the private key to public key
nix key convert-secret-to-public < signing.key
# Output: local-depot-1:public-key-base64...Add the public key to your Nix configuration to trust signatures from your cache. Replace local-depot-1:public-key-base64... with the actual output from the command above:
# Option 1: Add to ~/.config/nix/nix.conf
echo "trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= local-depot-1:public-key-base64..." >> ~/.config/nix/nix.conf
# Option 2: Add to /etc/nix/nix.conf (system-wide, requires sudo)
sudo sh -c 'echo "trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= local-depot-1:public-key-base64..." >> /etc/nix/nix.conf'
# Option 3: Use flags when running nix commands
nix build --option trusted-public-keys "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= local-depot-1:public-key-base64..." --option substituters "http://localhost:8080 https://cache.nixos.org" nixpkgs#slAdd your depot as a substituter:
# Add to nix.conf (depot first, then official cache as fallback)
echo "substituters = http://localhost:8080 https://cache.nixos.org" >> ~/.config/nix/nix.conf
# Or use the --option flag
nix build --option substituters "http://localhost:8080 https://cache.nixos.org" nixpkgs#slNote: The order matters - Nix checks substituters sequentially. List your depot first for faster lookups, with the official cache as a fallback.
Create an auth file with SSH public keys:
# auth.keys - format: permission ssh-keytype base64key comment
w ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABA... user@laptop
r ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABA... user@desktopThen start the server:
depot serve --auth-file auth.keys --private-key signing.key --verbose# Push a flake reference
depot push https://my-cache.example.com --flake-refs github:NixOS/nixpkgs#sl
# Push store paths
depot push https://my-cache.example.com --store-paths /nix/store/abc123...
# Push from stdin
echo "/nix/store/abc123..." | depot push https://my-cache.example.com --stdinOr push using nix copy - see push-without-tools for complete push examples.
nix copy --to https://my-cache.example.com nixpkgs#sl# Save specific modules.
depot go save golang.org/x/text@v0.21.0 rsc.io/quote@v1.5.2
# Save all dependencies from a go.mod file.
depot go save ./go.modThis saves modules and their transitive dependencies to .depot-storage/go. Each module's .info, .mod, and .zip files are fetched from proxy.golang.org.
depot go push http://localhost:8080# Point GOPROXY at your depot server.
export GOPROXY=http://localhost:8080/go,direct
export GONOSUMCHECK='*'
export GONOSUMDB='*'
# Or configure persistently.
go env -w GOPROXY=http://localhost:8080/go,direct
go env -w GONOSUMCHECK='*'
go env -w GONOSUMDB='*'In an air-gapped environment, omit ,direct so that Go does not attempt to fall back to direct fetching:
export GOPROXY=http://localhost:8080/goGONOSUMCHECK and GONOSUMDB disable checksum database lookups, which are unreachable in an air-gapped environment.
Then use Go as normal:
go mod download
go build ./...depot npm save expressThis will create a .depot-storage in the current directory containing the NPM package tarballs and metadata.
depot npm push http://localhost:8080npm install --registry http://localhost:8080 express# Download specific packages.
depot python save "requests>=2.0.0" "flask==2.3.0"
# Download from a requirements.txt file.
depot python save --stdin < requirements.txt
# Or pipe a list of packages.
echo "requests>=2.0.0" | depot python save --stdinThis will create a .depot-storage/python directory containing the Python package files and metadata.
The save command supports standard Python package specifiers:
package==1.0.0- Exact versionpackage>=1.0.0- Minimum versionpackage>=1.0.0,<2.0.0- Version rangepackage~=1.4.2- Compatible release
depot python push http://localhost:8080# Install from your depot.
pip install --index-url http://localhost:8080/python/simple/ requests
# Use as an additional package index.
pip install --extra-index-url http://localhost:8080/python/simple/ requests
# In requirements.txt, use:
# --index-url http://localhost:8080/python/simple/
# requests>=2.0.0Or configure pip permanently in pip.conf or ~/.pip/pip.conf:
[global]
index-url = http://localhost:8080/python/simple/The server supports SSH key-based authentication using JWT tokens. Authentication is configured via a text file containing SSH public keys with permission levels.
Each line in the auth file has the format:
<permission> <ssh-keytype> <base64-key> <comment>
Where:
permission:rfor read-only,wfor read-writessh-keytype: SSH key type (e.g.,ssh-rsa,ssh-ed25519)base64-key: The base64-encoded public keycomment: Optional comment
Example auth file:
w ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... admin@laptop
r ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD... readonly@desktop
w ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... deploy@ci
You can fetch public SSH keys from a Github username, by adding .keys, e.g. https://github.com/$GITHUB_USERNAME.keys
RSA, ECDSA (P-256, P-384, P-521) and ed25519 keys are supported, from ~/.ssh or from an SSH agent (including gpg-agent).
FIDO2 security keys (sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com) and SSH certificates are not supported, because their signatures cannot be verified as JWT signatures.
By default, the proxy and nix push commands use the first supported key they find, preferring agent keys over key files. Use -i / --identity (or DEPOT_IDENTITY) to choose a key, in the same way as ssh -i:
depot proxy https://my-cache.example.com -i ~/.ssh/id_ed25519
depot nix push https://my-cache.example.com -i ~/.ssh/id_ed25519 --flake-refs github:NixOS/nixpkgs#slIf the private key file cannot be read, because it is encrypted or is held in an agent, the matching public key (~/.ssh/id_ed25519.pub) is used to select the key from the agent.
- If any key has read-only (
r) permission, all access requires authentication - If only write (
w) keys are configured, only uploads require authentication - If no auth file is provided, no authentication is required
The depot proxy command creates an authenticated proxy to a remote cache:
# Start proxy on default port (43407)
depot proxy https://my-cache.example.com
# Start proxy on random port
depot proxy https://my-cache.example.com --port 0
# Then use nix commands through the proxy
nix copy --to http://localhost:43407 nixpkgs#slThe server supports uploading NAR files in multiple compression formats:
.nar- Uncompressed NAR files.nar.xz- XZ-compressed NAR files (most common with Nix).nar.gz- Gzip-compressed NAR files.nar.bz2- Bzip2-compressed NAR files
The server automatically detects the compression format from the URL and decompresses the content before processing.
Start server with S3 storage backend:
# Using AWS S3 with IAM role (EC2/ECS/Fargate).
depot serve \
--storage-type=s3 \
--s3-bucket=my-depot-bucket \
--s3-region=us-east-1 \
--database-url=postgres://user:pass@host/db
# Using SeaweedFS with explicit credentials.
depot serve \
--storage-type=s3 \
--s3-bucket=depot \
--s3-region=us-east-1 \
--s3-endpoint=http://localhost:8333 \
--s3-access-key-id=testkey \
--s3-secret-access-key=testsecret \
--s3-force-path-style=true \
--database-url=sqlite:///tmp/depot.db
# Environment variables can also be used.
export DEPOT_STORAGE_TYPE=s3
export DEPOT_S3_BUCKET=my-depot-bucket
export DEPOT_S3_REGION=us-east-1
depot serveS3 configuration options:
--storage-type: Set tos3for S3 storage--s3-bucket: S3 bucket name (required)--s3-region: AWS region (default: us-east-1)--s3-endpoint: Custom endpoint for self-hosted S3-compatible storage--s3-access-key-id: Access key (uses IAM if not set)--s3-secret-access-key: Secret key (uses IAM if not set)--s3-force-path-style: Use path-style URLs (required for SeaweedFS and similar)
go build -o depot ./cmd/depotgo run ./cmd/depotgo run ./cmd/depot serve --store-path=$HOME/depot-store --verbosenix buildnix runnix developnix build .#docker-imageOnce you've built the image, you can load it into a local Docker daemon with docker load.
docker rmi ghcr.io/a-h/depot:latest || true
docker load < resultgunzip -c result | microk8s ctr image import -mkdir -p ${HOME}/depot-store
docker run --rm -v ${HOME}/depot-store:/depot-store -p 8080:8080 ghcr.io/a-h/depot:latestPush a store path to the binary cache. If authentication is enabled, use the proxy command to proxy with authentication.
nix copy --to http://localhost:8080 `nix eval github:NixOS/nixpkgs/8cd5ce828d5d1d16feff37340171a98fc3bf6526#sl --raw` --refreshPush a couple of Go modules, NPM packages, Python packages, and Nix packages.
export DEPOT_URL=http://localhost:8080
# Save and push Go modules.
go run ./cmd/depot go save rsc.io/quote@v1.5.2
go run ./cmd/depot go push $DEPOT_URL
# Save and push NPM packages.
go run ./cmd/depot npm save express lodash
go run ./cmd/depot npm push $DEPOT_URL
# Save and push Python packages.
go run ./cmd/depot python save "requests>=2.0.0" "flask==2.3.0"
go run ./cmd/depot python push $DEPOT_URL
# Push Nix packages.
go run ./cmd/depot nix push $DEPOT_URL --flake-refs nixpkgs#hello
go run ./cmd/depot nix push $DEPOT_URL --flake-refs nixpkgs#jqDownload a couple of Go modules, NPM packages, Python packages, and Nix packages from depot.
export DEPOT_URL=http://localhost:8080
export DEPOT_GO_URL=$DEPOT_URL/go
export DEPOT_NPM_URL=$DEPOT_URL/npm
export DEPOT_PYTHON_URL=$DEPOT_URL/python/simple/
export DEPOT_NIX_URL=$DEPOT_URL/nix
export DOWNLOAD_DIR=$(mktemp -d)
echo "Downloading artifacts to $DOWNLOAD_DIR"
# Download Go modules from depot.
GOPROXY=$DEPOT_GO_URL GONOSUMCHECK='*' GONOSUMDB='*' go mod download rsc.io/quote@v1.5.2
# Download NPM packages from depot.
npm pack --registry $DEPOT_NPM_URL --pack-destination $DOWNLOAD_DIR express lodash
# Download Python packages from depot.
python -m pip download --dest $DOWNLOAD_DIR --index-url $DEPOT_PYTHON_URL requests flask
# Download Nix packages from depot.
nix build \
--no-link \
--option substituters "$DEPOT_NIX_URL https://cache.nixos.org" \
nixpkgs#hello nixpkgs#jqnix store ls --store http://localhost:8080 --recursive /nix/store/4h86fqf4nl9l4dqj8sjvqfw0f9x22wpg-sl-5.05Run all tests including integration tests:
go test ./... -v -coverprofile=coverage.out -timeout 5mgo test ./storage -v -run TestS3StorageShow coverage summary:
go tool cover -func=coverage.out | grep totalWarning: this may damage the running db.
sqlite3 -header -column "file:$HOME/depot-store/depot.db?ro=1" "SELECT key from kv;"export FLAKE=github:NixOS/nixpkgs/8cd5ce828d5d1d16feff37340171a98fc3bf6526
export PKG=github:NixOS/nixpkgs/8cd5ce828d5d1d16feff37340171a98fc3bf6526#sl
# Copy flake input and source to the store.
nix flake archive --to http://localhost:8080/nix $FLAKE --refresh
# Copy the sl package of the flake, and it derivation.
nix copy --to 'http://localhost:8080/nix' $PKG --refresh
nix copy --derivation --to 'http://localhost:8080/nix' $PKG --refresh
# Copy realised paths of inputs to the derivation so that we can build it on the remote.
nix copy --to http://localhost:8080/nix $(
nix-store --realise $(
nix derivation show "$PKG" | jq -r '.[].inputDrvs | keys[]'
)
)If you're hosting Depot on Docker or Kubernetes, you may encounter SQLite disk I/O errors due to filesystem restrictions. If you see an error like this:
sqlite: step: disk I/O error
This is because SSHFS (Rancher Desktop's default), 9fs and other network filesystems do not support file locking, which SQLite requires. To resolve this, you can use a volume that supports file locking, such as a local disk or a network filesystem that supports it. Disabling WAL with the &_journal_mode=DELETE SQLite connection string can also resolve the issue.