Skip to content
IIIF & Image Interoperability

To deploy a Cantaloupe IIIF server: install Java 17+, download and unzip the Cantaloupe release, copy the sample properties file and set your source plus cache, start the JAR with an appropriate heap, then front it with NGINX for TLS, CORS and caching. Verify with info.json and the official validator. Cantaloupe is a single self-contained Java application with an embedded web server, which is why it is the default choice for most archives. Here is the full path.

Step 1: Prepare the host and Java

Cantaloupe needs a modern JRE. On a Debian/Ubuntu box:

bash
sudo apt-get update && sudo apt-get install -y openjdk-17-jre-headless
java -version   # confirm 17 or newer

Create a service user and directories so nothing runs as root:

bash
sudo useradd -r -s /usr/sbin/nologin cantaloupe
sudo mkdir -p /opt/cantaloupe /data/masters /var/cache/cantaloupe
sudo chown -R cantaloupe:cantaloupe /var/cache/cantaloupe

Step 2: Install and configure

Download the release, unzip into /opt/cantaloupe, and start from the sample config:

bash
cp cantaloupe.properties.sample /opt/cantaloupe/cantaloupe.properties

The four blocks that matter most:

properties
# 1. Bind locally; the proxy faces the public
http.host = 127.0.0.1
http.port = 8182

# 2. Where masters live and how identifiers map to files
source.static = FilesystemSource
FilesystemSource.BasicLookupStrategy.path_prefix = /data/masters/
FilesystemSource.BasicLookupStrategy.path_suffix = .tif

# 3. Processor: Java2d is the safe default; OpenJpeg for JP2 masters
processor.ManualSelectionStrategy.fallback = Java2dProcessor

# 4. Derivative cache so each tile is computed once
cache.server.derivative = FilesystemCache
FilesystemCache.pathname = /var/cache/cantaloupe
cache.server.derivative.ttl_seconds = 2592000

Step 3: Run it as a managed service

Do not start it by hand in a terminal. Use systemd so it restarts on failure and boot:

ini
# /etc/systemd/system/cantaloupe.service
[Unit]
Description=Cantaloupe IIIF Image Server
After=network.target

[Service]
User=cantaloupe
ExecStart=/usr/bin/java -Xmx4g -Dcantaloupe.config=/opt/cantaloupe/cantaloupe.properties -jar /opt/cantaloupe/cantaloupe-5.0.6.jar
Restart=on-failure

[Install]
WantedBy=multi-user.target

Then sudo systemctl enable --now cantaloupe.

Which processor and heap settings should you pick?

The processor decodes and renders. Java2dProcessor handles TIFF, PNG and JPEG with no native dependencies and is the right default. For JPEG2000 masters, configure OpenJpegProcessor, which calls the openjpeg command-line tools, so install them. Set -Xmx to comfortably exceed the memory needed to hold your largest decoded region; for big manuscript scans several gigabytes is normal. Under-provisioning the heap produces OutOfMemoryError on the largest images, which is the single most common production failure.

How do you put Cantaloupe behind NGINX?

Never expose Cantaloupe directly. NGINX terminates TLS, adds CORS, and caches:

nginx
location /iiif/ {
    proxy_pass http://127.0.0.1:8182;
    add_header Access-Control-Allow-Origin "*" always;
    proxy_cache iiif_cache;
    proxy_cache_valid 200 30d;
}

Because Image API URLs are deterministic, the proxy cache means Cantaloupe renders each derivative only once, even under load.

How do you verify the deployment?

Confirm the core path end to end:

bash
# Metadata endpoint
curl -s https://images.example.org/iiif/3/leaf-12r/info.json | jq '.width, .height'
# A real rendered crop
curl -o test.jpg "https://images.example.org/iiif/3/leaf-12r/full/600,/0/default.jpg"

Then run the official IIIF Image API validator against the identifier. A valid info.json, a rendered crop, and a clean validator report mean you are live.

What pitfalls bite people most?

  • Forgetting CORS, so cross-domain viewers fail silently.
  • Heap too small for the biggest images (OutOfMemoryError).
  • No derivative cache, so the server recomputes identical tiles forever.
  • Exposing Cantaloupe directly to the internet instead of via a proxy.
  • A wrong public base URL, so info.json advertises image URLs nobody can reach.

Key Takeaways

  • Cantaloupe is a single Java 17+ JAR with an embedded web server.
  • Configure source, processor and derivative cache in the properties file.
  • Run it under systemd as a non-root user bound to localhost.
  • Java2dProcessor is the default; use OpenJpegProcessor for JP2 masters.
  • Front it with NGINX for TLS, CORS and aggressive caching of deterministic URLs.
  • Verify with info.json, a real crop, and the official Image API validator.

Frequently Asked Questions

What do I need to run Cantaloupe?

A Java 17+ runtime, the Cantaloupe release ZIP, a properties config file, and a directory or store of source images. It runs as a single self-contained JAR with an embedded web server.

How does Cantaloupe find my images?

Through a configured source. FilesystemSource maps an identifier to a file path; HttpSource fetches over HTTP; S3Source reads from object storage. You pick one and set its lookup strategy in the properties file.

Which processor should I use?

Java2dProcessor is the safe built-in default. For JPEG2000 masters, configure the OpenJpegProcessor (it shells out to the openjpeg tools). TurboJpegProcessor speeds up JPEG handling if the native library is present.

Should Cantaloupe face the internet directly?

No. Run it behind NGINX or a CDN that terminates TLS, sets CORS headers and caches responses. Keep Cantaloupe on localhost or a private network and let the proxy handle the public edge.

How big should the JVM heap be?

Size it to your largest images and concurrency. For very large masters, several gigabytes via -Xmx is common. Too small a heap causes OutOfMemoryError on big regions; monitor and tune under real load.

How do I confirm the deployment works?

Request an identifier's info.json and a sample image URL, then run the official IIIF Image API validator. A valid info.json and a rendered crop mean the core path is working.