Developer Tools Azure

BaGetter on Ubuntu 24.04 on Azure User Guide

| Product: BaGetter on Ubuntu 24.04 LTS on Azure

Overview

BaGetter is a lightweight, self-hosted server for hosting 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, and it presents a clean, browsable web gallery for searching packages and reading their metadata, versions and dependencies. Packages live on the 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 cloudimg image builds the pinned BaGetter 1.6.5 release on the supported ASP.NET Core 10 runtime, runs it as a dedicated bagetter system user under systemd, and fronts its web gallery and NuGet v3 API on port 80 with an nginx reverse proxy. The package store lives on a dedicated Azure data disk, and 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. Backed by 24/7 cloudimg support.

What is included:

  • 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 / 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 + nginx.service as systemd units, enabled and active
  • An unauthenticated /healthz endpoint for Azure Load Balancer health probes
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point; BaGetter is light on resources. NSG inbound: allow 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 cloudimg, and select Create. On Basics 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 Review + create -> Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name bagetter \
  --image <marketplace-image-urn> \
  --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

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

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

The bagetter, nginx and firstboot services active, BaGetter listening on loopback 127.0.0.1:5000 with nginx on port 80, and the dedicated 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:

sudo cat /root/bagetter-credentials.txt

This file contains BAGETTER_API_KEY (the key required to push packages), BAGETTER_SERVICE_INDEX (the /v3/index.json URL your dotnet clients point at) and 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.

The per-VM BaGetter credentials file listed as mode 0600 root:root, with the API key value redacted and the service index and gallery URLs shown

Step 6 - Confirm the health endpoint

nginx serves an unauthenticated health endpoint for load balancers and probes:

curl -s http://localhost/healthz

It returns ok. This endpoint never requires authentication, so it is safe 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 any client can restore from the feed, while pushing requires the API key. The following proves 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/Cloudimg.Smoke.Test.1.0.0.nupkg http://127.0.0.1/api/v2/package)"

It prints index: 200 then push (no key): 401. Nobody can push packages to your feed without the per-VM API key, while your builds can freely restore from the public read surface.

The BaGetter NuGet v3 service index reporting its version and resource count, a no-key push rejected with HTTP 401, and the hosted private packages returned by a search query

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 showing the package id, author, latest version, tags and description.

The BaGetter web gallery listing hosted private packages, each with its id, author, latest version, tags and description, with a search box and package-type and framework filters

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 package Readme, its dependencies, and a Versions table listing every published version with its download count and publish date.

The Contoso.Core package detail page in BaGetter showing the dotnet add package install command, the Readme, Used By, Dependencies and Versions sections, the version history table, and the Info sidebar with project, source and license links

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 a developer can quickly find the shared library they need across all your private packages.

The BaGetter gallery filtered by a search query to the packages in one namespace, showing the matching results with their versions and descriptions

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. From a machine with the .NET SDK, run the push using 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.

The BaGetter Upload page showing the step-by-step instructions and commands for pushing a package to the feed with the NuGet client

Step 12 - Restore from the feed

On any developer or build machine, add the feed as a restore source and your dotnet restore / dotnet build will 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 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, so your packages survive OS changes and the disk can be resized independently:

findmnt /var/lib/bagetter

The mount is backed by a separate Azure data disk 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. 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/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) so a restore that asks for a public package is cached on your feed.
  • Package limits: the maximum package size defaults to 8 GiB (MaxPackageSizeGiB); 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 and replace the contents of /opt/bagetter, then 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 exposing 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

cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.