MapFish Print on Ubuntu 24.04 on Azure User Guide
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 themapfish-printsystemd 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.serviceas systemd units, enabled and active- An unauthenticated
/healthzendpoint 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.

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

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.

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.

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, runsudo htpasswd -B /etc/nginx/.mapfish-print.htpasswd adminand thensudo systemctl reload nginx. - Print apps: add or edit apps under
/opt/tomcat/webapps/ROOT/print-apps/<app>/, thensudo systemctl restart mapfish-printto 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:8080in/opt/tomcat/conf/server.xml(theConnectorelement'saddress="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 -Xmx1536mby 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.