Deployment guide

BaGetter

Overview

BaGetter is a lightweight, self-hosted server for your own private NuGet package feed. It implements the standard NuGet v3 protocol, so any .NET toolchain can restore from it and push to it with the tools developers already use. A clean, browsable web gallery lets you search packages and read their metadata, versions and dependencies.

Packages live on a server you control, with metadata in a self-contained SQLite database and package files on local disk. BaGetter can also mirror packages from the public nuget.org feed on demand.

The SecureAnyCloud image builds the pinned BaGetter 1.6.5 release on the supported ASP.NET Core 10 runtime. It runs as a dedicated bagetter system user under systemd, with an nginx reverse proxy on port 80 in front of the web gallery and NuGet v3 API. The package store lives on a dedicated Azure data disk.

A unique push API key is generated on the first boot of every VM, so no shared credential ships in the image. Browsing and restore are open on your network, while pushing is gated by the per-VM key.

What you get:

  • BaGetter 1.6.5 running as the bagetter systemd service on the ASP.NET Core 10 runtime
  • The NuGet v3 service index at /v3/index.json, so any dotnet or nuget client can restore and push
  • A browsable web gallery on :80, fronted by nginx, for searching packages and reading metadata
  • Secure-by-default push: a unique API key is generated on first boot and required for every push
  • A dedicated Azure data disk at /var/lib/bagetter holding the SQLite metadata store and package files
  • bagetter.service and nginx.service as systemd units, enabled and active
  • An unauthenticated /healthz endpoint for Azure Load Balancer health probes
  • 24/7 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.

Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting size, because BaGetter is light on resources.

Configure the network security group (NSG) to allow inbound 22/tcp from your management network, and 80/tcp from the networks that run your builds and developer machines so they can browse, restore and push. BaGetter serves plain HTTP on port 80. For use beyond a trusted network, terminate TLS in front of it with your own domain (see Maintenance).

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure portal, choose Create a resource, search the Marketplace for BaGetter by SecureAnyCloud, and select Create.

On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80).

Review the dedicated data disk on the Disks tab, then select Review + create and Create.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:bagetter-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name bagetter \
  --image sac1647283583153:bagetter-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name bagetter --port 80 --priority 1010

Step 3 - Connect to your VM

Connect over SSH as azureuser:

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

Check that both services are active:

systemctl is-active bagetter.service nginx.service

Both report active. BaGetter serves its web gallery and NuGet v3 API on the loopback connector 127.0.0.1:5000, and nginx fronts it on port 80. BaGetter's SQLite metadata store and package files live on the dedicated Azure data disk mounted at /var/lib/bagetter.

Step 5 - Retrieve your push API key

BaGetter gates package pushes behind an API key. A unique key is generated on the first boot of your VM and written to a root-only file. Read it with sudo:

sudo cat /root/bagetter-credentials.txt

The file contains three values:

  • BAGETTER_API_KEY, the key required to push packages
  • BAGETTER_SERVICE_INDEX, the /v3/index.json URL that your dotnet clients point at
  • BAGETTER_URL, the web gallery

The key is stored only in the per-VM file /var/lib/bagetter/bagetter.env (mode 0600), so no shared key ships in the image. Store the key somewhere safe.

Step 6 - Confirm the health endpoint

Query the unauthenticated health endpoint that nginx serves for load balancers and probes:

curl -s http://localhost/healthz

It returns ok. This endpoint never requires authentication, so you can use it for an Azure Load Balancer health probe.

Step 7 - Confirm the feed serves and push is protected

The NuGet v3 service index is publicly readable so that any client can restore from the feed, while pushing requires the API key. The following commands check both: the service index returns 200, and a push attempt with no key is rejected with 401.

echo "index: $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/v3/index.json)"
echo "push (no key): $(curl -s -o /dev/null -w '%{http_code}' -X PUT -F package=@/opt/bagetter/testdata/Sac.Smoke.Test.1.0.0.nupkg http://127.0.0.1/api/v2/package)"

The output is index: 200 followed by push (no key): 401. Nobody can push packages to your feed without the per-VM API key, while your builds can restore freely from the open read surface.

Step 8 - Browse the web gallery

Browse to http://<vm-public-ip>/. BaGetter opens on the Packages gallery, a searchable list of every package hosted on your feed. Each row shows the package ID, author, latest version, tags and description.

Step 9 - View a package

Click any package to open its detail page. BaGetter shows a ready-to-copy install command for the .NET CLI, PackageReference, Paket CLI and Package Manager. The page also shows the package Readme, its dependencies, and a Versions table that lists every published version with its download count and publish date.

Step 10 - Search the feed

Use the search box, or append ?q= to the URL, to filter the gallery. Searching narrows the list to the matching packages, so developers can quickly find the shared library they need across all your private packages.

Step 11 - Push a package from your dev machine

Open the Upload page in the gallery for the exact push commands, or push directly with the standard .NET tooling. On a machine with the .NET SDK, run the push with the API key from Step 5 and the service index URL:

dotnet nuget push YourPackage.1.0.0.nupkg \
  --source http://<vm-public-ip>/v3/index.json \
  --api-key <BAGETTER_API_KEY>

The package is uploaded to your feed and appears immediately in the web gallery. Pushes without the key, or with the wrong key, are rejected with 401.

Step 12 - Restore from the feed

On any developer or build machine, add the feed as a restore source. Then dotnet restore and dotnet build pull private packages from it. Reading and restoring do not require the API key:

dotnet nuget add source http://<vm-public-ip>/v3/index.json --name bagetter dotnet restore

Point your CI pipelines at the same source, so that builds resolve your private packages from BaGetter alongside their public dependencies.

Step 13 - Confirm data lives on the dedicated disk

BaGetter's SQLite metadata store and package files are kept under /var/lib/bagetter on the dedicated Azure data disk. Your packages survive OS changes, and you can resize the disk independently. Confirm the mount:

findmnt /var/lib/bagetter

A separate Azure data disk backs the mount. It is captured into the image and re-provisioned on every VM.

Maintenance

  • API key. The push key is set on first boot and stored in the per-VM file /var/lib/bagetter/bagetter.env (ApiKey=..., mode 0600). To rotate it, edit that file as root and run sudo systemctl restart bagetter. Then update your CI and developer machines with the new key.
  • Protecting read access. By default, browsing and restore are open on your network. To require credentials for reading too, add an Authentication:Credentials list (username and password) to a BaGetter config override and restart the service. See the BaGetter documentation.
  • Mirroring nuget.org. BaGetter can serve a locally cached copy of public packages. Enable the upstream mirror in a config override (Mirror:Enabled=true), and a restore that asks for a public package is then cached on your feed.
  • Package limits. The maximum package size defaults to 8 GiB (MaxPackageSizeGiB), and nginx is configured to accept large uploads.
  • Storage. All BaGetter state lives under /var/lib/bagetter on the data disk: the SQLite bagetter.db and the packages/ file store. Back up that volume to protect your packages.
  • Upgrades. BaGetter is built from a pinned source tag and published under /opt/bagetter, running on the apt-managed ASP.NET Core 10 runtime. To upgrade, rebuild a newer BaGetter release, replace the contents of /opt/bagetter, and run sudo systemctl restart bagetter. Your store under /var/lib/bagetter is preserved.
  • TLS. BaGetter serves plain HTTP on port 80. Front it with TLS, for example certbot with your own domain, before you expose it beyond a trusted network.
  • Security patches. unattended-upgrades remains enabled, so the OS and the ASP.NET Core runtime continue to receive security updates automatically.

Support

SecureAnyCloud provides 24/7 expert support for this image. Contact sales@secureanycloud.com.

Deploy on Azure

Launch BaGetter on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure