Pq
Developer Tools Azure

Project Quay on Ubuntu 24.04 on Azure User Guide

| Product: Project Quay on Ubuntu 24.04 LTS on Azure

Overview

This image runs Project Quay 3.17.3, the open source container image registry that powers Quay.io, on Ubuntu 24.04 LTS. Project Quay is the upstream community project of Red Hat Quay: it provides private image repositories with fine grained access control across users, organizations, teams and robot accounts, a standards compliant OCI and Docker v2 registry API, tag lifecycle management with expiration and auto pruning, and a complete REST API for automation.

cloudimg delivers the full upstream service topology natively under systemd, reverse proxied behind nginx, with PostgreSQL 16 and Redis 7 already configured, so a private container registry is operational within minutes of launch.

What is included:

  • Project Quay 3.17.3 built from the official source release into /opt/quay, running in a dedicated Python virtual environment
  • The complete upstream process topology under one systemd unit (quay.service): the web, registry and security scanning application servers plus the full set of background workers for garbage collection, notifications, quotas and auto pruning
  • PostgreSQL 16 as the primary database and Redis 7 for build logs and events
  • nginx publishing the registry and web UI on port 80
  • Registry storage on the instance disk at /var/lib/quay/storage
  • Secure by default: no administrator account and no shared secret ship in the image. On first boot fresh secret keys and a fresh database password are generated, and a unique administrator account plus a scoped API access token are written to a root only file. Anonymous access and open self registration are disabled
  • Per boot swap on the Azure ephemeral resource disk (quay-swap.service), giving the worker fleet headroom on smaller VM sizes
  • A fully patched Ubuntu 24.04 LTS base with unattended security upgrades enabled
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2ms (2 vCPU / 8 GiB RAM) is the recommended size; Standard_B2s (2 vCPU / 4 GiB RAM) works for evaluation, where the image automatically provisions swap on the VM's ephemeral resource disk. NSG inbound: allow 22/tcp from your management network and 80/tcp for the registry and web interface. Project Quay serves plain HTTP on port 80; for production or shared access, terminate TLS with your own domain (see the final step).

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Project Quay 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). Then Review + create then Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name quay \
  --image <marketplace-image-urn> \
  --size Standard_B2ms \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

# Open the HTTP port so you can reach the registry and web interface
az vm open-port --resource-group <your-rg> --name quay --port 80 --priority 900

Step 3 - Retrieve the per VM admin credentials

Nothing secret is baked into the image. On the very first boot a two stage service pair runs: quay-firstboot.service generates a fresh secret key, a fresh database secret key and a fresh database password and writes the instance address into the configuration, then quay-admin-init.service creates a unique admin account plus a scoped OAuth access token through Quay's own bootstrap API, which only works while the database is empty. The credentials are written to /root/quay-credentials.txt, readable only by root. SSH in as azureuser and read them:

$ sudo cat /root/quay-credentials.txt
# Project Quay - generated on first boot by quay-admin-init.service.
# These credentials are unique to THIS VM. Store them somewhere safe.

quay.url=http://20.55.10.42/
quay.admin.user=admin
quay.admin.pass=Xq7pR2mK9wU4nZ8vB3hL6yD1
quay.api.token=1x2Y3z4A5b6C7d8E9f0G1h2I3j4K5l6M7n8O9p0Q

# Canonical keys consumed by the cloudimg smoke verifier + guide substitution:
QUAY_ADMIN_PASSWORD=Xq7pR2mK9wU4nZ8vB3hL6yD1
QUAY_ACCESS_TOKEN=1x2Y3z4A5b6C7d8E9f0G1h2I3j4K5l6M7n8O9p0Q

The first boot services are ordered before the application, so the registry only ever starts once every per VM secret has been generated. The values above are an example; your instance will have its own unique credentials. On first boot the database schema check and admin creation can take two to three minutes after SSH first answers; if the file is not there yet, wait a moment and try again once the sentinel /var/lib/cloudimg/quay-firstboot.done exists.

The two first boot services enabled and the script header describing the per VM secret rotation

Step 4 - Confirm the services are running

Check that PostgreSQL, Redis, the Quay process tree and nginx are all active:

sudo systemctl is-active postgresql redis-server quay nginx

All four report active. quay.service runs Quay's own supervisord process tree, exactly mirroring the upstream topology: the web, registry and security scanning application servers plus the background workers. nginx publishes the interface on port 80 and proxies to Quay's internal nginx on 8080:

sudo ss -tln | grep -E ':80 |:8080 '

The four Quay services reporting active and the listening ports, nginx on 80 and Quay's internal nginx on 8080

Step 5 - Confirm the web endpoints

Quay's built in health checker verifies the database, Redis and storage and returns 200. The registry API and the REST API both refuse anonymous requests with 401; this is the secure default.

curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://localhost/health/instance
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://localhost/v2/
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://localhost/api/v1/user/

The health checker returning 200 and the registry and REST APIs refusing anonymous requests with 401

Step 6 - Sign in to the web UI

Browse to http://<vm-ip>/ and sign in as admin with the password from /root/quay-credentials.txt.

The Project Quay sign in page served on port 80

After signing in you land on the repositories view. The screenshots in this guide show a registry populated with two small example images; a freshly deployed instance starts empty and ready for your own images.

The Project Quay repositories view after signing in

Step 7 - Push your first container image

The instance ships with skopeo, so you can copy an image into your registry straight from the VM itself. This exercises the real Docker v2 push and pull path end to end. Replace the password with the value from /root/quay-credentials.txt:

sudo skopeo copy --dest-tls-verify=false \
  --dest-creds 'admin:<QUAY_ADMIN_PASSWORD>' \
  docker://public.ecr.aws/docker/library/alpine:3.20 \
  docker://localhost/admin/alpine:3.20

Read the image back from the registry to prove the round trip:

sudo skopeo inspect --tls-verify=false \
  --creds 'admin:<QUAY_ADMIN_PASSWORD>' \
  docker://localhost/admin/alpine:3.20 | head -6

From your own workstation the same push works with Docker or Podman. The registry serves plain HTTP until you add TLS, so Docker clients need the instance listed under insecure-registries in /etc/docker/daemon.json (Podman: an [[registry]] entry with insecure = true in /etc/containers/registries.conf):

docker login <vm-ip>          # user admin, password from /root/quay-credentials.txt
docker tag myapp:latest <vm-ip>/admin/myapp:latest
docker push <vm-ip>/admin/myapp:latest

In the web UI the repository appears immediately, with the tag list showing size, modification time and manifest digest:

The repository tag list showing the pushed alpine 3.20 tag with its manifest digest

Under Organizations you can create shared namespaces with teams, role based permissions and robot accounts for CI/CD pipelines:

The organizations view showing the admin namespace with superuser badge

Step 8 - Use the REST API

Project Quay exposes a complete REST API under /api/v1/. The access token from /root/quay-credentials.txt carries administrative scopes. Read the token into a shell variable and query your user and repositories:

TOKEN=$(sudo grep '^QUAY_ACCESS_TOKEN=' /root/quay-credentials.txt | cut -d= -f2-)
curl -s -H "Authorization: Bearer $TOKEN" http://localhost/api/v1/user/ | head -c 200
TOKEN=$(sudo grep '^QUAY_ACCESS_TOKEN=' /root/quay-credentials.txt | cut -d= -f2-)
curl -s -H "Authorization: Bearer $TOKEN" 'http://localhost/api/v1/repository?namespace=admin'

skopeo inspecting the pushed image and the repository API listing the namespace

Additional application tokens can be created in the web UI under your account settings, and robot accounts (machine credentials scoped to specific repositories) under each organization.

Step 9 - Enable HTTPS with your own domain

Project Quay ships on plain HTTP on port 80 so it works immediately. For production, point a DNS record at the VM's public IP and terminate TLS in front of the registry. A common approach is to obtain a certificate with certbot and configure the fronting nginx site (/etc/nginx/sites-available/quay) for TLS, or to place Azure Application Gateway in front of the VM. After enabling TLS, update the application's canonical address in /opt/quay/conf/stack/config.yaml: set SERVER_HOSTNAME to your domain and PREFERRED_URL_SCHEME to https, then restart:

$ sudo systemctl restart quay

With TLS in place, Docker and Podman clients no longer need the insecure registry configuration.

Administration and support

The application configuration lives in /opt/quay/conf/stack/config.yaml (owned root and readable by the quay service user). Registry blobs live under /var/lib/quay/storage; size the OS disk or attach a data disk to match your image volume. The process tree is managed as one unit with sudo systemctl restart quay, and its logs stream to the journal: sudo journalctl -u quay -f. The web, registry and security scanning worker counts are tuned through the WORKER_COUNT_* environment variables in /etc/systemd/system/quay.service, using the same variables the upstream container supports. quay-swap.service provisions swap on the Azure ephemeral resource disk at every boot; it degrades gracefully on VM sizes without a resource disk. To add trusted corporate CA certificates for outbound TLS, use Ubuntu's standard flow: copy the certificate to /usr/local/share/ca-certificates/ and run sudo update-ca-certificates.

This image is a repackaged open source software product with additional charges for cloudimg support services. Our engineers provide 24/7 support for deployment, upgrades, TLS termination, storage scaling, mirroring configuration and CI/CD integration. This image is an independent build of the upstream Project Quay open source software (Apache License 2.0) and is not affiliated with, sponsored or endorsed by Red Hat. Quay is a trademark of Red Hat, Inc. All product and company names are trademarks or registered trademarks of their respective holders; use of the names here is purely to identify the software this image packages.