Clair on Ubuntu 24.04 on Azure User Guide
Overview
Clair is an Apache-2.0 licensed open source static analysis service for containers, maintained by the Quay project. It pulls the layers of an OCI or Docker image, indexes the operating system and language packages inside them, matches those packages against a continuously updated set of vulnerability databases, and reports the known CVEs that affect the image. Clair has no web interface: it is an HTTP API service that registries, CI pipelines and security tooling call to submit images and read back a vulnerability report.
The cloudimg image runs Clair 4.9.0 in combo mode - the indexer, matcher and notifier all in a single process - backed by a local PostgreSQL database on the same VM, so the appliance is entirely self contained. The companion clairctl command line client is installed for one line scans. A unique PostgreSQL password is generated on each VM's first boot, so no credential is ever baked into the image. Backed by 24/7 cloudimg support.
What is included:
- Clair 4.9.0 (built from the official quay/clair source release) running in combo mode as a dedicated
clairsystem user - A local PostgreSQL back end on the same VM (all three Clair components share the one database)
- The
clairctlcommand line client for scanning images and retrieving reports - The REST API on port
6060and a loopback-only introspection server (health checks and Prometheus metrics) on127.0.0.1:8089 - A unique PostgreSQL password generated on first boot and written to a root-only file
- 24/7 cloudimg support
Clair's REST API is unauthenticated by default (this is Clair's own default). The appliance is secure by default because the Azure network security group opens only SSH (22) - port 6060 is not reachable from the network until you deliberately open it and apply your own access controls.
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting point; scale up for larger images and higher scan throughput. NSG inbound: allow 22/tcp from your management network. Open 6060/tcp only when you intend to expose the scanning API, and only behind your own controls.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Clair 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) only. Then Review + create -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name clair \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm Clair and PostgreSQL are running
Clair runs as clair.service in combo mode, backed by the local PostgreSQL service. Check both are active and confirm the client version:
clairctl --version
sudo systemctl is-active clair.service postgresql.service
clairctl version v4.9.0 (claircore v1.5.48)
active
active
Confirm the listening ports: the REST API on 6060, the introspection (health/metrics) server on loopback 8089, and PostgreSQL on loopback 5432:
ss -tln | grep -E ':6060|:8089|:5432'
LISTEN 0 200 127.0.0.1:5432 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8089 0.0.0.0:*
LISTEN 0 4096 *:6060 *:*

Step 5 - Check the health and readiness endpoints
Clair exposes /healthz (liveness) and /readyz (readiness) on the loopback-only introspection server. Both return 200 when Clair is healthy:
curl -s -o /dev/null -w 'healthz: %{http_code}\n' http://127.0.0.1:8089/healthz
curl -s -o /dev/null -w 'readyz: %{http_code}\n' http://127.0.0.1:8089/readyz
healthz: 200
readyz: 200
The introspection server also serves Prometheus metrics at http://127.0.0.1:8089/metrics.
Step 6 - Retrieve your per-VM database password
Each VM generates its own unique PostgreSQL password on first boot for the clair role and writes it to a root-only credentials file:
sudo cat /root/clair-credentials.txt
# Clair 4.9.0 - generated on first boot. Unique to this VM.
CLAIR_API_URL=http://<vm-ip>:6060/
CLAIR_HEALTH_URL=http://127.0.0.1:8089/healthz
CLAIR_DB_NAME=clair
CLAIR_DB_USER=clair
CLAIR_DB_PASSWORD=<CLAIR_DB_PASSWORD>
The PostgreSQL password is used internally by Clair over loopback; PostgreSQL is not exposed on the network. In the commands below, <CLAIR_DB_PASSWORD> stands for the value of CLAIR_DB_PASSWORD. Store it in your secrets manager.

Step 7 - Scan a container image with clairctl
clairctl report runs the full round-trip: it fetches the image manifest and layers, submits them to Clair's indexer, then retrieves the vulnerability report from the matcher. No Docker daemon is required - clairctl pulls the image itself. Scan any public image by reference:
clairctl report --host http://127.0.0.1:6060/ mirror.gcr.io/library/alpine:3.14
alpine:3.14 ok
clairctl prints the image reference followed by the result; ok means the round-trip completed and the report was retrieved. When vulnerabilities are found, they are listed with their CVE identifiers, affected package and severity.
The first scan after boot can take a few minutes while Clair downloads the upstream vulnerability data for the first time; subsequent scans are fast. Point clairctl at any image in any registry you can reach - use -o json for machine-readable output to feed into your pipeline.

Step 8 - Query the REST API directly
Registries and CI systems call Clair's REST API directly. The indexer state endpoint confirms the API is serving:
curl -s http://127.0.0.1:6060/indexer/api/v1/index_state
{"state":"cb31df8269833698891b35a63251a81a"}
The per-VM database password authenticates the clair role over loopback - a wrong password is rejected, so the database is never reachable without the per-VM credential:
PGPASSWORD=<CLAIR_DB_PASSWORD> psql -h 127.0.0.1 -U clair -d clair -c "SELECT current_user, current_database();"
current_user | current_database
--------------+------------------
clair | clair
(1 row)

Step 9 - Expose and secure the scanning API
The REST API binds 0.0.0.0:6060, but the Azure NSG opens only 22 by default, so the API is not reachable from the network until you open 6060. Clair's API is unauthenticated by default - before exposing it, put it behind your own controls. Recommended options:
- Keep
6060closed and reach Clair over an SSH tunnel from the machine that scans:
ssh -L 6060:127.0.0.1:6060 azureuser@<vm-public-ip>
# then, locally:
clairctl report --host http://127.0.0.1:6060/ <image:tag>
- Restrict the NSG rule for
6060to the source IP ranges of your registry or CI runners only. - Front Clair with a reverse proxy that terminates TLS and enforces authentication, and enable Clair's own pre-shared-key JWT auth (
auth.pskin/etc/clair/config.yaml) for API callers.
Never expose 6060 to the internet without transport encryption and access control.
Maintenance
- Vulnerability data: Clair's matcher refreshes its vulnerability databases automatically in the background on a schedule, so reports stay current with no action from you.
- Configuration: the combo config lives at
/etc/clair/config.yaml. After changing it, runsudo systemctl restart clair.service. - Password: the
clairrole password is generated per VM on first boot. Rotate it withsudo -u postgres psql -c "ALTER ROLE clair WITH PASSWORD '<new-password>';"and update the connstrings in/etc/clair/config.yaml. - Logs:
sudo journalctl -u clair.service -ffollows the scanner logs. - Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.
- Scaling: for high volume scanning, Clair also supports a distributed deployment (separate indexer, matcher and notifier); this image ships the self-contained combo configuration.
Support
cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.
Clair is a trademark of Red Hat, Inc. This image is provided by cloudimg and is not affiliated with or endorsed by the Quay project or Red Hat. PostgreSQL is a trademark of the PostgreSQL Community Association.