Developer Tools AWS

BaGetter Private NuGet Server on AWS User Guide

| Product: BaGetter on AWS

Overview

This image runs BaGetter, a lightweight self-hosted NuGet package feed server, as a hardened systemd service on Ubuntu 24.04. BaGetter hosts your organisation's private .NET packages, exposes the standard NuGet v3 service index that any NuGet client can restore from, and provides a browsable web gallery for discovering what is published.

BaGetter itself binds to the loopback interface only (127.0.0.1:5000) and is fronted by an nginx TLS reverse proxy on port 443, so the application is never directly reachable from the network and all traffic to the gallery and the NuGet API is encrypted. Package metadata is stored in SQLite and package content on the filesystem, both placed under /var/lib/bagetter on a dedicated EBS data volume that is separate from the operating system disk, so your published packages are independently resizable and snapshottable.

The image is secure by default. A stock NuGet server can accept anonymous uploads if no push key is configured; this image never ships that way. A unique push API key is generated on the first boot of every deployed instance and written to /root/bagetter-credentials.txt with mode 0600, so two instances launched from the same AMI never share a credential and no usable key is baked into the image. A push with a missing or wrong API key is rejected, while read access - the service index, package restore and the gallery - stays open by design, which is the standard private feed posture. A unique self-signed TLS certificate is also generated per instance on first boot.

BaGetter 1.6.5 is built from source and run framework dependent on the supported ASP.NET Core 10 runtime.

Prerequisites

Before you deploy this image you need:

  • An Amazon Web Services account where you can launch EC2 instances
  • IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
  • An EC2 key pair in the target Region for SSH access to the instance
  • A VPC and subnet in the target Region, with a security group allowing inbound port 22 from your management network and inbound port 443 from the developer and CI networks that will push to and restore from the feed
  • The AWS CLI (version 2) installed locally if you plan to deploy from the command line
  • A machine with the .NET SDK installed to push and restore packages

Launching the instance

You can subscribe and launch from the AWS Marketplace console, or launch from the AWS CLI once you have subscribed. Replace the placeholders with your own values.

aws ec2 run-instances \
  --image-id <ami-id> \
  --instance-type m5.large \
  --key-name <key-name> \
  --subnet-id <subnet-id> \
  --security-group-ids <security-group-id> \
  --metadata-options "HttpTokens=required,HttpEndpoint=enabled" \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=bagetter}]'

The recommended security group opens only SSH (22) and the HTTPS front door (443). Scope both to your own developer, CI and administrator networks - do not leave them open to the whole internet.

Connecting to your instance

Connect over SSH as the default login user for your operating system variant. The login user depends on the variant you launched:

OS variant SSH login user
Ubuntu 24.04 ubuntu
ssh -i /path/to/your-key.pem ubuntu@<public-ip>

Retrieving your push API key

The per instance push API key, the feed URL and the NuGet service index URL are written to a root only file on first boot. Read them over SSH:

sudo cat /root/bagetter-credentials.txt

You will see the feed URL, the service index URL and the API key, for example BAGETTER_API_KEY=.... Keep the API key secret - anyone who has it can publish or overwrite packages on your feed.

Browsing the web gallery

Open the feed URL from the credentials file in a browser - https://<public-ip>/. Because the certificate is self-signed by default, your browser will warn until you install your own certificate (covered below). The gallery lists every published package with its latest version, owner and description, and has a search box across the top.

BaGetter web gallery listing published packages

Click any package to open its detail page, which shows the install commands for the .NET CLI, PackageReference, Paket and the Package Manager console, the full version history and the package metadata.

BaGetter package detail page with install commands and version history

Verifying the feed on the instance

You can confirm the services are healthy and the push gate is enforced directly on the instance. This reads the API key from the credentials file, checks the service index answers over TLS, and proves that an anonymous push is refused with HTTP 401:

KEY=$(sudo grep '^BAGETTER_API_KEY=' /root/bagetter-credentials.txt | cut -d= -f2-)
systemctl is-active bagetter.service nginx.service
curl -ks -o /dev/null -w 'healthz: %{http_code}\n' https://127.0.0.1/healthz
curl -ks -o /dev/null -w 'v3 service index: %{http_code}\n' https://127.0.0.1/v3/index.json
curl -ks -o /dev/null -w 'anonymous push (expect 401): %{http_code}\n' \
  -X PUT -F 'package=@/opt/bagetter/testdata/Cloudimg.Smoke.Test.1.0.0.nupkg' \
  https://127.0.0.1/api/v2/package
echo "api key length: ${#KEY} chars"

Both services report active, the health endpoint and the service index return 200, and the anonymous push returns 401 - only holders of the API key can publish.

Adding the feed to a NuGet client

On a developer or CI machine, add the service index as a package source. Because the default certificate is self-signed, either install your own certificate on the instance first, trust the self-signed certificate on the client, or terminate TLS at a load balancer with a real certificate.

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

Push a package with your API key:

dotnet nuget push YourPackage.1.0.0.nupkg --source https://<public-ip>/v3/index.json --api-key <your-token>

Restore from the feed by referencing it in a nuget.config alongside your solution:

dotnet restore --source https://<public-ip>/v3/index.json

Read access to the service index, package restore and the gallery is public on the network the security group allows in; only pushing requires the API key.

Rotating the API key

The active push API key lives in the per instance environment file. To rotate it, replace the value and restart BaGetter:

sudo sed -i "s/^ApiKey=.*/ApiKey=$(openssl rand -hex 32)/" /var/lib/bagetter/bagetter.env
sudo systemctl restart bagetter.service

Update /root/bagetter-credentials.txt and your clients with the new value.

Replacing the TLS certificate

A unique self-signed certificate is generated per instance at /etc/nginx/ssl/bagetter.crt and /etc/nginx/ssl/bagetter.key. Replace both files with your own certificate and private key and reload nginx, or front the instance with a load balancer that terminates TLS using an ACM certificate:

# Install the certificate you obtained for <your-domain>:
sudo cp your-fullchain.pem /etc/nginx/ssl/bagetter.crt
sudo cp your-private.key   /etc/nginx/ssl/bagetter.key
sudo nginx -t && sudo systemctl reload nginx.service

Storage layout

Package metadata (the SQLite database bagetter.db) and package content (the packages/ directory) live under /var/lib/bagetter on a dedicated EBS volume, separate from the operating system disk, so you can resize or snapshot your package store independently:

df -hT /var/lib/bagetter

Enabling upstream mirroring

BaGetter can act as a read-through cache of an upstream feed such as nuget.org, so restores continue to work when the upstream is unavailable. This is configured through BaGetter's Upstream settings; contact cloudimg support and we will help you enable and tune mirroring for your environment.

Backups

Back up the SQLite metadata database and the package store together so the feed can be restored consistently:

sudo tar -czf /tmp/bagetter-backup.tgz -C /var/lib/bagetter bagetter.db packages

Copy the archive off the instance, or snapshot the dedicated EBS volume from the AWS console or CLI.

Services and troubleshooting

BaGetter runs as bagetter.service on loopback port 5000; nginx fronts it on 443. Useful commands:

systemctl status bagetter.service --no-pager
sudo journalctl -u bagetter.service --no-pager | tail -50

If the gallery does not load, confirm both services are active and that your security group allows inbound 443 from your network.

Support

cloudimg provides 24/7 technical support for this product by email and live chat. Our engineers help with deployment, connecting NuGet clients and CI pipelines, push API key management, TLS termination, upstream mirroring, backups and scaling. Email support@cloudimg.co.uk or use live chat, and we will work with you until your issue is resolved.