Pinning - Mic92/niks3 GitHub Wiki

Pinning Store Paths

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.

Overview

When you pin a store path:

  1. The pin is stored in the database, protecting the closure from GC
  2. The store path is written to S3 at pins/<name> for easy retrieval
  3. The pin can be updated by pushing again with the same name

Creating Pins

During Push

Use the --pin flag when pushing a store path:

niks3 push --pin myapp /nix/store/abc123...-myapp-1.0

This uploads the closure and creates a pin named myapp pointing to it.

Note: The --pin flag requires exactly one store path.

For Existing Store Paths

If the store path is already in the cache, use pins create:

niks3 pins create myapp /nix/store/abc123...-myapp-1.0

This creates a pin without re-uploading the closure.

Retrieving Pinned Paths

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)

Deploying Systems with Pins

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" switch

This 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

Managing Pins

List All Pins

niks3 pins list

Output:

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-only

Output:

myapp
myservice

Delete a Pin

niks3 pins delete myapp

Note: Deleting a pin removes the GC protection. The closure may be garbage collected in future GC runs.

Garbage Collection Protection

Pinned closures are protected from garbage collection:

  • The niks3 gc command excludes pinned closures from deletion
  • All objects in a pinned closure's dependency tree are preserved
  • Deleting a pin removes this protection

Use Cases

Deployment References

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.3

Rollback Support

Keep 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.3

Development Environments

Pin development tool closures:

niks3 push --pin devshell $(nix build .#devShells.x86_64-linux.default --print-out-paths)

Pin Name Requirements

  • Alphanumeric characters, dashes (-), underscores (_), and dots (.)
  • Maximum 256 characters
  • Examples: myapp, myapp-v1.2.3, prod.myservice, dev_environment

Example: CI/CD Integration

GitHub Actions

- name: Push and pin release
  run: |
    niks3 push --pin "release-${{ github.ref_name }}" result

GitLab CI

deploy:
  script:
    - niks3 push --pin "release-${CI_COMMIT_TAG}" result
⚠️ **GitHub.com Fallback** ⚠️