QGIS Server
Overview
QGIS Server is the map server of the QGIS project. It reads a QGIS project file and publishes the layers inside it as standards-compliant OGC web services: WMS 1.3.0 and 1.1.1 for rendered map images, WFS for vector features, WCS for coverages and WMTS for cached tiles. QGIS Server shares its rendering engine with QGIS on the desktop. The symbology, labelling and layer order that a cartographer designed are exactly what clients receive over the web, with no second styling step.
QGIS Server is a FastCGI application, and it has no authentication and no user accounts of its own. Published as-is on a public IP, every layer of every project is readable by anyone. The SecureAnyCloud image closes that gap by design. QGIS Server runs as two persistent FastCGI worker processes that listen only on local unix sockets. nginx on port 80 is the sole network listener and the security boundary. Every OGC request, the bundled map viewer and its assets sit behind HTTP Basic Auth (user admin), and the password is generated uniquely on the first boot of your VM.
QGIS Server is installed from the official QGIS Long Term Release apt repository, so the runtime stays patchable with apt upgrade instead of being a source build that you can never update. A working sample project and dataset is published on a dedicated Azure data disk at /srv/qgis. SecureAnyCloud authored its synthetic geometry, so the licensing is unambiguous. The image is backed by 24/7 SecureAnyCloud support.
What you get:
- QGIS Server 3.44 LTR installed from the official QGIS apt repository (
qgis.org/ubuntu-ltr), and verified at build time to have come from that repository. - Two persistent FastCGI workers (
qgis-server@1,qgis-server@2) supervised byspawn-fcgi, sized at one per vCPU for the recommended Standard_B2s. - OGC services (WMS, WFS, WCS and WMTS) on
/ogc/, fronted by nginx, with the workers reachable only over local unix sockets. - Per-VM HTTP Basic Auth (user
admin) that protects the OGC endpoint and the viewer, with a unique password generated on first boot. - A bundled Leaflet map viewer at
/with a live WMS legend and click-to-identify GetFeatureInfo, served locally with no CDN dependency. - A working sample QGIS project and GeoPackage, created by SecureAnyCloud, on a dedicated Azure data disk mounted at /srv/qgis.
/usr/local/sbin/qgis-server-selftest.sh, an end-to-end OGC check that you can re-run at any time.- An unauthenticated
/healthzendpoint for Azure Load Balancer health probes. - 24/7 SecureAnyCloud support.
Prerequisites
- An active Azure subscription, an SSH key pair, and a VNet and subnet in the target region.
- Standard_B2s (2 vCPU, 4 GiB RAM) is the recommended size, and the two rendering workers are tuned for it. Scale up if you publish large raster projects or expect heavy concurrent tile traffic.
- Network Security Group inbound rules: allow
22/tcpfrom your management network,80/tcpfor the OGC endpoint and viewer, and443/tcpif you add TLS. - The appliance serves plain HTTP on port 80. For production, 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 QGIS Server by SecureAnyCloud, and select Create.
On the Basics tab, 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 the dedicated data disk on the Disks tab. Then choose Review + create and Create.
Step 2 - Deploy from the Azure CLI
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:qgis-server-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name qgis-server \
--image sac1647283583153:qgis-server-ubuntu-24-04:default:latest \
--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 qgis-server --port 80 --priority 1010Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>Step 4 - Confirm the services are running
systemctl is-active qgis-server@1.service qgis-server@2.service nginx.service ls -l /run/qgis-server/ findmnt -no SOURCE,TARGET,FSTYPE,SIZE /srv/qgisAll three services report active. The two FastCGI workers listen on the unix sockets qgis-1.sock and qgis-2.sock, owned by qgis:www-data with mode 0660, so only the nginx worker process can reach them. QGIS Server has no TCP port of its own, so nginx cannot be bypassed. Your published projects and their data live on the dedicated Azure data disk mounted at /srv/qgis.
Step 5 - Confirm the QGIS version and the sample project
cat /var/lib/sac/config/sac-qgis-server-version apt-cache policy qgis-server | head -3 ls -l /srv/qgis/projects /srv/qgis/data ogrinfo -so /srv/qgis/data/sac-sample.gpkg sac_regions | head -8The version file records the exact QGIS Server build in this image. apt-cache policy shows that it was installed from qgis.org/ubuntu-ltr, the project's own Long Term Release repository. QGIS Server is an ordinary apt package here, so security updates arrive through apt upgrade like everything else on the system.
The sample project sac-sample.qgs and its GeoPackage sit on the data disk. ogrinfo reports the sac_regions layer with its nine features.
Step 6 - Retrieve your password
nginx protects the OGC endpoint and the viewer with HTTP Basic Auth. The username is admin. A unique password is generated on the first boot of your VM and written to a file that only root can read:
sudo cat /root/qgis-server-credentials.txtThis file contains QGIS_SERVER_USERNAME, QGIS_SERVER_PASSWORD, the QGIS_SERVER_URL to open in a browser and the QGIS_SERVER_OGC_URL to give to OGC clients. The password is stored on disk only as a bcrypt hash in /etc/nginx/.qgis-server.htpasswd. No plaintext password ships in the image, and no two VMs share a login. Store the password somewhere safe.
Step 7 - Confirm the health endpoint
curl -s http://localhost/healthzThe endpoint returns ok. It is deliberately the only unauthenticated path on the VM, so you can use it for an Azure Load Balancer health probe without exposing any map data.
Step 8 - Confirm the authentication gate
QGIS Server has no access control of its own, so the nginx gate is what stands between your data and the internet. The following commands read the per-VM password from the credentials file and prove the round trip. An unauthenticated request is rejected, a wrong password is rejected, and only the correct password reaches the service:
PW=$(sudo grep '^QGIS_SERVER_PASSWORD=' /root/qgis-server-credentials.txt | cut -d= -f2-) CAPQ='SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities' echo "unauth : $(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1/ogc/?$CAPQ")" echo "wrongpw : $(curl -s -o /dev/null -w '%{http_code}' -u admin:wrong-pw "http://127.0.0.1/ogc/?$CAPQ")" echo "authed : $(curl -s -o /dev/null -w '%{http_code}' -u admin:$PW "http://127.0.0.1/ogc/?$CAPQ")"It prints unauth : 401, wrongpw : 401 and authed : 200. The same gate covers GetMap, GetFeatureInfo, WFS and the viewer, not just GetCapabilities, so there is no unauthenticated path to any layer.
Step 9 - Confirm a real OGC round-trip
A map server that returns HTTP 200 does not necessarily draw anything, because a blank tile is still a valid PNG. The image ships a self-test that checks the content, not just the status code. It asserts that:
- GetCapabilities advertises both sample layers by name.
- A GetMap request returns a PNG that is genuinely not blank.
- GetFeatureInfo identifies a feature, and WFS returns features.
- Unauthenticated and wrong-password requests are refused.
Run the self-test:
sudo /usr/local/sbin/qgis-server-selftest.shThe self-test prints OK. If anything is wrong, it prints BROKEN-<reason> instead and exits non-zero, so you can wire it into your own monitoring. You can also see the evidence by hand:
PW=$(sudo grep '^QGIS_SERVER_PASSWORD=' /root/qgis-server-credentials.txt | cut -d= -f2-) Q='SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=sac_regions,sac_sites&STYLES=&CRS=EPSG:3857&WIDTH=800&HEIGHT=800&FORMAT=image/png' curl -s -o /tmp/map.png -u admin:$PW "http://127.0.0.1/ogc/?$Q&BBOX=-334000,6106000,668000,7761000" file /tmp/map.png gdalinfo -stats /tmp/map.png | grep -E 'Size is|StdDev'file reports an 800x800 PNG, and gdalinfo reports a non-zero standard deviation on each band. This proves that the pixels vary, so a map was drawn. If you render the same request over an area where the project has no data, every band reports StdDev=0.000, which is what a blank tile looks like.
Step 10 - Open the map viewer
Browse to http://<vm-public-ip>/. Your browser prompts for a username and password. Enter admin and the password from Step 6.
The bundled viewer loads the sample project through WMS and draws it. QGIS Server generates the legend live from the project's own symbology using GetLegendGraphic, so the legend always matches whatever project you publish. Leaflet is served from the VM itself, not from a CDN, so the viewer works on locked-down networks with no outbound internet access.
Step 11 - Zoom in and watch the server re-render
Use the + control or the scroll wheel to zoom in. Every pan and zoom issues fresh WMS GetMap requests, and QGIS Server re-renders the layers at the new scale from the source data. This is server-side rendering, not a pre-cut tile cache, so scale-dependent styling and labelling behave exactly as they do in QGIS Desktop.
Step 12 - Identify a feature with GetFeatureInfo
Click anywhere on the map. The viewer issues a WMS GetFeatureInfo request, the standard OGC identify operation, and shows the attributes of the feature under your cursor. QGIS Desktop, OpenLayers and any other OGC client make the same request, so if identify works here, it works for your clients too.
Step 13 - Connect QGIS Desktop and other OGC clients
Any OGC client can consume this server. In QGIS Desktop, choose Layer > Add Layer > Add WMS/WMTS Layer. Create a new connection with the URL http://<vm-public-ip>/ogc/, set the authentication to Basic with username admin and your per-VM password, then connect and add the layers. The same URL works for Add WFS Layer.
Browsing the capabilities document shows exactly what any client sees: the service metadata, the supported operations and formats, and the published layers.
PW=$(sudo grep '^QGIS_SERVER_PASSWORD=' /root/qgis-server-credentials.txt | cut -d= -f2-)
curl -s -u admin:$PW 'http://127.0.0.1/ogc/?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities' | grep -oE '<Name>[^<]*</Name>'
curl -s -u admin:$PW 'http://127.0.0.1/ogc/?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=sac_regions&MAXFEATURES=1&OUTPUTFORMAT=GeoJSON'The first command lists the published layer names, and the second returns a GeoJSON feature over WFS. The capabilities document advertises itself at whatever address you used to reach it, because the appliance derives the address from the request. No hostname is baked into the project, so there is nothing to reconfigure when you put the VM behind a domain or load balancer.
Step 14 - Publish your own project
The sample project makes the server useful out of the box. Replace it with your own. Copy your .qgs (or .qgz) project and all of its data onto the data disk, point the appliance at it, and reload:
# from your workstation scp -r ./my-project azureuser@<vm-public-ip>:/tmp/
# on the VM
sudo mv /tmp/my-project /srv/qgis/projects/
sudo chown -R qgis:www-data /srv/qgis
sudo sed -i 's|fastcgi_param QGIS_PROJECT_FILE .*|fastcgi_param QGIS_PROJECT_FILE /srv/qgis/projects/my-project/my-project.qgs;|' \
/etc/nginx/sites-available/sac-qgis
sudo nginx -t && sudo systemctl reload nginx
sudo systemctl restart qgis-server@1 qgis-server@2Use absolute paths inside the project, or relative paths with the data stored alongside the .qgs file, so the workers can resolve every layer.
In QGIS Desktop, open Project > Properties > QGIS Server to set the WMS service title, the advertised extent, the CRS list and which layers are published over WFS and WCS. QGIS Server reads those settings straight from the project file. /usr/local/lib/sac/make-sample-project.py shows the same settings applied programmatically with PyQGIS, if you would rather generate projects than author them by hand.
Maintenance
- Password: the password is set on first boot and stored as a bcrypt entry in
/etc/nginx/.qgis-server.htpasswd. To change it, runsudo htpasswd -B /etc/nginx/.qgis-server.htpasswd adminand thensudo systemctl reload nginx. - Restrict access: the appliance serves plain HTTP on port 80. For production, restrict port 80 to trusted IP ranges in your Network Security Group and front it with TLS terminating on
:443, for example with certbot and your own domain. HTTP Basic Auth sends the password with every request, so TLS is strongly recommended on any network you do not fully control. - Scaling the workers: each worker holds the rendering stack plus the cached project in memory, so the image runs two of them, one per vCPU on Standard_B2s. On a larger VM, add instances (
sudo systemctl enable --now qgis-server@3). Then add a matchingserver unix:/run/qgis-server/qgis-3.sock max_fails=3 fail_timeout=10s;line to theupstream qgisserverblock in/etc/nginx/sites-available/sac-qgis, and reload nginx. - Tuning rendering:
QGIS_SERVER_MAX_THREADS,QGIS_SERVER_PARALLEL_RENDERINGandQGIS_SERVER_CACHE_SIZEare set in/etc/systemd/system/qgis-server@.service. They are deliberately conservative for 4 GiB of RAM, so raise them only alongside more RAM. After any change, runsudo systemctl daemon-reloadand restart the workers. - Logs: QGIS Server logs to the journal at level 2. Run
sudo journalctl -u 'qgis-server@*' -fwhile you issue requests, which is the fastest way to debug a project that will not load. nginx access and error logs are in/var/log/nginx/. - Self-test: re-run
sudo /usr/local/sbin/qgis-server-selftest.shafter any change to your project or nginx configuration. It exits non-zero with a reason if the OGC round trip or the auth gate stops working. - Storage: every published project and its data live under
/srv/qgison the dedicated data disk. Back up that volume to protect your work. - Upgrades: QGIS Server is an apt package from
qgis.org/ubuntu-ltr, sosudo apt update && sudo apt upgradekeeps it current within the Long Term Release series. Restart the workers afterwards. - Security patches: unattended-upgrades stays enabled, so the OS continues to receive security updates automatically.
Support
SecureAnyCloud provides 24/7 expert support for this image. Contact sales@secureanycloud.com.
Deploy on Azure
Launch QGIS Server on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com