Skip to content

Choosing package storage

NuGetKeep stores package blobs (.nupkg, .snupkg, extracted PDBs) in a storage backend. Metadata is separate — it always lives in the database, whatever storage you pick. The data-protection keys, the SQLite file (when used), and license.jwt also stay on the /data volume regardless of the blob backend.

Out of the box blobs live under /data/packages inside the data volume. Nothing to configure, and backup is copying one volume. For a single server with a persistent volume — most teams — this is the right choice.

Choose object storage instead when:

  • you deploy on Kubernetes or a platform where persistent volumes are unwanted or awkward,
  • your platform team standardizes on S3/Azure Blob for durable blobs,
  • you want storage that scales and replicates independently of the host, or
  • a future multi-instance/HA topology is on your roadmap (object storage + PostgreSQL is the shared-state pair it needs).

One provider covers real AWS S3 and anything speaking the S3 API (MinIO, Cloudflare R2, Ceph, …) — a custom endpoint URL selects the latter. Three ways to run it, all built into the installer:

Terminal window
curl -fsSL https://nugetkeep.com/install | bash -s -- --storage minio

The generated compose stack gains a pinned minio/minio service with its own nugetkeep-miniodata volume and a strong generated root password (stored in the stack’s .env, mode 0600, reused on re-runs). The app waits for MinIO’s healthcheck before starting and creates the nugetkeep bucket itself on first boot. No MinIO ports are published to the host; admin tasks go through docker compose exec minio mc ….

Terminal window
curl -fsSL https://nugetkeep.com/install | bash -s -- --storage s3 \
--s3-endpoint https://minio.internal:9000 \
--s3-bucket nugetkeep --s3-access-key # secret via S3_SECRET_KEY on the bash side of the pipe (see the Azure example)

Omit --s3-endpoint for real AWS S3 (set --s3-region instead). Omit the access/secret key on AWS to use the instance’s IAM role / default credential chain — the right choice on EC2/EKS. The installer pre-flights the bucket (a real HeadBucket) before writing anything: a missing bucket only warns (NuGetKeep creates it at startup when the credentials allow), rejected credentials or an unreachable endpoint fail with specific guidance (--skip-storage-check bypasses it).

An S3 endpoint on the same machine as Docker is reached via host.docker.internal, not localhost (inside the container, localhost is the container).

Terminal window
# The env var goes on the bash side of the pipe — `VAR=… curl … | bash` would
# set it for curl, not for the installer.
curl -fsSL https://nugetkeep.com/install |
AZURE_CONNECTION='DefaultEndpointsProtocol=https;AccountName=…;AccountKey=…;EndpointSuffix=core.windows.net' \
bash -s -- --storage azure-blob --azure-container nugetkeep

The container is created at startup when absent (or pre-create it and grant only data access). Prefer the AZURE_CONNECTION env var over the flag — connection strings carry the account key.

Outside the installer, the same settings drive everything (e.g. Kubernetes):

Terminal window
NUGETKEEP_STORAGE_PROVIDER=S3 # or AzureBlob
NUGETKEEP_S3_ENDPOINT= # empty = real AWS S3
NUGETKEEP_S3_BUCKET=nugetkeep
NUGETKEEP_S3_REGION=us-east-1
NUGETKEEP_S3_ACCESS_KEY= # empty = IAM role / default chain
NUGETKEEP_S3_SECRET_KEY=
NUGETKEEP_AZURE_BLOB_CONNECTION= # AzureBlob provider
NUGETKEEP_AZURE_BLOB_CONTAINER=nugetkeep

The blob layout in the bucket mirrors the filesystem layout exactly ({feed}/{id}/{version}/{id}.{version}.nupkg, symbols under {feed}/symbols/), so a bucket is browseable the same way a volume is.

ModeWhat to back up
FilesystemThe nugetkeep-data volume. One volume, one backup — database, packages, and keys together.
Bundled MinIOThe nugetkeep-data volume (database/keys) and the nugetkeep-miniodata volume (blobs).
External S3 / Azure BlobThe nugetkeep-data volume, plus the bucket/container versioning & replication your provider already offers.

There is no automatic blob migration between storage providers (in any direction). Switching means the new backend starts empty — already-pushed packages keep their metadata (they still appear in search) but their downloads 404 until re-pushed or re-mirrored. The installer warns loudly if you ask it to switch an existing stack; the old volume/bucket is left untouched. If you anticipate needing object storage, start on it.