Kellnr
Overview
Kellnr is an open source private registry for Rust crates: a crates.io you run yourself, for code your team does not publish to the world. It speaks the registry protocol cargo already knows. Publishing an internal crate is cargo publish, and depending on one is an ordinary dependency line pointing at your own registry.
Kellnr serves the sparse index that cargo fetches, and stores and returns the .crate files themselves. Its web interface lets you browse what has been published, manage users and read-only accounts, and issue and revoke API tokens. Crate ownership, groups and per-crate access control let one registry serve several teams. An optional caching proxy can mirror public crates so builds keep working when the public index does not.
This SecureAnyCloud image delivers Kellnr 6.8.0 on Ubuntu 24.04 as a single node, served over TLS, with an administrator password and a cargo API token generated on the first boot of your VM. It is backed by 24/7 SecureAnyCloud support.
What you get:
- Kellnr 6.8.0, the unmodified upstream release binary at
/usr/local/bin/kellnr, pinned by tag and by the SHA-256 of the release asset, run by systemd as the unprivilegedkellnruser - nginx terminating TLS on port
443with a certificate generated for your VM. Kellnr speaks plain HTTP only, and both of its credentials are bearer credentials sent on every request, so Kellnr never leaves the loopback interface - Authentication required on every registry operation. The index, crate downloads and publishing all demand a token, so a private registry is genuinely private rather than merely unadvertised
- No default credential. The upstream default password for Kellnr's
adminaccount is the string admin, and it is fixed the moment the registry database is created. This image ships no database at all, so that default never exists on a running instance - The crate store at
/var/lib/kellnr, owned by a service account with no shell and no password - The address your registry advertises to cargo resolved per VM, so
cargo publishworks from a developer machine immediately - No compiler toolchain: a registry is used from a developer machine, so
cargo,rustcandrustupare all absent from the image - An on-VM self test that publishes a crate, reads it back byte for byte and removes it again
- The
kellnrandnginxsystemd services, enabled and active
Kellnr and Rust are trademarks of their respective owners. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them. This image packages the unmodified open source software, which is distributed under the Apache License 2.0.
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region.
Standard_B2s (2 vCPU, 4 GiB RAM) is the recommended size: one small Rust binary, an embedded SQLite database and nginx sit comfortably inside it. Choose a larger size if you expect a large crate catalogue or heavy concurrent CI traffic.
Configure the network security group to allow inbound 22/tcp from your management network and 443/tcp from wherever your developers and CI runners connect. There is deliberately no port 80, because every URL this image publishes is HTTPS.
On the machine you will run cargo from, you need Rust 1.74 or newer. Authenticated registries became stable in cargo 1.74, and older versions cannot send a token to the index.
Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Kellnr Crate Registry 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).
Select Review + create, then Create. Open port 443 afterwards, as shown in the next step.
Deploy from the Azure CLI
Accept the image terms once per subscription, then deploy:
az vm image terms accept --urn sac:kellnr-ubuntu-24-04:default:latest
az group create --name kellnr-rg --location eastus
az vm create \
--resource-group kellnr-rg \
--name kellnr-vm \
--image sac1647283583153:kellnr-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
az vm open-port --resource-group kellnr-rg --name kellnr-vm --port 443 --priority 1010Connect to your VM
Connect over SSH:
ssh azureuser@<vm-public-ip>The login user is azureuser unless you chose a different administrator name when you created the VM.
Confirm the registry is running
Check that both services are active:
systemctl is-active kellnr nginxBoth must report active. The kellnr unit does not start until first boot has finished generating this VM's credentials. nginx does not start until first boot has created its certificate. If you have only just created the VM, wait a minute and run the command again.
Check the version and the liveness endpoint:
/usr/local/bin/kellnr --version curl -s http://127.0.0.1:8000/api/v1/health; echoThat prints kellnr 6.8.0 and then OK. The health endpoint is deliberately the one route outside the authentication layer, so a load balancer can probe it without a credential. It reveals nothing except that the process is alive.
Check what the network can reach
Run the port check:
sudo /usr/local/sbin/kellnr-port-check.shThe script prints the exact set of TCP ports reachable from outside the VM and fails if the set is anything other than 22 and 443. Kellnr itself listens on 127.0.0.1:8000 and is proven unreachable on the VM's external address. Nothing listens on port 80.
To see the raw listening sockets:
sudo ss -Hltn | awk '{print $4}' | sort -uRetrieve the first-boot credentials
Every credential this registry uses was generated on this VM's first boot and exists on no other instance. The credentials are written to a file that only root can read.
This command shows the file with the two secrets masked, so you can see its shape before you read the real values:
sudo sed -E 's/^(KELLNR_ADMIN_PASSWORD=|KELLNR_API_TOKEN=).*/\1<redacted>/' \
/root/kellnr-credentials.txtThe file contains these fields:
- KELLNR_URL: the web interface for this VM
- KELLNR_HOST: this VM's customer-facing address, resolved at first boot
- KELLNR_ADMIN_USER: always admin
- KELLNR_ADMIN_PASSWORD: the web interface password for this VM
- KELLNR_API_TOKEN: the cargo API token for this VM
- KELLNR_REGISTRY_INDEX: the index URL to put in ~/.cargo/config.toml
- KELLNR_TLS_CERT: this VM's certificate, for clients that verify it
Read the real values when you need them:
sudo grep -E '^KELLNR_(URL|HOST|ADMIN_USER|REGISTRY_INDEX)=' /root/kellnr-credentials.txtThe upstream default password for Kellnr's admin account is the string admin, and the credential is decided once, at the instant the registry database is created. This image ships no database, so that default never reaches a running instance. The next step proves it rather than asserting it.
Confirm the registry refuses everything but your credential
Run the authentication check:
sudo /usr/local/sbin/kellnr-verify-auth.sh /root/kellnr-credentials.txtThe script runs the whole credential gate against the real TLS front door. It prints KELLNR_AUTH_OK only if every one of these holds:
- the documented default
admin/adminis refused with401 - a blank password is refused with
400, which is Kellnr's own validation answering before any credential check - five well-known weak passwords are refused with
401 - this VM's password is accepted, reports
is_admin, and hands out a session the web API then honours - the registry index and the publish endpoint both refuse a missing token and a wrong token with
401 - the index advertises
https://at this VM's own address withauth-requiredset - kellnr's own port does not answer on the VM's external address, and nothing answers on port 80
- TLS verification refuses an address the certificate does not name
Each of those checks is itself tested against a known-bad input, so a check that could never fail is caught. Run the self test:
sudo /usr/local/sbin/kellnr-gate-selftest.sh /root/kellnr-credentials.txtSign in to the web interface
Open https://<KELLNR_HOST>/ in a browser. The image ships a certificate generated for your VM's own address. Because it is self-signed, your browser warns once. Step 14 shows how to install your own certificate.
Sign in as admin with the password from Step 6.
The home page is the registry dashboard: how many crates and versions you hold, how many downloads they have served, and what was published most recently.
Point cargo at your registry
On the machine you build from, add the registry to ~/.cargo/config.toml. The index URL is the KELLNR_REGISTRY_INDEX value from Step 6. It is a sparse+https:// URL, which is the modern protocol cargo uses by default:
[registries.kellnr] index = "sparse+https://<KELLNR_HOST>/api/v1/crates/"Put the token in ~/.cargo/credentials.toml, which cargo expects to be readable only by you:
[registries.kellnr] token = "<KELLNR_API_TOKEN>"Cargo does not use the address you typed to download crates. It reads the registry's index configuration and follows the URLs published there. This image resolves your VM's own address at first boot and publishes it over HTTPS, so cargo works from a developer machine straight away.
To see exactly what cargo will be told:
sudo bash -c 'T=$(grep "^KELLNR_API_TOKEN=" /root/kellnr-credentials.txt | cut -d= -f2-); H=$(grep "^KELLNR_HOST=" /root/kellnr-credentials.txt | cut -d= -f2-); curl -s --cacert /etc/ssl/kellnr/kellnr.crt --resolve "$H:443:127.0.0.1" -H "Authorization: $T" "https://$H/api/v1/crates/config.json"; echo'When auth-required is true, cargo sends your token when it fetches the index and downloads crates.
The --resolve flag is there because an Azure VM cannot dial its own public IP. The Standard Load Balancer does not hairpin, so a request from the VM to its own public address times out. --resolve keeps your real address in the URL and in the TLS check while sending the connection to loopback. From your laptop, use the URL as is.
Publish a crate and depend on it
From your own crate's directory, publish to the registry. Run this on the machine you build from, not on the registry VM, which deliberately carries no Rust toolchain:
cargo publish --registry kellnrThen depend on it from another project by naming the registry:
[dependencies] my-internal-crate = { version = "0.1.0", registry = "kellnr" }The crate page in the web interface shows the version, the licence, the owners and the exact dependency line to paste:
You do not need a Rust toolchain on the registry VM to check that publishing works end to end. The image ships a self test that does the complete round trip with your own token through the real TLS front door, then puts the registry back exactly as it found it:
sudo /usr/local/sbin/kellnr-publish-probe.sh /root/kellnr-credentials.txtThe probe publishes a crate using cargo's own wire format and requires the sparse index to list it with a matching checksum. It downloads the crate back and requires the bytes to be identical, rather than merely a 200. It then deletes the crate and requires the registry to be empty again on disk and in the database.
Along the way, the probe requires the emptiness check to fail while the crate is present, because a check that could not see a crate that is genuinely there would be worthless.
Confirm nothing was left behind:
sudo /usr/local/sbin/kellnr-registry-empty.sh --with-db /root/kellnr-credentials.txtIssue tokens for CI
The token in your credentials file belongs to the admin account. Give each CI pipeline its own token instead, so you can revoke one without disturbing anything else.
In the web interface, choose Settings, then Auth. Tokens. Name the token and select Create Token. Copy the value immediately, because it is shown once.
For a build agent, put the token in the environment rather than in a file. Set these on the CI runner, not on the registry VM:
export CARGO_REGISTRIES_KELLNR_TOKEN="the token you just created"
export CARGO_REGISTRIES_KELLNR_INDEX="sparse+https://your-registry-address/api/v1/crates/"Under Settings, User Management lets you add users and mark accounts read-only, so they can consume crates but not publish. Group Management grants several people access to the same crates at once.
Mirror crates.io (optional)
Kellnr can cache public crates alongside your private ones, so a build keeps working when the public index is slow or unreachable. The proxy is off by default in this image. A registry that silently reaches out to the internet and mirrors whatever is asked of it is not what most people want on first boot, and the cache grows without bound.
To turn it on, edit /etc/kellnr/kellnr.toml and set enabled = true under [proxy]:
sudo sed -n '/^\[proxy\]/,/^$/p' /etc/kellnr/kellnr.tomlAfter you edit the file, apply the change with sudo systemctl restart kellnr. Allow for the extra disk the cache will use.
Back up your registry
The whole registry is one directory: the SQLite database and every .crate file live under /var/lib/kellnr. This is deliberate: a backup is a copy of one path, and a restore cannot leave metadata describing crates that are not there.
sudo tar -czf /tmp/kellnr-backup.tar.gz -C /var/lib kellnr && sudo ls -lh /tmp/kellnr-backup.tar.gzCopy the archive off the VM with scp and keep it somewhere durable. Your /etc/kellnr/kellnr.env file belongs with it. It carries the administrator password and API token, and Kellnr fixes the administrator credential when the database is created, so a restored database with no matching environment file cannot be signed into. Back up both together.
sudo rm -f /tmp/kellnr-backup.tar.gzUse your own certificate and domain
The image generates a self-signed certificate for your VM's IP address on first boot. For a registry your whole team will use, point a DNS name at the VM and install a certificate for it.
Put your certificate chain at /etc/ssl/kellnr/kellnr.crt and your private key at /etc/ssl/kellnr/kellnr.key (0600 root:root).
Then set KELLNR_ORIGIN__HOSTNAME in /etc/kellnr/kellnr.env to the hostname Kellnr should advertise to cargo, and restart both services. If you obtain a certificate with certbot, use its --nginx installer or copy the issued files into the paths above.
The hostname matters as much as the certificate. It is the value cargo is handed in the index configuration, so changing the certificate without changing the hostname leaves every client being told to fetch from the old address.
Keep the image patched
Unattended security upgrades are enabled. To patch now:
sudo apt-get update && sudo apt-get upgrade -yKellnr itself is a pinned upstream release rather than an Ubuntu package, so apt never updates it and never warns you that it is old.
To move to a newer release, take the Step 13 backup first. Then download the kellnr-x86_64-unknown-linux-gnu.zip asset from the Kellnr releases page and verify its SHA-256 against the value GitHub publishes for that asset. Stop the service, replace /usr/local/bin/kellnr, and start the service again. Your data directory is untouched, and Kellnr migrates its own database on the next start.
Rotate the administrator password
Run the rotation helper, passing the new password on standard input:
printf '%s' '<new-password>' | sudo /usr/local/sbin/kellnr-set-admin-password.sh /root/kellnr-credentials.txtReplace <new-password> with a password of at least 12 characters. The helper reads the password from standard input rather than the command line, so it does not land in your shell history or the process table.
Kellnr's own endpoint accepts anything that is not empty. This wrapper refuses blank, short, all-digit and well-known weak values, and refuses the password you are already using. It then proves the rotation took by requiring the new password to be accepted and the old one to be refused before it updates your credentials file.
To see the helper refuse a weak value without changing anything:
if printf '%s' 'short' | sudo /usr/local/sbin/kellnr-set-admin-password.sh /root/kellnr-credentials.txt >/dev/null 2>&1; then echo "UNEXPECTED: a 5-character password was accepted"; exit 1; else echo "a weak password was REJECTED and nothing changed"; fiBecause the administrator credential is fixed when the registry database is created, this is the only way to change it. Re-provisioning the VM does not rotate it. First boot deliberately preserves the credentials of a registry that already exists, because creating new ones would hand you a credentials file your own registry has never heard of, with your crates still inside it.
Troubleshooting
kellnr.service is inactive and systemctl status says the condition failed. First boot has not finished. It writes /var/lib/sac/data/kellnr.bootstrap-ready as its very last action, and the service is gated on that file, so that the upstream default password can never be written into a database. Check journalctl -u kellnr-firstboot.service.
nginx will not start. It is gated on /etc/ssl/kellnr/kellnr.crt for the same reason. If you replaced the certificate, check that the file exists and that sudo nginx -t passes.
cargo says the registry index is unauthorized. Either your token is missing or your cargo is older than 1.74. Authenticated registries became stable in cargo 1.74, and earlier versions cannot send a token to the index at all.
cargo downloads fail with a connection error to 127.0.0.1. Something has reset KELLNR_ORIGIN__HOSTNAME to the upstream default. Check /etc/kellnr/kellnr.env, then re-read the index configuration as in Step 9. The dl URL it prints is what cargo actually uses.
A crate will not publish because the version already exists. Registries do not allow a version to be replaced. Publish a new version, or delete the existing one from its crate page in the web interface if it was a mistake.
You have lost the credentials file. Kellnr fixes the administrator credential when the database is created and there is no way to re-apply a new one. Restore /etc/kellnr/kellnr.env from backup. Without a backup, the only route is a fresh registry, which means losing your published crates. This is why Step 13 asks you to back up both together.
Support
SecureAnyCloud provides 24/7 support for this image: deployment, upgrades, TLS certificates and custom domains, cargo and CI integration, user and token management, crates.io mirroring and scaling. Contact support through the Azure Marketplace listing or at secureanycloud.com.
Deploy on Azure
Launch Kellnr Crate Registry 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