Ci
Media & Entertainment Azure

Cantaloupe IIIF Image Server on Ubuntu 24.04 on Azure User Guide

| Product: Cantaloupe IIIF Image Server on Ubuntu 24.04 LTS on Azure

Overview

Cantaloupe is an open source image server that implements the International Image Interoperability Framework (IIIF) Image API. It serves your source images over a standard, interoperable HTTP API that delivers any region, size, rotation and quality of an image on request, generating tiles and derivatives on the fly so clients never download more than they need. This is what powers deep zoom experiences and IIIF viewers such as OpenSeadragon and Mirador across libraries, museums, archives and digital humanities projects. Point it at a directory of images and it exposes each one as an addressable IIIF resource, complete with an info.json describing the available sizes and tiles.

The cloudimg image installs the official Cantaloupe 5.0.7 release JAR at /opt/cantaloupe, running as the cantaloupe service under OpenJDK 17 bound to loopback 127.0.0.1:8182, behind nginx on port 443. Common raster formats (JPEG, PNG, TIFF, GIF, BMP) are served by the built in Java 2D processor and JPEG 2000 by OpenJPEG.

Secure by default, no default login: Cantaloupe's IIIF endpoints are public with no authentication by default, and its admin control panel is disabled. This image never exposes any of it directly. Cantaloupe binds loopback only, and nginx is the only public listener, fronting the whole IIIF surface over TLS with HTTP Basic Auth. A cantaloupe-firstboot.service oneshot generates a unique per VM password and a per VM self signed TLS certificate on each VM's first boot, writes a bcrypt .htpasswd and a root only info note, proves the auth gate with a real IIIF round trip, then disables itself. No two VMs share a password, and none is baked into the image: the shipped .htpasswd is empty (nginx returns 401 for every request) and nginx will not start at all until first boot has seeded the per VM credential.

Note on the interface: Cantaloupe is an image server driven through the IIIF Image API. You address images by URL, embed them in an IIIF viewer, or fetch derivatives with any HTTP client. This guide uses curl against the API. The self signed certificate means clients pass -k (or trust /etc/nginx/tls/cantaloupe.crt); replace it with a CA certificate for production (see the HTTPS section).

What is included:

  • Cantaloupe 5.0.7 (University of Illinois/NCSA Open Source License) on OpenJDK 17
  • cantaloupe.service bound to 127.0.0.1:8182, run as a dedicated non root cantaloupe user under a hardened systemd unit
  • nginx reverse proxy on port 443 (TLS) with per VM HTTP Basic Auth, and a port 80 redirect
  • An unauthenticated /healthz endpoint for load balancer and probe checks
  • cantaloupe-firstboot.service for the first boot per VM certificate, password and gate proof
  • A unique per VM password generated on first boot, in a root only 0600 file
  • One demo image (identifier sample.jpg) under /var/lib/cantaloupe/images so the IIIF endpoints render immediately
  • Ubuntu 24.04 LTS base, fully patched
  • 24/7 cloudimg support, 24h response SLA

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet with a subnet. Cantaloupe is CPU and memory bound while generating derivatives; large source images and high request volumes benefit from more RAM. Recommended VM size: Standard_B2s (2 vCPU, 4 GB RAM) for evaluation and light collections, or a Standard_D2s_v5 or larger for production throughput and large images.

Step 1: Deploy from the Azure Portal

Search the Marketplace for Cantaloupe on Ubuntu 24.04, choose your VM size, and attach an NSG that allows TCP 22 (SSH) from your management network and TCP 443 (the IIIF API) from the networks and applications that need to serve images. Front the service with a CA issued certificate in production (see the HTTPS section below).

Step 2: Deploy from the Azure CLI

RG="cantaloupe-prod"; LOCATION="eastus"; VM_NAME="cantaloupe-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/cantaloupe-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name cantaloupe-vnet --address-prefix 10.90.0.0/16 --subnet-name cantaloupe-subnet --subnet-prefix 10.90.1.0/24
az network nsg create -g "$RG" --name cantaloupe-nsg
az network nsg rule create -g "$RG" --nsg-name cantaloupe-nsg --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name cantaloupe-nsg --name allow-https --priority 110 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 443 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name cantaloupe-vnet --subnet cantaloupe-subnet --nsg cantaloupe-nsg --public-ip-sku Standard

Step 3: Connect via SSH

ssh azureuser@<vm-ip>

Step 4: Verify the services are running

Cantaloupe listens on loopback 127.0.0.1:8182 and nginx fronts the IIIF API on port 443 (with port 80 redirecting to it). The /healthz endpoint is public; every other endpoint requires your per VM password.

sudo systemctl is-active cantaloupe nginx
sudo ss -tln | grep -E ':443 |:8182'
curl -sk https://127.0.0.1/healthz

You should see both services active, the two listening sockets (Cantaloupe on loopback 8182, nginx on 443), and ok from the health endpoint. An unauthenticated IIIF request returns 401.

Terminal showing the cantaloupe and nginx services active, Cantaloupe bound to loopback 8182 with nginx fronting port 443, and the health endpoint returning ok

Step 5: Read your per VM password

The password was generated on this VM's first boot and stored in a root only file. Read it and keep it secret: it authenticates every IIIF request. Confirm that unauthenticated and wrong password requests are rejected while the per VM password succeeds.

sudo ls -l /root/cantaloupe-credentials.txt
echo "no credentials -> HTTP $(curl -sk -o /dev/null -w '%{http_code}' https://127.0.0.1/iiif/3/sample.jpg/info.json)"
echo "wrong password -> HTTP $(curl -sk -o /dev/null -w '%{http_code}' -u admin:wrong https://127.0.0.1/iiif/3/sample.jpg/info.json)"

The info note holds CANTALOUPE_URL, CANTALOUPE_USERNAME (admin) and CANTALOUPE_PASSWORD, is owned root:root with mode 0600. Unauthenticated and wrong password calls return 401 because the nginx Basic Auth gate enforces authentication on the whole IIIF surface.

Terminal showing the root only credentials file with 0600 permissions and the IIIF endpoint rejecting unauthenticated and wrong password requests with HTTP 401 while the per VM password returns HTTP 200

Step 6: Fetch the IIIF image information

Every IIIF resource has an info.json that describes the image's full dimensions and the sizes and tiles a client can request. This is what an IIIF viewer reads first to plan its deep zoom tiling. Load the per VM password into a shell variable, then request the demo image's info.json.

export CANTALOUPE_PW=$(sudo grep '^CANTALOUPE_PASSWORD=' /root/cantaloupe-credentials.txt | cut -d= -f2-)
curl -sk -u "admin:$CANTALOUPE_PW" https://127.0.0.1/iiif/3/sample.jpg/info.json | python3 -m json.tool

The response is an IIIF Image API 3.0 info.json: the @context, an ImageService3 type, the full width and height (2000 x 1500 for the demo image), and the pyramid of sizes and tiles a viewer can request. To serve your own image, drop a file into /var/lib/cantaloupe/images/ and address it by filename.

Terminal showing the IIIF Image API 3.0 info.json for the demo image with its context, ImageService3 type, full width and height, and the pyramid of available sizes and tiles

Step 7: Render image derivatives on demand

The IIIF Image API encodes the region, size, rotation and quality you want directly in the URL, and Cantaloupe generates that derivative on the fly. Request the full image, a 500 pixel wide thumbnail, and a 300 x 300 square crop of the demo image.

export CANTALOUPE_PW=$(sudo grep '^CANTALOUPE_PASSWORD=' /root/cantaloupe-credentials.txt | cut -d= -f2-)
# Full image at native size
curl -sk -u "admin:$CANTALOUPE_PW" "https://127.0.0.1/iiif/3/sample.jpg/full/max/0/default.jpg" -o full.jpg
# A 500 pixel wide thumbnail (aspect preserved)
curl -sk -u "admin:$CANTALOUPE_PW" "https://127.0.0.1/iiif/3/sample.jpg/full/500,/0/default.jpg" -o thumb.jpg
# A 300 x 300 square crop
curl -sk -u "admin:$CANTALOUPE_PW" "https://127.0.0.1/iiif/3/sample.jpg/square/300,300/0/default.jpg" -o square.jpg
identify full.jpg thumb.jpg square.jpg

Each request returns a JPEG rendered to exactly the region and size in the URL. The IIIF request syntax is {identifier}/{region}/{size}/{rotation}/{quality}.{format} — for example .../sample.jpg/full/500,/0/default.jpg means the full image, scaled to 500 pixels wide, no rotation, default quality, as JPEG.

Terminal showing three IIIF image derivatives rendered on demand, the full 2000x1500 image, a 500x375 thumbnail and a 300x300 square crop, each returning HTTP 200, alongside the Cantaloupe and Java version baseline

Step 8: Serve your own images

Cantaloupe serves images from /var/lib/cantaloupe/images/ using the identifier as the filename. Copy an image in, set the ownership, and address it immediately — no restart or reindex is required.

export CANTALOUPE_PW=$(sudo grep '^CANTALOUPE_PASSWORD=' /root/cantaloupe-credentials.txt | cut -d= -f2-)
sudo cp /var/lib/cantaloupe/images/sample.jpg /var/lib/cantaloupe/images/myimage.jpg
sudo chown cantaloupe:cantaloupe /var/lib/cantaloupe/images/myimage.jpg
curl -sk -o /dev/null -w 'myimage info.json -> HTTP %{http_code}\n' -u "admin:$CANTALOUPE_PW" https://127.0.0.1/iiif/3/myimage.jpg/info.json

For large collections, mount a dedicated data disk or a file share at /var/lib/cantaloupe/images/. Cantaloupe also supports pulling images from cloud object storage and databases through its other sources — see the upstream configuration reference. The active configuration lives at /etc/cantaloupe/cantaloupe.properties; edit it and run sudo systemctl restart cantaloupe to apply changes.

Step 9: Embed in an IIIF viewer

Because the API is standard IIIF, any IIIF viewer can display these images with deep zoom. Point OpenSeadragon or Mirador at the image's info.json URL, for example https://<vm-ip>/iiif/3/sample.jpg/info.json, supplying the Basic Auth credentials. The viewer reads the tile pyramid from info.json and requests only the tiles in view as the user pans and zooms.

Step 10: Enable a CA issued certificate

The image ships with a per VM self signed certificate so the API is encrypted from first boot. For production, point a DNS record at the VM and replace it with a CA issued certificate using certbot.

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain>

After issuance, restrict the NSG so only 443 (and 22 from your management network) is reachable, and serve images over https://<your-domain>/iiif/3/....

Persistence, first boot and updates

Source images live under /var/lib/cantaloupe/images/ on the OS disk and are captured into the image; for large or changing collections, attach a data disk or file share at that path. The first boot service generates the per VM TLS certificate and Basic Auth password, proves the auth gate, then disables itself so subsequent reboots are unaffected. The OS ships fully patched with unattended security upgrades enabled. To change the password later, run sudo htpasswd -B /etc/nginx/.cantaloupe.htpasswd admin and reload nginx with sudo systemctl reload nginx.

Support

Every cloudimg deployment includes 24/7 support with a 24 hour response SLA. Contact support@cloudimg.co.uk for help with deployment, configuration or scaling.

This is a repackaged open source software product with additional charges for cloudimg support services. Cantaloupe is distributed under the University of Illinois/NCSA Open Source License. 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.