Artifact Keeper
Overview
Artifact Keeper is an open source universal artifact registry. Instead of running a separate server for each 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. Everything is published to and pulled from one place, with one set of credentials and one audit trail.
This SecureAnyCloud 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.
What you get:
- Artifact Keeper 1.10.0, using the official upstream container images pinned by digest and 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 stay on the loopback interface, and there is no plaintext port. - PostgreSQL 16, installed natively from the Ubuntu archive so the same updates patch it as the rest of the VM. It has no TCP listener: 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 with
401instead of seeing 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 that 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 exists 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 that a read-only token cannot write, and removes everything it created.
- The
postgresql,docker,artifact-keeper-backend,artifact-keeper-webandnginxsystemd services, enabled and active. - 24/7 SecureAnyCloud support.
This image packages the unmodified open source software, which is distributed under the MIT License. Artifact Keeper and Ubuntu are trademarks of their respective owners. All product and company names are trademarks or registered trademarks of their respective holders. Their use does not imply any affiliation with or endorsement by them.
Prerequisites
- An Azure subscription with permission to create virtual machines.
- An SSH key pair.
- A network security group that allows inbound TCP 443 (the web interface and API) and 22 (SSH) from the addresses you trust. Nothing else needs to be open.
Deploy from the Azure Marketplace
Search the Azure Marketplace for Artifact Keeper on Ubuntu 24.04 LTS by SecureAnyCloud, then choose Create.
Select a VM size. Standard_B2ms (2 vCPU, 8 GiB) is the recommended size.
Add your SSH public key, allow inbound ports 443 and 22, and create the VM.
Deploy from the Azure CLI
Accept the image terms once per subscription, then create the VM and open port 443:
az vm image terms accept --urn sac: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 sac1647283583153: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 1010Standard_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.
Connect to your VM
Connect over SSH:
ssh azureuser@<vm-public-ip>The build account used to create the image does not exist on your VM. azureuser (or the administrator name you chose) is the only login.
Confirm the registry is running
First boot generates this VM's secrets and only then releases the registry. The first start takes a little longer than later ones because the database schema is created. Check the services:
sudo systemctl is-active postgresql@16-main docker artifact-keeper-backend artifact-keeper-web nginxAll five report active. To see the containers themselves, run:
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 the only service the network reaches.
The running build reports its own version, which is 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"'Check what the network can reach
Run the exposure check:
sudo /usr/local/sbin/artifact-keeper-ports.sh --checkThis 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, because a port that exists only to redirect is still a port. PostgreSQL has no TCP listener, and the backend's metrics port is never published.
Retrieve the first-boot credentials
Every secret this registry uses was generated on this VM's first boot and exists on no other instance. The secrets are written to a file that only root can read. Display it with the password masked:
sudo sed -E 's/^(ARTIFACT_KEEPER_ADMIN_PASSWORD=).*/\1<redacted>/' \
/root/artifact-keeper-credentials.txtThis shows the shape of the file before you read the real password. The fields are:
- 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
To read the non-secret values, run:
sudo grep -E '^ARTIFACT_KEEPER_(URL|HOST|ADMIN_USER)=' /root/artifact-keeper-credentials.txtThe 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. 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 7 clears the lock.
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 your browser warns about it until you install your own (see Step 13).
The registry requires a new password before it serves. Choosing one clears the setup lock, and the API unlocks at the same moment.
If you drive the VM from a script rather than a browser, the image ships the whole first-run step as one command. It signs in with the bootstrap password and changes the administrator password at POST /api/v1/users/me/password to a fresh random one. It then writes that new password back into /root/artifact-keeper-credentials.txt (still 0600 root:root, so the file never goes stale) and runs the full credential gate of Step 8 against the now-unlocked registry:
sudo /usr/local/sbin/artifact-keeper-verify-auth.sh /root/artifact-keeper-credentials.txt --complete-setupThis change is one-way: you cannot return to the bootstrap password afterwards. The command ends on ARTIFACT_KEEPER_AUTH_OK.
If you cleared the lock in the browser instead, the credentials file is now stale. It still carries the bootstrap password, and every later step on this page reads the administrator password from that file. Put the password you chose into it before going on, or those steps are refused with 401. Then check the setup status:
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, whichever way you changed it. From then on, the dashboard is the registry's home page.
Verify that only your credentials are accepted
Run the credential gate:
sudo /usr/local/sbin/artifact-keeper-verify-auth.sh /root/artifact-keeper-credentials.txtRun this after Step 7, not before. If you used the command-line path in Step 7, --complete-setup already ran this gate once, and this is the same proof on demand.
While the setup lock is armed, the gate cannot attribute its most important result. Token minting sits outside the lock's allowlist, so a read-only token and an administrator are refused identically. Instead of claiming a pass it cannot justify, the gate prints ARTIFACT_KEEPER_AUTH_DEFERRED and stops. After you change the password, it runs in full and prints ARTIFACT_KEEPER_AUTH_OK.
This is the most important check on this product. Against the real TLS front door, it proves that:
- the web interface and the API are refused with
401when no credential is supplied, because anonymous access is switched off explicitly rather than left at its default - weak and product-shaped password guesses are refused with
401 - the second built-in account, the scanner service account, has no password 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 check is fed a known-bad input first, so a check that cannot fail is caught rather than trusted.
Push an artifact and pull it back
The on-VM self-test runs the whole round trip for you through the real TLS front door:
sudo /usr/local/sbin/artifact-keeper-roundtrip.sh /root/artifact-keeper-credentials.txtThe script creates a repository, uploads an artifact, fetches the raw bytes back and asserts that they are byte-identical to what went in. It exercises the native Maven path as a second witness, then deletes everything it created and confirms that the registry is empty again.
Know this before you write your own client: a plain GET on an artifact's upload path returns JSON metadata, not the file. ?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 reports a mismatch it cannot explain. Always fetch from the download route.
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 that holds only read:artifacts can pull. When it tries to push, it is refused with 403 FORBIDDEN and Token does not have required scope: write:artifacts. This separation is the point: a build that only consumes packages never holds a credential that could replace one.
Point a package manager at the registry
Every ecosystem reaches the same endpoint over TLS on this VM's address and authenticates with a token. Maven publishes and resolves under /maven/<repository-key>/. The other native protocols follow the same shape under their own prefixes.
Create a repository for each ecosystem you want to host and choose the matching format. The native path appears with it.
The registry resolves its own address at first boot, so the links it hands back already carry this VM's https address. A client can follow them from a developer machine or a CI runner without any rewriting.
Back up your registry
A backup has two parts, because artifact content does not live in the database. First, dump the repository metadata:
sudo -u postgres pg_dump -Fc artifact_keeper > /var/tmp/artifact-keeper-db.dump && sudo ls -lh /var/tmp/artifact-keeper-db.dumpSecond, archive the artifact content, which 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.gzCopy both files off the VM and keep them together. A database dump without the matching blobs describes artifacts whose bytes are gone.
The secrets are not in either file. They live in /etc/artifact-keeper/artifact-keeper.env, and a restored instance keeps the secrets that its own first boot generated, so a restore never hands anyone an old password.
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 and replace the certificate and key. Then 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 nginxChanging AK_EXTERNAL_URL matters as much as the certificate. It is the address the registry writes into every link it returns, so clients follow whatever is set here. The variable must be AK_EXTERNAL_URL, because a PUBLIC_URL setting is not read.
Keep the image patched
The VM applies Ubuntu security updates automatically. nginx, PostgreSQL and the container runtime terminate TLS, store your data and face the network. Each is an ordinary Ubuntu package, so automatic updates cover them. Confirm the setting:
sudo grep -h 'APT::Periodic' /etc/apt/apt.conf.d/20auto-upgradesBoth lines read "1". That keeps the package lists refreshed and security updates installed without any action from you.
Artifact Keeper itself is a pinned upstream container image rather than an Ubuntu package, so apt never updates it and never warns you that it is old. The units run it by digest with --pull=never, which stops an instance from quietly changing underneath you.
To move to a newer release, take the Step 12 backup and 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 run 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.
Enable the optional scanning and search services
This image deliberately ships without the optional OpenSearch, Trivy and OpenSCAP services. Their configuration variables are removed entirely instead of being disabled with a flag, because the registry treats a variable set to an empty string as a configured address and tries 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 or OPENSCAP_URL. Then restart the backend. Add a variable only when the service behind it is running.
Size the instance 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: a scan that takes under three seconds unconstrained takes roughly thirty-five at a tenth of a vCPU. Move to a non-burstable size before you enable scanning.
Rotate the administrator password
You can change the administrator password at any time, from the interface or at POST /api/v1/users/me/password. Nothing is baked into the data. The credentials file is only a record of what first boot generated, and after a rotation it no longer matches. Treat the interface as the source of truth and keep the new password in your own secret store.
This matters for the checks on this page. Step 8 and Step 9 both read the administrator password from that file, so after a rotation they are refused with 401 until you write the new password into ARTIFACT_KEEPER_ADMIN_PASSWORD yourself. The posture check below needs no credential, so use it straight after a rotation:
sudo /usr/local/sbin/artifact-keeper-ports.sh --checkFor automation, rotate tokens rather than 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. The registry is deliberately gated on first boot having completed, because a registry with no credentials file would have no authentication. Check both the unit result and the sentinel file:
sudo systemctl show -p ActiveState -p Result artifact-keeper-firstboot.service
sudo test -f /var/lib/sac/data/artifact-keeper-firstboot.done && echo "first-boot sentinel present"Result=success together with the sentinel means that 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. By design, it refuses the administrator too. Change the password (Step 7) and the API unlocks.
Everything returns 401. This is the intended behavior without a credential, including for reads. Anonymous access is switched off on this image, so an unauthenticated caller sees 401 GUEST_ACCESS_DISABLED instead of 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 checked. Wait for the window to roll over. 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. A GET on the upload path returns JSON metadata. The raw bytes come from /api/v1/repositories/<key>/download/<path>. See Step 9.
Check the whole appliance at once. The exposure check needs no credential, so you can run it as often as you like:
sudo /usr/local/sbin/artifact-keeper-ports.sh --checkThe full credential gate is the Step 8 command. Each run spends about ten sign-in attempts against the administrator account, and the limiter budgets roughly thirty per account per source address per fifteen minutes. Run it when you need the proof, not in a loop, or it sheds its own logins with 429.
Support
SecureAnyCloud provides 24/7 support for this image. Support covers deployment, upgrades, TLS certificates and custom domains, CI and pipeline integration, credential and token management, and storage and database backup and scaling. Contact support through the Azure Marketplace listing or at secureanycloud.com.
Deploy on Azure
Launch Artifact Keeper 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