Pinning - Mic92/niks3 GitHub Wiki
Note: This feature is currently a pull request (#155) and not yet merged.
Pins are named, persistent references to store paths that protect closures from garbage collection. They work similarly to Cachix pins.
When you pin a store path:
- The pin is stored in the database, protecting the closure from GC
- The store path is written to S3 at
pins/<name>for easy retrieval - The pin can be updated by pushing again with the same name
Use the --pin flag when pushing a store path:
niks3 push --pin myapp /nix/store/abc123...-myapp-1.0This uploads the closure and creates a pin named myapp pointing to it.
Note: The --pin flag requires exactly one store path.
If the store path is already in the cache, use pins create:
niks3 pins create myapp /nix/store/abc123...-myapp-1.0This creates a pin without re-uploading the closure.
Pins are stored in S3 and can be retrieved with curl:
# Get the store path
curl https://cache.example.com/pins/myapp
# Returns: /nix/store/abc123...-myapp-1.0
# Directly realize the pinned path
nix-store -r $(curl -s https://cache.example.com/pins/myapp)This pattern is powerful for deploying NixOS systems. Push and pin your system closure from CI:
# In CI: build and pin the system
niks3 push --pin myserver $(nix build .#nixosConfigurations.myserver.config.system.build.toplevel --print-out-paths)Then on the target server, deploy with:
# Fetch the pinned system path
system=$(curl -s https://cache.example.com/pins/myserver)
# Realize the closure with a GC root to prevent collection during switch
nix-store -r "$system" --add-root /run/next-system --indirect
# Update the system profile (enables rollback via boot menu)
nix-env -p /nix/var/nix/profiles/system --set "$system"
# Activate the new system
"$system/bin/switch-to-configuration" switchThis approach:
- Requires no Nix evaluation on the target (fast deploys)
- Works with minimal tooling (just curl, nix-store, nix-env)
- Preserves rollback capability through the system profile
niks3 pins listOutput:
NAME NARINFO KEY UPDATED AT
---- ----------- ----------
myapp abc123def456.narinfo 2024-12-18T10:30:00Z
myservice xyz789uvw012.narinfo 2024-12-17T15:45:00Z
For scripting, use --names-only:
niks3 pins list --names-onlyOutput:
myapp
myservice
niks3 pins delete myappNote: Deleting a pin removes the GC protection. The closure may be garbage collected in future GC runs.
Pinned closures are protected from garbage collection:
- The
niks3 gccommand excludes pinned closures from deletion - All objects in a pinned closure's dependency tree are preserved
- Deleting a pin removes this protection
Pin your production deployments to ensure they're always available:
# In your CI/CD pipeline
niks3 push --pin production-v1.2.3 /nix/store/...-myapp-1.2.3Keep previous versions pinned for quick rollbacks:
niks3 pins create production-v1.2.2 /nix/store/...-myapp-1.2.2
niks3 pins create production-v1.2.3 /nix/store/...-myapp-1.2.3Pin development tool closures:
niks3 push --pin devshell $(nix build .#devShells.x86_64-linux.default --print-out-paths)- Alphanumeric characters, dashes (
-), underscores (_), and dots (.) - Maximum 256 characters
- Examples:
myapp,myapp-v1.2.3,prod.myservice,dev_environment
- name: Push and pin release
run: |
niks3 push --pin "release-${{ github.ref_name }}" resultdeploy:
script:
- niks3 push --pin "release-${CI_COMMIT_TAG}" result