Artifact Keeper on Ubuntu 24.04 on Azure User Guide
Overview
Artifact Keeper is an open source universal artifact registry. Rather than running a separate server for every language your teams build in, it speaks the native protocols of many package ecosystems from a single endpoint — Maven, npm, PyPI, container images, Cargo, Go, Helm, deb and rpm — so everything is published to and pulled from one place, with one set of credentials and one audit trail.
The cloudimg image delivers Artifact Keeper 1.10.0 on Ubuntu 24.04 as a single node, served over TLS, with an administrator login generated on the first boot of your VM. Backed by 24/7 cloudimg support.
What is included:
- Artifact Keeper 1.10.0, the official upstream container images pinned by digest, run by systemd. The units run with
--pull=never, so the instance can never silently fetch a different build of the same version - nginx terminating TLS on port
443with a certificate generated for your VM. The registry's own services never leave the loopback interface, and there is no plaintext port at all - PostgreSQL 16, native from the Ubuntu archive so it is patched by the same updates as the rest of the VM, with no TCP listener at all — the backend reaches it over a Unix socket, so port
5432is absent from every interface - Authentication required on every request. Anonymous access is switched off explicitly, so an unauthenticated caller is refused
401rather than being shown a readable registry - The setup lock left armed. The first-boot administrator password is a bootstrap credential: until you change it the registry refuses every API route outside a small allowlist, so an instance nobody has finished setting up cannot serve
- Per-VM secrets, minted at first boot. The administrator password, the token signing secret, the webhook key, the database password and the TLS key pair are all generated on your machine. None of them existed inside the image
- Scoped API tokens for automation, so a pipeline that only pulls never holds a credential that could push
- An on-VM self test that pushes an artifact through the real TLS front door, fetches it back and compares the bytes, proves a read-only token cannot write, and removes everything it created
- The
postgresql,docker,artifact-keeper-backend,artifact-keeper-webandnginxsystemd services, enabled and active
Artifact Keeper and Ubuntu 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 MIT License.

Prerequisites
- An Azure subscription with permission to create virtual machines
- An SSH key pair
- A network security group allowing inbound TCP 443 (the web interface and API) and 22 (SSH) from the addresses you trust. Nothing else needs to be open
Step 1: Deploy from the Azure Marketplace
Search the Azure Marketplace for Artifact Keeper on Ubuntu 24.04 LTS by cloudimg, choose Create, and select a VM size. Standard_B2ms (2 vCPU, 8 GiB) is the recommended size. Add your SSH public key, allow inbound 443 and 22, and create the VM.
Step 2: Deploy from the Azure CLI
Accept the image terms once per subscription, then deploy:
az vm image terms accept --urn cloudimg:artifact-keeper-ubuntu-24-04:default:latest
az group create --name artifact-keeper-rg --location eastus
az vm create \
--resource-group artifact-keeper-rg \
--name artifact-keeper-vm \
--image cloudimg:artifact-keeper-ubuntu-24-04:default:latest \
--size Standard_B2ms \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
az vm open-port --resource-group artifact-keeper-rg --name artifact-keeper-vm --port 443 --priority 1010
Standard_B2ms (2 vCPU, 8 GiB) is the recommended size. Artifact storage grows with what you publish, so size the OS disk for the artifacts you expect to keep.
Step 3: Connect to your VM
ssh azureuser@<vm-public-ip>
The build account used to create the image does not exist on your VM. azureuser (or whichever administrator name you chose) is the only login.
Step 4: Confirm the registry is running
First boot generates this VM's secrets and only then releases the registry, so the first start takes a little longer than later ones while the database schema is created.
sudo systemctl is-active postgresql@16-main docker artifact-keeper-backend artifact-keeper-web nginx
All five report active. To see the containers themselves:
sudo docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
The PORTS column reads 127.0.0.1:8080->8080/tcp for the backend and 127.0.0.1:3000->3000/tcp for the web tier. Both are published to the loopback interface only; nginx is what the network reaches.
The running build reports its own version, compiled into the binary rather than echoed from a config file:
sudo bash -c 'H=$(grep "^ARTIFACT_KEEPER_HOST=" /root/artifact-keeper-credentials.txt | cut -d= -f2-)
curl -sS --cacert /usr/local/share/ca-certificates/artifact-keeper-appliance.crt \
--resolve "$H:443:127.0.0.1" "https://$H/health"'
Step 5: Check what the network can reach
sudo /usr/local/sbin/artifact-keeper-ports.sh --check
This asserts the exact set of ports reachable from outside the VM, and fails if anything has changed. The shipped set is exactly 22 and 443. There is deliberately no port 80: a port that exists only to redirect is still a port. PostgreSQL has no TCP listener at all, and the backend's metrics port is never published.
Step 6: Retrieve the first-boot credentials
Every secret this registry uses was generated on this VM's first boot and exists on no other instance. They are written to a file only root can read:
sudo sed -E 's/^(ARTIFACT_KEEPER_ADMIN_PASSWORD=).*/\1<redacted>/' \
/root/artifact-keeper-credentials.txt
That shows the file with the password masked, so you can see its shape before you read the real value. The fields are:
| Field | What it is |
|---|---|
ARTIFACT_KEEPER_URL |
the web interface and API base URL for this VM |
ARTIFACT_KEEPER_HOST |
this VM's customer-facing address, resolved at first boot |
ARTIFACT_KEEPER_ADMIN_USER |
the administrator account name, always admin |
ARTIFACT_KEEPER_ADMIN_PASSWORD |
the bootstrap administrator password for this VM |
ARTIFACT_KEEPER_TLS_CERT |
this VM's certificate, for clients that verify it |
Read the non-secret values when you need them:
sudo grep -E '^ARTIFACT_KEEPER_(URL|HOST|ADMIN_USER)=' /root/artifact-keeper-credentials.txt

The password in that file is a bootstrap credential, not a final one. The registry keeps its first-run setup lock armed until you change it, and while the lock is armed every API route outside a small allowlist is refused — including routes the administrator would otherwise be allowed to use. Step 8 clears it.
Step 7: Sign in and clear the setup lock
Open https://<ARTIFACT_KEEPER_HOST>/ in a browser and sign in as admin with the bootstrap password from Step 6. The certificate is generated for your VM, so a browser will warn about it until you install your own (see Step 13).
The registry will require a new password before it serves. Choosing one clears the setup lock, and the API unlocks at the same moment.

To do the same thing from the command line instead, change the password at POST /api/v1/users/me/password and check the lock afterwards:
sudo bash -c 'CF=/root/artifact-keeper-credentials.txt
H=$(grep "^ARTIFACT_KEEPER_HOST=" $CF | cut -d= -f2-)
CA=/usr/local/share/ca-certificates/artifact-keeper-appliance.crt
curl -sS --cacert "$CA" --resolve "$H:443:127.0.0.1" "https://$H/api/v1/setup/status"'
setup_required reads false once the password has been changed. After that the dashboard is the registry's home page.

Step 8: Confirm the door refuses everything but your credentials
sudo /usr/local/sbin/artifact-keeper-verify-auth.sh /root/artifact-keeper-credentials.txt
Run this after Step 7, not before. While the setup lock is still armed the gate cannot attribute its most important result: token minting sits outside the lock's allowlist, so behind the lock a read-only token and an administrator are refused identically. Rather than claim a pass it cannot justify, the gate prints ARTIFACT_KEEPER_AUTH_DEFERRED and stops. Once you have changed the password it runs in full and prints ARTIFACT_KEEPER_AUTH_OK.
This is the check that matters most on this product. It proves, against the real TLS front door:
- the web interface and the API are refused
401with no credential, because anonymous access is switched off explicitly rather than left at its default - weak and product-shaped password guesses are refused
401 - the second built-in account, the scanner service account, has no password at all and cannot authenticate
- this VM's administrator credential is accepted, and its session reports administrator rights
- the setup lock was armed before you cleared it, and refused
403 SETUP_REQUIREDeven to the authenticated administrator - a read-only API token is refused on a write, with a
200read alongside it as the positive control - the running registry reports version
1.10.0
Each of those checks is fed a known-bad input first, so a check that cannot fail is caught rather than trusted.

Step 9: Push an artifact and pull it back
The on-VM self test does the whole round trip for you, through the real TLS front door:
sudo /usr/local/sbin/artifact-keeper-roundtrip.sh /root/artifact-keeper-credentials.txt
It creates a repository, uploads an artifact, fetches the raw bytes back and asserts they are byte-identical to what went in, exercises the native Maven path as a second witness, then deletes everything it created and confirms the registry is empty again.

One detail is worth knowing before you write your own client. A plain GET on an artifact's upload path returns JSON metadata, not the file — and ?download=true, ?raw=true and an Accept: application/octet-stream header all still return that metadata. The raw bytes come from the dedicated download route:
GET /api/v1/repositories/<repository-key>/download/<path>
A client that compares the upload path's response against the file it uploaded will report a mismatch it cannot explain. Use the download route.
Step 10: Mint scoped API tokens for automation
Automation should never carry the administrator password. Mint a token instead, at POST /api/v1/users/me/tokens, and give it only the scopes it needs:
sudo bash -c 'CF=/root/artifact-keeper-credentials.txt
H=$(grep "^ARTIFACT_KEEPER_HOST=" $CF | cut -d= -f2-)
U=$(grep "^ARTIFACT_KEEPER_ADMIN_USER=" $CF | cut -d= -f2-)
CA=/usr/local/share/ca-certificates/artifact-keeper-appliance.crt
echo "mint a token as $U at https://$H/api/v1/users/me/tokens with a scopes array"'
Scopes are namespaced: read:artifacts, write:artifacts, delete:artifacts, read:repositories, write:repositories and so on. A bare read is rejected with a validation error, so name the scope in full.
A token holding only read:artifacts can pull and is refused 403 FORBIDDEN with Token does not have required scope: write:artifacts when it tries to push. That separation is the point: a build that only consumes packages never holds a credential that could replace one.

Step 11: Point a package manager at the registry
Every ecosystem reaches the same endpoint over TLS on this VM's address, authenticating with a token. Maven publishes and resolves under /maven/<repository-key>/, and the other native protocols follow the same shape under their own prefixes. Create a repository for each ecosystem you want to host, choosing the matching format, and the native path appears with it.
Because the address the registry writes into the links it hands back is resolved at first boot, those links already carry this VM's own https address — a client can follow them from a developer machine or a CI runner without any rewriting.

Step 12: Back up your registry
A backup has two halves, because artifact content does not live in the database. The repository metadata is a single dump:
sudo -u postgres pg_dump -Fc artifact_keeper > /var/tmp/artifact-keeper-db.dump && sudo ls -lh /var/tmp/artifact-keeper-db.dump
The artifact content is the blob store on the instance's own disk:
sudo tar -C /var/lib -czf /var/tmp/artifact-keeper-blobs.tar.gz artifact-keeper && sudo ls -lh /var/tmp/artifact-keeper-blobs.tar.gz
Copy both files off the VM and keep them together — a database dump without the matching blobs describes artifacts whose bytes are gone. Note that the secrets are not in either file: they live in /etc/artifact-keeper/artifact-keeper.env, and a restored instance keeps whatever secrets its own first boot generated, so a restore never hands anyone an old password.
Step 13: Use your own certificate and domain
The shipped certificate is generated for your VM's address. To use a real domain, point DNS at the VM, replace the certificate and key, and tell the registry its new address so the links it publishes match:
sudo cp /path/to/fullchain.pem /etc/ssl/artifact-keeper/artifact-keeper.crt
sudo cp /path/to/privkey.pem /etc/ssl/artifact-keeper/artifact-keeper.key
sudo chmod 0600 /etc/ssl/artifact-keeper/artifact-keeper.key
sudo sed -i 's#^AK_EXTERNAL_URL=.*#AK_EXTERNAL_URL=https://packages.your-domain.com#' /etc/artifact-keeper/artifact-keeper.env
sudo systemctl restart artifact-keeper-backend artifact-keeper-web nginx
Changing AK_EXTERNAL_URL matters as much as the certificate: it is the address the registry writes into every link it returns, so a client that follows those links is following whatever is set here. Note that the variable is AK_EXTERNAL_URL — a PUBLIC_URL setting is not read.
Step 14: Keep the image patched
The VM applies Ubuntu security updates automatically. Everything that terminates TLS, stores your data or faces the network — nginx, PostgreSQL and the container runtime — is an ordinary Ubuntu package, so it is covered:
sudo grep -h 'APT::Periodic' /etc/apt/apt.conf.d/20auto-upgrades
Both lines read "1", which is what keeps the package lists refreshed and security updates installed without you doing anything.
Artifact Keeper itself is a pinned upstream container image rather than an Ubuntu package, so apt will never update it and will never warn you that it is old. The units run it by digest with --pull=never, which is what stops an instance quietly changing underneath you. To move to a newer release: take the Step 12 backup, fetch the tag you want, replace the image reference in /etc/systemd/system/artifact-keeper-backend.service and /etc/systemd/system/artifact-keeper-web.service with the new digests, then sudo systemctl daemon-reload and restart both units. Upgrade the two together — the web tier and the backend are released as a matching pair, and a mismatched pair is not a supported combination. The database schema migrates automatically on the next start.
Step 15: Enable the optional scanning and search services
This image deliberately ships without the optional OpenSearch, Trivy and OpenSCAP services. They are not disabled with a flag: their configuration variables are removed entirely, because the registry treats a variable set to an empty string as a configured address and would try to use it. The registry's health check omits those sub-checks when the variables are absent, so their absence reads as healthy rather than permanently degraded.
To turn them on, run each service and add its URL to /etc/artifact-keeper/artifact-keeper.env — OPENSEARCH_URL, TRIVY_URL, OPENSCAP_URL — then restart the backend. Add the variable only when the service behind it is actually running.
Size the instance for it first. OpenSearch alone wants a multi-gigabyte JVM heap, and Standard_B2ms is a burstable size with 8 GiB and no swap. Upstream's own measurements show scanning degrading sharply once CPU credits are exhausted — the same scan that takes under three seconds unconstrained takes roughly thirty-five at a tenth of a vCPU. Move to a non-burstable size before enabling scanning, rather than enabling it here and discovering the cliff under load.
Step 16: Rotate the administrator password
The administrator password can be changed at any time, from the interface or at POST /api/v1/users/me/password. Nothing is baked into the data, and the credentials file is only a record of what first boot generated — after a rotation it no longer matches, so treat the interface as the source of truth and keep the new password in your own secret store.
Confirm the whole posture still holds afterwards:
sudo /usr/local/sbin/artifact-keeper-verify-auth.sh /root/artifact-keeper-credentials.txt
For automation, prefer rotating tokens over rotating the password: revoke the token and mint a replacement with the same scopes, and no pipeline ever needs the administrator credential.
Troubleshooting
The registry will not start. It is deliberately gated on first boot having completed, because a registry with no credentials file would be a registry with no authentication. Check both:
sudo systemctl show -p ActiveState -p Result artifact-keeper-firstboot.service
sudo test -f /var/lib/cloudimg/artifact-keeper-firstboot.done && echo "first-boot sentinel present"
Result=success together with the sentinel means first boot completed and the registry has been released. If the sentinel is missing, sudo journalctl -u artifact-keeper-firstboot.service shows why.
Everything returns 403 with SETUP_REQUIRED. The setup lock is still armed. It refuses the administrator too, by design — change the password (Step 8) and the API unlocks.
Everything returns 401. That is the intended behaviour without a credential, including for reads: anonymous access is switched off on this image, so an unauthenticated caller sees 401 GUEST_ACCESS_DISABLED rather than a readable registry.
A login returns 429. The login rate limiter is doing its job. It budgets attempts per account per source address over a fifteen-minute window, so a burst of failed sign-ins is shed before the password is even checked. Wait for the window to roll, and use API tokens for automation so pipelines never consume the interactive login budget.
An upload works but the download looks wrong. Check which route you fetched. The upload path returns JSON metadata on a GET; the raw bytes come from /api/v1/repositories/<key>/download/<path>. See Step 9.
Check the whole appliance at once.
sudo /usr/local/sbin/artifact-keeper-verify-auth.sh /root/artifact-keeper-credentials.txt
sudo /usr/local/sbin/artifact-keeper-ports.sh --check
Support
cloudimg provides 24/7 support for this image: deployment, upgrades, TLS certificates and custom domains, CI and pipeline integration, credential and token management, storage and database backup and scaling. Contact support through the Azure Marketplace listing or at cloudimg.co.uk.