Applications Azure

MapFish Print on Ubuntu 24.04 on Azure User Guide

| Product: MapFish Print on Ubuntu 24.04 LTS on Azure

Overview

MapFish Print is an open source Java service that renders printable map and report documents entirely server side. A client, such as a GIS portal, an OpenLayers or GeoMapFish application, or a script, sends a JSON print specification describing the layout, the map center and layers, and any titles or legends, and MapFish Print returns a finished PDF, PNG, JPEG, BMP or TIFF built with the JasperReports rendering engine. It has powered production cartographic printing for GIS platforms for over a decade and has no interactive UI of its own: it is a headless backend REST API that other applications call. The cloudimg image runs the official print-servlet 4.0.6 WAR on Apache Tomcat 11 with OpenJDK 21, the exact combination upstream ships in its own official Docker image. MapFish Print has no accounts of its own, so nginx adds a per-VM HTTP Basic Auth gate in front of it. Backed by 24/7 cloudimg support.

What is included:

  • MapFish Print 4.0.6 (official print-servlet.war) deployed on Apache Tomcat 11.0.23, running as the mapfish-print systemd service
  • OpenJDK 21 (never Oracle JDK)
  • The print REST API on :80, fronted by nginx, with Tomcat bound to loopback only
  • Per-VM HTTP Basic Auth (user admin) protecting the entire API, with a unique password generated on first boot
  • One working example print app (examples) bundled out of the box: a JasperReports layout with a local, self contained GeoJSON world countries map layer, so you can generate a real PDF with a single API call the moment the VM boots, no external map server required
  • mapfish-print.service + nginx.service as systemd units, enabled and active
  • An unauthenticated /healthz endpoint for Azure Load Balancer health probes
  • 24/7 cloudimg support

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 comfortable starting point for the bundled example app and typical single map report jobs; move to a larger size if you print large, high DPI or multi map reports. NSG inbound: allow 22/tcp from your management network and 80/tcp for the print API. MapFish Print serves plain HTTP on port 80; for production use, terminate TLS in front of it with your own domain and restrict access to trusted IP ranges (see Maintenance).

Step 1 - Deploy from the Azure Marketplace

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

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name mapfish-print \
  --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

az vm open-port --resource-group <your-rg> --name mapfish-print --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

systemctl is-active mapfish-print.service nginx.service
sudo ss -tln | grep -E '127.0.0.1.*:8080 |:80 '

Both services report active. MapFish Print (Tomcat) is bound to the loopback address 127.0.0.1:8080 only; nginx fronts it on port 80 and is the only path in from the network.

The mapfish-print and nginx services active, Tomcat listening on loopback 127.0.0.1:8080 only, and nginx on public port 80

Step 5 - Retrieve your API password

nginx protects the entire print API with HTTP Basic Auth. The username is admin and a unique password is generated on the first boot of your VM and written to a root only file:

sudo cat /root/mapfish-print-credentials.txt

This file contains MAPFISH_PRINT_USERNAME, MAPFISH_PRINT_PASSWORD and the MAPFISH_PRINT_URL. The password is stored on disk only as a bcrypt hash in /etc/nginx/.mapfish-print.htpasswd, so no plaintext password ships in the image. Store the password somewhere safe.

You can also confirm the deployed version, the JVM, the loopback only Tomcat connector, and the bundled example print app's layout definition:

grep Mapfish-Print-Version /opt/tomcat/webapps/ROOT/META-INF/MANIFEST.MF
java -version
cat /opt/tomcat/webapps/ROOT/print-apps/examples/config.yaml

The deployed MapFish Print version 4.0.6, the OpenJDK 21 runtime, the Tomcat connector patched to loopback only, and the bundled examples print app config.yaml defining the A4 portrait layout

Step 6 - Confirm the health endpoint

nginx serves an unauthenticated health endpoint for load balancers and probes:

curl -s http://localhost/healthz

It returns ok. This endpoint never requires authentication, so it is safe for an Azure Load Balancer health probe.

Step 7 - Confirm authentication and list the print apps

Because a password is set on first boot, an unauthenticated request to the print API returns HTTP 401, so nobody can generate a print job without the password. The following reads the per-VM password from the credentials file and proves the round trip: unauthenticated is rejected, a wrong password is rejected, and the correct password authenticates and lists the registered print apps.

PW=$(sudo grep '^MAPFISH_PRINT_PASSWORD=' /root/mapfish-print-credentials.txt | cut -d= -f2-)
echo "unauth  : $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/print/apps.json)"
echo "wrongpw : $(curl -s -o /dev/null -w '%{http_code}' -u admin:wrong-pw http://127.0.0.1/print/apps.json)"
echo "authed  : $(curl -s -o /dev/null -w '%{http_code}' -u admin:$PW http://127.0.0.1/print/apps.json)"
curl -s -u admin:$PW http://127.0.0.1/print/apps.json

It prints unauth : 401, wrongpw : 401, authed : 200, then a JSON array listing the registered print apps, including examples, the bundled sample app.

The Basic Auth round trip returning 401 unauthenticated, 401 for a wrong password, 200 with the per-VM password, the registered print apps including examples, and the unauthenticated /healthz endpoint returning ok

Step 8 - Generate your first PDF

The bundled examples print app renders an A4 portrait report with a title and a world map built entirely from a local GeoJSON layer shipped in the image, so it needs no external map server. buildreport is the synchronous endpoint: it returns the finished document directly in the response body. Post the bundled sample specification and save the result:

PW=$(sudo grep '^MAPFISH_PRINT_PASSWORD=' /root/mapfish-print-credentials.txt | cut -d= -f2-)
curl -u admin:$PW -H 'Content-Type: application/json' \
  -d @/etc/mapfish-print/sample-request.json \
  http://127.0.0.1/print/examples/buildreport.pdf -o report.pdf
file report.pdf

The request returns HTTP 200 and report.pdf is a genuine multi page PDF document, not just a successful status code. Copy it off the VM with scp to view it, or point your own client at http://<vm-public-ip>/print/examples/buildreport.pdf with the same credentials.

Posting the bundled sample print spec to the buildreport.pdf endpoint returning HTTP 200, and the file command confirming a genuine PDF document was produced, not merely a 200 status

Step 9 - Explore the print API further

MapFish Print also exposes capabilities, asynchronous job submission, and status polling for larger reports:

PW=$(sudo grep '^MAPFISH_PRINT_PASSWORD=' /root/mapfish-print-credentials.txt | cut -d= -f2-)
# Describe the layouts, attributes and output formats a print app supports
curl -s -u admin:$PW http://127.0.0.1/print/examples/capabilities.json
# Submit an asynchronous job (returns a reference ID + status/download URLs instead of blocking)
curl -s -u admin:$PW -H 'Content-Type: application/json' \
  -d @/etc/mapfish-print/sample-request.json \
  http://127.0.0.1/print/examples/report.pdf

Use the synchronous buildreport.<format> endpoint from Step 8 for small, fast reports and interactive use. Use the asynchronous report.<format> plus status/<ref>.json plus report/<ref> flow for larger jobs where you do not want the client to block on the HTTP request.

Step 10 - Add your own print app

Add a new directory under /opt/tomcat/webapps/ROOT/print-apps/<your-app>/ containing your own config.yaml and JasperReports .jrxml template (the same layout used by the bundled examples app is a good starting point), then restart the service so MapFish Print picks it up:

PW=$(sudo grep '^MAPFISH_PRINT_PASSWORD=' /root/mapfish-print-credentials.txt | cut -d= -f2-)
sudo systemctl restart mapfish-print
# Tomcat takes a few seconds to redeploy the webapp after a restart; poll until it answers.
for i in $(seq 1 20); do
  curl -s -o /dev/null -w '%{http_code}' -u admin:$PW http://127.0.0.1/print/apps.json | grep -q 200 && break
  sleep 2
done
curl -s -u admin:$PW http://127.0.0.1/print/apps.json

Your new app id (the directory name) now appears alongside examples, and you can buildreport against it exactly as in Step 8. See the MapFish Print configuration documentation for the full attribute, layer, processor and JasperReports reference.

Maintenance

  • Password: the API password is set on first boot and stored as a bcrypt entry in /etc/nginx/.mapfish-print.htpasswd. To change it, run sudo htpasswd -B /etc/nginx/.mapfish-print.htpasswd admin and then sudo systemctl reload nginx.
  • Print apps: add or edit apps under /opt/tomcat/webapps/ROOT/print-apps/<app>/, then sudo systemctl restart mapfish-print to pick up changes.
  • Restrict access: MapFish Print serves plain HTTP on port 80. For production, restrict access to trusted IP ranges in your Network Security Group, and front it with TLS (for example certbot with your own domain) terminating on :443.
  • Loopback binding: Tomcat is bound to 127.0.0.1:8080 in /opt/tomcat/conf/server.xml (the Connector element's address="127.0.0.1" attribute), so nginx is the only path in. Keep it that way - do not change the connector to a public address.
  • Heap size: the JVM heap is set in /opt/tomcat/bin/setenv.sh (-Xms256m -Xmx1536m by default, sized for Standard_B2s). Increase it if you print large, high DPI or multi map reports and see out of memory errors in /opt/tomcat/logs/catalina.out.
  • Logs: Tomcat and MapFish Print logs live under /opt/tomcat/logs/catalina.out.
  • Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.

Support

cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.