Cantaloupe IIIF Image Server 5.0.8
Overview
This guide covers launching and using the Cantaloupe IIIF Image Server 5.0.8 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud media servers listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.
Cantaloupe is an open-source image server for the IIIF Image API, the image delivery standard of the International Image Interoperability Framework, used by libraries, museums, archives and digital humanities projects. Each of your images gets a IIIF address. A client asks for any region of it, at any size, rotation, quality and format, and Cantaloupe cuts and scales that derivative on the fly. A deep-zoom viewer therefore downloads only the tiles on screen. An info.json document describes each image: its size, the tile sizes and what the server supports.
On this image, the IIIF Image API 3 base is https://<host>/iiif/3/, and API 2 is at https://<host>/iiif/2/. Both are served over HTTPS only. Port 80 only redirects.
What you get:
- A password unique to your instance. nginx puts HTTP Basic Auth (user
admin) in front of every IIIF request. The password is generated on your instance's first boot and written to/root/cantaloupe-credentials.txt(root only). Nothing secret is in the image. - Images from a local directory only. Cantaloupe reads source images from
/var/lib/cantaloupe/imagesand nowhere else. Its HTTP, S3, Azure and database sources and its delegate script are switched off, so no request can make the server fetch a URL, and a path outside the directory is refused. - No network access for Cantaloupe. Cantaloupe listens on
127.0.0.1:8182behind nginx. It runs as the unprivilegedcantaloupeaccount in a systemd sandbox and may not send traffic to any address but the loopback. Neither Cantaloupe nor nginx can reach the EC2 instance metadata service. - Bounded work per request. Upscaling is refused, so no derivative is larger than its source image. A
maxsize request is capped at 10 megapixels. nginx rate-limits each client and accepts onlyGETandHEAD. Error pages carry no stack trace. - Admin console and API off. The control panel, the REST API, the health endpoint and the legacy IIIF 1 endpoint are disabled. nginx answers
404for anything that is not the IIIF API. - HTTPS from the first boot. nginx serves port 443 with a certificate generated on your instance. A certificate you install yourself is never replaced (Step 9).
- JPEG 2000 without proprietary code. OpenJPEG from Ubuntu decodes JPEG 2000 images. The upstream release archive's optional Kakadu libraries are not on the image.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
- Cantaloupe 5.0.8 (University of Illinois/NCSA Open Source License), OpenJDK 21, OpenJPEG and nginx from Ubuntu 24.04, cloud-init and the AWS Systems Manager agent.
- Default user:
ubuntu, architecture: x86_64, recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet
- A security group that allows TCP 22 (SSH) from your own network, and TCP 443 (the IIIF API) and TCP 80 (redirects to HTTPS) from the networks your clients use. Open 443 only as widely as your clients need.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Cantaloupe IIIF Image Server 5.0.8 on Ubuntu 24.04 as the software version, choose your Region, then choose Launch through EC2.
Choose m5.large, your key pair, a security group as described above, and a subnet. Your images live on the root volume. If your collection is large, raise its size under Configure storage.
Alternatively, launch with the AWS CLI. Replace the values in angle brackets. This command sets a 100 GiB root volume:
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=100,VolumeType=gp3}' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=cantaloupe}]'Connecting to your instance
Connect to Cantaloupe IIIF Image Server 5.0.8 on Ubuntu 24.04 over SSH as the ubuntu user:
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot works out the instance's name, generates the certificate and the password, and starts Cantaloupe. It proves a IIIF round trip over HTTPS and only then opens the web server. This takes about a minute:
systemctl is-active cantaloupe nginx cantaloupe-set-hostactive active activecantaloupe-set-host runs on every boot before nginx. It keeps the served name, the certificate and nginx's host rules in step (Step 9).
Only SSH (22) and nginx (80, 443) listen on the network. Cantaloupe's own listener is on the loopback address only. Java shows it as the IPv4-mapped [::ffff:127.0.0.1]:8182.
The :53 entries are Ubuntu's local DNS resolver, :323 is the time service on the loopback address, and :68 is the DHCP client:
sudo ss -lntup | awk 'NR>1 {print $1, $5}' | sort -utcp 0.0.0.0:22 tcp 0.0.0.0:443 tcp 0.0.0.0:80 tcp 127.0.0.53%lo:53 tcp 127.0.0.54:53 tcp [::]:22 tcp [::]:443 tcp [::]:80 tcp [::ffff:127.0.0.1]:8182 udp 127.0.0.1:323 udp 127.0.0.53%lo:53 udp 127.0.0.54:53 udp 172.31.16.10%ens5:68 udp [::1]:323Cantaloupe runs as the cantaloupe account on Ubuntu's OpenJDK 21, with its configuration in /etc/cantaloupe/cantaloupe.properties:
ps -o user:12=,args= -C java java -version 2>&1 | head -1cantaloupe /usr/bin/java -XX:MaxRAMPercentage=50 -XX:+ExitOnOutOfMemoryError -XX:-UsePerfData -Djava.awt.headless=true -Djava.io.tmpdir=/var/lib/cantaloupe/tmp -Duser.home=/var/lib/cantaloupe/tmp -Dcantaloupe.config=/etc/cantaloupe/cantaloupe.properties -jar /opt/cantaloupe/cantaloupe-5.0.8.jar openjdk version "21.0.12.1" 2026-08-18Cantaloupe may use up to half of the instance's memory for its Java heap, so a larger instance type gives it more room for big images without any change.
If nginx is not running, either first boot has not finished or it failed. In both cases the IIIF API is never opened. Check systemctl status cantaloupe-firstboot and sudo tail -40 /var/log/sac/cantaloupe-firstboot.log.
Get your IIIF address and password
sudo cat /root/cantaloupe-credentials.txtOnly root can read the file. It holds the service address, this instance's Basic Auth user and password, and the certificate fingerprint:
# Cantaloupe IIIF Image Server - generated on first boot. Unique to this instance; keep it safe.
# IIIF Image API 3: CANTALOUPE_IIIF_BASE<file name>/info.json (API 2: replace /iiif/3/ with /iiif/2/), with HTTP Basic
# Auth (CANTALOUPE_USERNAME / CANTALOUPE_PASSWORD). Images are served from /var/lib/cantaloupe/images. Compare
# CANTALOUPE_TLS_SHA256 with the certificate your client sees (or install your own certificate: see the guide). CANTALOUPE_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/ CANTALOUPE_IIIF_BASE=https://ec2-203-0-113-10.compute-1.amazonaws.com/iiif/3/ CANTALOUPE_USERNAME=admin CANTALOUPE_PASSWORD=<CANTALOUPE_PASSWORD> CANTALOUPE_TLS_SHA256=3F:76:70:FE:F2:5D:BA:37:A1:D4:BE:C7:50:FE:41:07:55:49:49:09:EE:B1:79:14:65:FB:82:3C:4A:E7:2C:E3To see everything except the password:
sudo grep -E '^CANTALOUPE_(URL|IIIF_BASE|USERNAME|TLS_SHA256)=' /root/cantaloupe-credentials.txtCANTALOUPE_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/ CANTALOUPE_IIIF_BASE=https://ec2-203-0-113-10.compute-1.amazonaws.com/iiif/3/ CANTALOUPE_USERNAME=admin CANTALOUPE_TLS_SHA256=3F:76:70:FE:F2:5D:BA:37:A1:D4:BE:C7:50:FE:41:07:55:49:49:09:EE:B1:79:14:65:FB:82:3C:4A:E7:2C:E3You can confirm the whole path from the instance itself. The helper fetches info.json and renders JPEG, PNG and JPEG 2000 derivatives through nginx over HTTPS with the certificate pinned. It also checks that all of these requests are refused: requests without the password, requests with a wrong password, writes, upscaling, paths outside the images directory, and remote URLs.
sudo /usr/local/sbin/cantaloupe-verify.shCANTALOUPE_ROUNDTRIP_OK (IIIF 3 + 2 over HTTPS: info.json ids on the configured host, JPEG + JPEG 2000 renders; unauth, wrong password, writes, upscaling, traversal and remote identifiers refused)Change the password with cantaloupe-set-password. With no argument, it generates a new 24-character password. With -, it reads yours from standard input (at least 16 printable characters, no spaces, :, " or backslash).
It never accepts the password as an argument, so the password does not appear in the process list or your shell history. It updates /root/cantaloupe-credentials.txt, and nginx refuses the old password as soon as the command returns:
$
sudo cantaloupe-set-password cantaloupe-set-password: password replaced (sudo cat /root/cantaloupe-credentials.txt)To choose your own password:
$ read -rs NEWPW && printf '%s\n' "$NEWPW" | sudo cantaloupe-set-password - ; unset NEWPW cantaloupe-set-password: password replaced (sudo cat /root/cantaloupe-credentials.txt)Request images with the IIIF Image API
The image comes with two demo images in /var/lib/cantaloupe/images: sample.jpg and sample.jp2. They are the same 2400 x 1600 picture as JPEG and JPEG 2000.
This step uses curl on the instance. From your own machine, the requests are the same, with the address from Step 4.
First, create a private work directory in memory (/dev/shm), a copy of the instance's certificate, and a curl configuration file that holds the password. This keeps the password off the command line and out of your shell history:
install -d -m 0700 /dev/shm/iiif-demo && cd /dev/shm/iiif-demo
sudo sed -n 's/^CANTALOUPE_IIIF_BASE=//p' /root/cantaloupe-credentials.txt > base cp /etc/ssl/cantaloupe/cantaloupe.crt server.crt ( umask 077
sudo sed -n 's/^CANTALOUPE_PASSWORD=\(.*\)$/user = "admin:\1"/p' /root/cantaloupe-credentials.txt > iiif.cfg printf 'cacert = "server.crt"\nsilent\nshow-error\n' >> iiif.cfg ) ls -l iiif.cfg | awk '{print $1, $9}'; cat base-rw------- iiif.cfg https://ec2-203-0-113-10.compute-1.amazonaws.com/iiif/3/The image information document. An image's IIIF address is the base followed by its file name. info.json gives its size, the ready-made sizes and the tile layout a viewer uses. Its id is always built from the instance's configured name (Step 9), never from what the request claims.
cd /dev/shm/iiif-demo curl -K iiif.cfg -o info.json -w '%{http_code} %{content_type}\n' "$(cat base)sample.jpg/info.json" python3 -c 'import json; d = json.load(open("info.json")); print(d["id"]); print(d["type"], d["profile"], d["width"], "x", d["height"]); print("sizes:", [s["width"] for s in d["sizes"]]); print("tiles:", d["tiles"])'200 application/ld+json;charset=UTF-8;profile="http://iiif.io/api/image/3/context.json" https://ec2-203-0-113-10.compute-1.amazonaws.com/iiif/3/sample.jpg ImageService3 level2 2400 x 1600 sizes: [150, 300, 600, 1200, 2400] tiles: [{'width': 512, 'height': 512, 'scaleFactors': [1, 2, 4, 8, 16]}]Derivatives. A IIIF image request is {base}{file}/{region}/{size}/{rotation}/{quality}.{format}. These requests ask for the whole image 600 pixels wide, the right half at full size as PNG, a 300 x 300 grey square rotated by 90 degrees, and one 512-pixel tile:
cd /dev/shm/iiif-demo B="$(cat base)sample.jpg" curl -K iiif.cfg -o full-600.jpg -w '%{http_code} %{content_type}\n' "$B/full/600,/0/default.jpg" curl -K iiif.cfg -o region.png -w '%{http_code} %{content_type}\n' "$B/1200,0,1200,800/max/0/default.png" curl -K iiif.cfg -o square-gray.jpg -w '%{http_code} %{content_type}\n' "$B/square/300,300/90/gray.jpg" curl -K iiif.cfg -o tile.jpg -w '%{http_code} %{content_type}\n' "$B/0,0,1024,1024/512,512/0/default.jpg" file -b full-600.jpg region.png square-gray.jpg tile.jpg | cut -d, -f1-2,8200 image/jpeg 200 image/png 200 image/jpeg 200 image/jpeg JPEG image data, JFIF standard 1.02, 600x400 PNG image data, 1200 x 800 JPEG image data, JFIF standard 1.02, 300x300 JPEG image data, JFIF standard 1.02, 512x512IIIF Image API 2 clients (older viewers and manifests) use the /iiif/2/ base. The document then carries @id and the API 2 profile:
cd /dev/shm/iiif-demo curl -K iiif.cfg -o info2.json -w '%{http_code}\n' "$(cat base | sed 's#/iiif/3/#/iiif/2/#')sample.jpg/info.json" python3 -c 'import json; d = json.load(open("info2.json")); print(d["@id"]); print(d["profile"][0], d["width"], "x", d["height"])'200 https://ec2-203-0-113-10.compute-1.amazonaws.com/iiif/2/sample.jpg http://iiif.io/api/image/2/level2.json 2400 x 1600JPEG 2000 images are decoded with OpenJPEG (opj_decompress) and served the same way:
cd /dev/shm/iiif-demo curl -K iiif.cfg -o jp2.json -w '%{http_code}\n' "$(cat base)sample.jp2/info.json" python3 -c 'import json; d = json.load(open("jp2.json")); print(d["id"], d["width"], "x", d["height"])' curl -K iiif.cfg -o jp2-tile.jpg -w '%{http_code} %{content_type}\n' "$(cat base)sample.jp2/0,0,1200,800/600,/0/default.jpg"200 https://ec2-203-0-113-10.compute-1.amazonaws.com/iiif/3/sample.jp2 2400 x 1600 200 image/jpegWhat is refused. The server refuses a request with no password, upscaling beyond the original size, a file that is not in the images directory, a remote URL given as the identifier, and any method except GET and HEAD:
cd /dev/shm/iiif-demo B="$(cat base)sample.jpg" curl -s --cacert server.crt -o /dev/null -w 'no password: %{http_code}\n' "$B/info.json" curl -K iiif.cfg -o /dev/null -w 'upscaled (^pct:200): %{http_code}\n' "$B/full/%5Epct:200/0/default.jpg" curl -K iiif.cfg -o /dev/null -w 'not in the images directory: %{http_code}\n' "$(cat base)no-such-image.jpg/info.json" curl -K iiif.cfg -o /dev/null -w 'a remote URL as the identifier: %{http_code}\n' "$(cat base)https:%2F%2Fexample.com%2Fa.jpg/info.json" curl -K iiif.cfg -o /dev/null -w 'POST: %{http_code}\n' -X POST "$B/info.json"no password: 401 upscaled (^pct:200): 403 not in the images directory: 404 a remote URL as the identifier: 404 POST: 405Add your own images
Copy your images into /var/lib/cantaloupe/images. The cantaloupe account must be able to read them. Root-owned files with mode 0644 (directories 0755) are the simplest option.
Cantaloupe reads JPEG, PNG, TIFF (including tiled and pyramidal TIFF), GIF, BMP and JPEG 2000. An image's identifier is its path below the directory. Address a file in a subdirectory with the slash encoded as %2F.
For example, copy files from your own machine:
scp -i <your-key.pem> my-photo.jpg ubuntu@<public-ip>:/tmp/ ssh -i <your-key.pem> ubuntu@<public-ip> 'sudo install -o root -g root -m 0644 /tmp/my-photo.jpg /var/lib/cantaloupe/images/ && rm /tmp/my-photo.jpg'This example copies the demo image under two new names, one in a subdirectory. It requests both and removes them again:
sudo install -o root -g root -m 0644 /var/lib/cantaloupe/images/sample.jpg /var/lib/cantaloupe/images/my-photo.jpg
sudo install -d -o root -g root -m 0755 /var/lib/cantaloupe/images/collection-a
sudo install -o root -g root -m 0644 /var/lib/cantaloupe/images/sample.jpg /var/lib/cantaloupe/images/collection-a/page-001.jpg cd /dev/shm/iiif-demo curl -K iiif.cfg -o /dev/null -w 'my-photo.jpg: %{http_code}\n' "$(cat base)my-photo.jpg/info.json" curl -K iiif.cfg -o /dev/null -w 'collection-a/page-001.jpg: %{http_code}\n' "$(cat base)collection-a%2Fpage-001.jpg/info.json"
sudo rm -r /var/lib/cantaloupe/images/my-photo.jpg /var/lib/cantaloupe/images/collection-a ls /var/lib/cantaloupe/imagesmy-photo.jpg: 200 collection-a/page-001.jpg: 200 README.txt sample.jp2 sample.jpgNew files are served at once, and there is nothing to restart.
For large images, convert them to tiled pyramidal TIFF or JPEG 2000 first, for example with vips on your own machine. Cantaloupe then reads only the tiles and resolution level it needs, instead of decoding the whole image for every request.
You can delete the demo images once your own are in place.
Using it from viewers and applications
Anything that speaks the IIIF Image API can use the instance. This includes IIIF Presentation manifests, where you point each canvas's image service at https://<host>/iiif/3/<file>, and viewers such as Mirador, Universal Viewer and OpenSeadragon.
Every request needs the Basic Auth password, so the client has to be able to send it:
- Server-side applications (a manifest generator, a thumbnail job, your own web application's back end) send the password directly, as
curldoes above. This is the simplest and safest pattern. Keep 443 open only to those servers. - Browser viewers on your own site cannot send a Basic Auth password with the cross-origin requests a viewer makes for
info.json, and you should not put the password in a public web page. Serve the IIIF requests through a back end of yours, such as a reverse proxy that adds theAuthorizationheader for your users. Keep this instance's 443 open only to that back end.
Cantaloupe adds Access-Control-Allow-Origin: * to its IIIF responses, as the IIIF specification recommends. A viewer on another site therefore works once it reaches the API through such a proxy.
Limits and caching
These limits apply:
- Size of a max request: 10,000,000 pixels, and a larger image is scaled down to fit (max_pixels in /etc/cantaloupe/cantaloupe.properties)
- Upscaling: refused with 403, so an explicit size is at most the original size (max_scale = 1.0)
- Request rate per client address: 50 per second, with bursts of 200 (limit_req in /etc/nginx/conf.d/sac-cantaloupe.conf)
- Concurrent connections per client address: 32 (limit_conn)
- Methods: GET and HEAD (the IIIF location in the nginx site)
A client over the rate limit gets 429 Too Many Requests. This example sends 400 full-size requests at once (64 in parallel) from one address:
cd /dev/shm/iiif-demo P="$(cat base)sample.jpg/full/max/0/default.jpg" for i in $(seq 1 400); do echo "url = \"$P\""; echo "output = /dev/null"; echo 'write-out = "%{http_code}\n"'; done > burst.cfg curl -K iiif.cfg --parallel --parallel-max 64 -K burst.cfg | sort | uniq -c37 200 363 429The exact split depends on timing. An IIIF viewer that requests tiles as you pan and zoom stays well inside the limits.
Responses carry Cache-Control: max-age=2592000, private, so browsers and your own proxy may keep derivatives for 30 days. Cantaloupe keeps no server-side derivative cache on disk, and renders each request from the source image.
These are the important settings as shipped:
sudo grep -E '^(source\.static|source\.delegate|delegate_script\.enabled|FilesystemSource\.BasicLookupStrategy\.path_prefix|max_pixels|max_scale|endpoint\.(admin|api|health|iiif\.1)\.enabled|http\.host|http\.port|cache\.server\.derivative\.enabled|print_stack_trace_on_error_pages) ' /etc/cantaloupe/cantaloupe.propertieshttp.host = 127.0.0.1 http.port = 8182 max_pixels = 10000000 max_scale = 1.0 print_stack_trace_on_error_pages = false delegate_script.enabled = false endpoint.iiif.1.enabled = false endpoint.admin.enabled = false endpoint.api.enabled = false endpoint.health.enabled = false source.static = FilesystemSource source.delegate = false FilesystemSource.BasicLookupStrategy.path_prefix = /var/lib/cantaloupe/images/ cache.server.derivative.enabled = falseTo change a Cantaloupe setting, edit the file (it is root:cantaloupe, mode 0640) and run sudo systemctl restart cantaloupe.
Turning on a remote source (HttpSource, S3Source, AzureStorageSource), the delegate script or the admin endpoints undoes protections described in this guide. The systemd sandbox also blocks Cantaloupe's outbound network traffic, so a remote source would need that changed too.
When you are done with the examples, remove the work directory:
rm -rf /dev/shm/iiif-demoYour own host name and certificate
On every boot, cantaloupe-set-host works out the name to serve. It uses the name you set, or else the instance's public DNS name from the instance metadata service (IMDSv2). Every IIIF id that Cantaloupe writes is built from that configured name, never from the request:
systemctl is-enabled cantaloupe-set-host.service
sudo cantaloupe-set-hostname --bootenabled cantaloupe-set-hostname: unchanged (ec2-203-0-113-10.compute-1.amazonaws.com)Use your own DNS name. Point a DNS record at the instance. An Elastic IP keeps the address fixed. Then run:
$
sudo cantaloupe-set-hostname images.example.com cantaloupe-set-hostname: the IIIF service is https://images.example.com/iiif/3/ (certificate SHA-256 1D:81:28:66:9E:61:18:CC:D4:CD:CD:7E:3D:8F:97:FC:3F:C9:50:3C:A9:7C:77:2D:34:8F:A4:F1:C6:60:A3:D3)The name is kept in /etc/sac/cantaloupe-hostname for every later boot, and the credentials file is updated. nginx serves that name and redirects the instance's other addresses to it. Every info.json id becomes https://images.example.com/iiif/.... The certificate in place was the one SecureAnyCloud generated, so it was re-created for the new name.
Which certificates are replaced. Only a certificate SecureAnyCloud generated is ever re-created. Its SHA-256 fingerprint is recorded, root-owned, in /var/lib/sac/data/cantaloupe-cert.sha256. A certificate counts as SecureAnyCloud's only while it matches that record:
sudo cat /var/lib/sac/data/cantaloupe-cert.sha256 openssl x509 -in /etc/ssl/cantaloupe/cantaloupe.crt -noout -fingerprint -sha2563F:76:70:FE:F2:5D:BA:37:A1:D4:BE:C7:50:FE:41:07:55:49:49:09:EE:B1:79:14:65:FB:82:3C:4A:E7:2C:E3 sha256 Fingerprint=3F:76:70:FE:F2:5D:BA:37:A1:D4:BE:C7:50:FE:41:07:55:49:49:09:EE:B1:79:14:65:FB:82:3C:4A:E7:2C:E3Install your own certificate, from your CA or from Let's Encrypt with a DNS challenge, by putting the full chain and the key at the same paths. Anything there that does not match the record, including a symlink to a chain you manage elsewhere, is yours and is never replaced, either on boot or when the name changes:
sudo install -m 0644 -o root -g root fullchain.pem /etc/ssl/cantaloupe/cantaloupe.crt
sudo install -m 0600 -o root -g root privkey.pem /etc/ssl/cantaloupe/cantaloupe.key
sudo nginx -t && sudo systemctl reload nginxAfter that, a name change keeps your files:
$
sudo cantaloupe-set-hostname images.example.com cantaloupe-set-hostname: the certificate in /etc/ssl/cantaloupe is not one this helper minted: it is kept unchanged cantaloupe-set-hostname: the IIIF service is https://images.example.com/iiif/3/ (certificate SHA-256 <your certificate's fingerprint>)Go back to a generated certificate by removing both files and running the helper with the name to serve:
$
sudo rm /etc/ssl/cantaloupe/cantaloupe.crt /etc/ssl/cantaloupe/cantaloupe.key $
sudo cantaloupe-set-hostname images.example.comTo return to the instance's own public name on every boot, run sudo rm /etc/sac/cantaloupe-hostname && sudo cantaloupe-set-hostname --boot.
Updates
Unattended security upgrades update Ubuntu packages, including OpenJDK, OpenJPEG and nginx. To update everything now, run sudo apt-get update && sudo apt-get upgrade, then sudo systemctl restart cantaloupe so that Cantaloupe runs on the updated Java.
Cantaloupe itself is not an apt package. It is the release jar at /opt/cantaloupe/cantaloupe-5.0.8.jar, and apt never changes it:
systemctl is-enabled unattended-upgrades apt-daily-upgrade.timer dpkg -S /opt/cantaloupe/cantaloupe-5.0.8.jar 2>&1 || trueenabled enabled dpkg-query: no path found matching pattern /opt/cantaloupe/cantaloupe-5.0.8.jarSecureAnyCloud publishes a new version of this listing when Cantaloupe has a new release or a security fix. To move, launch the new version and copy /var/lib/cantaloupe/images across. Alternatively, attach a snapshot of the old root volume to the new instance and copy from it.
Security notes
- Local images only (no server-side request forgery).
source.static = FilesystemSourcewithsource.delegate = falseand the delegate script off. An identifier is only ever a file name below/var/lib/cantaloupe/images/. A URL, ans3://address or a../path given as the identifier is looked up as a file name and not found (Step 5). - Cantaloupe has no outbound network. Its systemd unit allows IP traffic to the loopback addresses only (
IPAddressAllow=localhost,IPAddressDeny=any), so even a changed configuration could not reach another host or the metadata service. - Instance metadata. systemd denies Cantaloupe and nginx access to the EC2 instance metadata service (169.254.169.254 and its IPv6 address). Launch with IMDSv2 required (
HttpTokens=required), as in Step 1. - Authentication and methods. Every IIIF request and the landing page need the Basic Auth password, and only
GETandHEADreach Cantaloupe. nginx sends Cantaloupe the configured host name and the real client address. It setsX-Forwarded-Forfrom the connection and never passes it through. It also drops client-supplied forwarding headers and strips theAuthorizationandCookieheaders. - Nothing else exposed. The admin control panel, the REST API, the health endpoint and the IIIF 1 endpoint are off. nginx returns
404for every path except/iiif/3/,/iiif/2/, the landing page and/healthz. nginx answers /healthz, for load balancer checks, without the password. Requests for host names the instance does not serve are dropped. - Sandboxed process. Cantaloupe runs as the unprivileged
cantaloupeaccount with a read-only view of the system (it can write only its temporary directory), a private/tmp, no capabilities and a system call filter. It can read its configuration, and nginx's account cannot. - Exact listeners. Only 22, 80 and 443 listen on the network (Step 3). Open them in the security group only to the networks that need them, and use the password over HTTPS only.
The metadata block and the network sandbox, as systemd applies them:
systemctl show cantaloupe nginx -p Id -p IPAddressDeny -p IPAddressAllowIPAddressAllow=127.0.0.0/8 ::1/128 IPAddressDeny=::/0 0.0.0.0/0 Id=cantaloupe.service IPAddressAllow= IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 Id=nginx.serviceBuild provenance and licence
Cantaloupe 5.0.8 is the upstream release (GitHub release v5.0.8, 28 October 2026). The image build downloads the release archive, checks its SHA-512, and extracts only the application jar (checked by SHA-256), the licence, the third-party notices and the sample configuration. The archive's deps/ directory, which holds Kakadu JPEG 2000 libraries under a proprietary licence, is never extracted; JPEG 2000 is decoded with OpenJPEG from Ubuntu instead.
cat /usr/share/doc/sac/cantaloupe-BUILD-INFO.txt sha256sum /opt/cantaloupe/cantaloupe-5.0.8.jar ls /usr/share/doc/cantaloupe/Cantaloupe 5.0.8 on Ubuntu 24.04 (sac AWS bundle member media) release archive https://github.com/cantaloupe-project/cantaloupe/releases/download/v5.0.8/cantaloupe-5.0.8.zip sha512 30cbbc6b6b8e54b6c1d60e66af05e68c304fa75431da51be773d591309e319b2c24f8f61556ba1beb972b0fadc6ed9ca580d7dae7c4f917c4eb83d3e1e87cad4; NCSA licence, LICENSE.txt sha256 51c929fcfed0ed03bd2f55287dec6b2b933469c6875624d319a6ea8f665dc0fc -> /usr/share/doc/cantaloupe/ jar /opt/cantaloupe/cantaloupe-5.0.8.jar sha256 63e2984d6af504b2bb133af5a8df3115614130a914caf145ba1a2c31e3a1c912; third-party notices /usr/share/doc/cantaloupe/LICENSE-3RD-PARTY.txt sha256 ffa5e5fba76ce2e3cfb0b7c529d89c63e10df1d242d30f60d66d8448214e78de not extracted from the archive: deps/ (Kakadu native libraries, proprietary); JPEG 2000 via OpenJpegProcessor + opj_decompress java 21.0.12.1+1-1~24.04.4 (openjdk-21-jre-headless, Ubuntu main) opj_decompress 2.5.0-2ubuntu0.5 (libopenjp2-tools, Ubuntu universe; source openjpeg2 in main) nginx 1.24.0-2ubuntu7.19 (Ubuntu main) built 2026-11-07T09:43:18Z 63e2984d6af504b2bb133af5a8df3115614130a914caf145ba1a2c31e3a1c912 /opt/cantaloupe/cantaloupe-5.0.8.jar LICENSE-3RD-PARTY.txt LICENSE.txt cantaloupe.properties.sample/usr/share/doc/cantaloupe/LICENSE.txt: Cantaloupe's University of Illinois/NCSA Open Source License.
/usr/share/doc/cantaloupe/LICENSE-3RD-PARTY.txt: the notices of the libraries bundled in the jar.
/usr/share/doc/cantaloupe/cantaloupe.properties.sample: upstream's full configuration reference, with every key documented.
Libraries in the jar. A vulnerability scan of the jar lists advisories in bundled libraries that are not reachable in this configuration: Netty and the cloud storage clients (used only by the S3 and Azure sources and caches, which are off and could not connect out anyway), Jackson's JSON parser (no client-supplied JSON is parsed: the API, the delegate and the derivative cache are off), Guava (no Java deserialization of client data) and build-time libraries. SecureAnyCloud publishes a new version when Cantaloupe ships updated libraries.
Cantaloupe's source: github.com/cantaloupe-project/cantaloupe (tag v5.0.8). Cantaloupe is an open source project of the University of Illinois at Urbana-Champaign Library; IIIF is a trademark of the IIIF Consortium. SecureAnyCloud is not affiliated with or endorsed by either.
Troubleshooting
401 Unauthorized. The password is wrong or missing; read it again from/root/cantaloupe-credentials.txt. Aftercantaloupe-set-passwordthe old one is refused at once.
404 Not Foundfor an image. The file is not in/var/lib/cantaloupe/images, the name differs in case, a subdirectory slash is not encoded as%2F, or thecantaloupeaccount cannot read the file (sudo -u cantaloupe head -c1 /var/lib/cantaloupe/images/<file> >/dev/null).
403 Forbiddenfor a size. The request asks for more than the original size (^upscaling), which is refused (Step 8).
429 Too Many Requests. A client is over the rate limits (Step 8).
idsininfo.jsonname the wrong host. Set the name clients use withcantaloupe-set-hostname(Step 9).
- Certificate errors. The client does not trust this instance's certificate, or connects with a name the certificate does not carry. Trust
cantaloupe.crtor install your own (Step 9).
- The connection is dropped with no answer. The request named a host the instance does not serve. Use the name in
CANTALOUPE_URL, or set yours withcantaloupe-set-hostname.
- Logs:
journalctl -u cantaloupe,/var/log/nginx/access.logand/var/log/nginx/error.log, and/var/log/sac/cantaloupe-firstboot.log.
Support
SecureAnyCloud images come with 24/7 support. Email sales@secureanycloud.com with your AWS account ID, the instance ID and the output of sudo /usr/local/sbin/cantaloupe-verify.sh. For Cantaloupe itself, see the Cantaloupe user manual and the IIIF Image API.
Deploy on AWS
Launch Cantaloupe IIIF Image Server 5.0.8 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com