Um
Applications Azure

uMap on Ubuntu 24.04 on Azure User Guide

| Product: uMap on Ubuntu 24.04 LTS on Azure

Overview

uMap is an open source web application for creating and sharing maps built on OpenStreetMap layers. You draw markers, lines and polygons directly in the browser, import your own geodata from GeoJSON, GPX, KML or CSV, choose icons, colours and styles, organise features into data layers, and share the finished map with a link or embed it in any web page. Because maps are drawn over standard OpenStreetMap tiles there is nothing heavy to host, and by default anyone can create a map without an account, editing it privately through a secret link, which is exactly how the public uMap service works.

uMap is a GeoDjango application, so the cloudimg image supplies the production stack around it: uMap 3.7.3 is installed at a pinned upstream release into a dedicated Python virtual environment, served by gunicorn and reverse proxied behind nginx, and backed by PostgreSQL 16 with the PostGIS spatial extension for map metadata and Redis for caching, all on the same instance. gunicorn is bound to loopback 127.0.0.1:8019 and is never exposed directly; nginx on port 80 is the only way in, plus an unauthenticated /healthz endpoint for load balancer probes. Map data layers are stored as GeoJSON files on disk and served efficiently by nginx.

The image is secure by default. No credential of any kind ships, and nothing is listening on the public port in the image at all, so there is no window in which a freshly launched VM is reachable with a known password. On the first boot of every VM a fresh Django secret key, a fresh PostgreSQL password, a fresh Redis password and a unique randomly generated administrator password are created, written to a root only file, and only then is the public site brought up. Backed by 24/7 cloudimg support.

What is included:

  • uMap 3.7.3 installed in a dedicated Python virtual environment and running as the umap gunicorn systemd service
  • Django 5.2 LTS on Python 3.12, within uMap's supported range
  • The uMap web application on :80, fronted by nginx with gunicorn bound to loopback only
  • A PostgreSQL 16 database with the PostGIS extension on loopback, holding map metadata, with a per VM database password generated on first boot
  • Redis on loopback as the Django cache, with a per VM password generated on first boot
  • Anonymous map creation enabled by default, the public by design uMap posture, with the Django admin reserved for a single instance administrator
  • A ready made example map, Welcome to uMap by cloudimg, with markers, so the home page shows a real map the moment it boots
  • No default account and no shipped secret: the administrator user and the Django secret key are both created on first boot with unique random values
  • Host settings configured so the app works on the public IP address or a custom domain, with no baked in hostname
  • An unauthenticated /healthz endpoint for Azure Load Balancer health probes
  • postgresql.service, redis-server.service, umap.service (gunicorn) and nginx.service as systemd units
  • 24/7 cloudimg support

Key facts

Item Value
Platform Ubuntu 24.04 LTS (Gen2)
Default SSH user azureuser
Application root /srv/umap
Python virtual environment /srv/umap/venv
Settings file /etc/umap/umap.conf
Per VM environment file /srv/umap/umap.env
Map data layers (GeoJSON) /srv/umap/var/data
Service account umap (non login system user)
Per VM credentials /root/umap-credentials.txt
Recommended size Standard_B2s (2 vCPU / 4 GiB RAM)

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point. NSG inbound: allow 22/tcp from your management network, 80/tcp for uMap, and 443/tcp if you add TLS. The image serves plain HTTP on port 80; for production use, terminate TLS with your own domain and restrict access to trusted IP ranges (see Security Recommendations).

Step 1 - Deploy from the Azure Marketplace

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

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name umap \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

Open port 80 to your network so you can reach uMap:

az vm open-port --resource-group <your-rg> --name umap --port 80

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

First boot generates the per VM secrets, creates the administrator account and then brings the public site up. It normally completes within a minute of the VM reaching Running. Check all four services:

systemctl is-active postgresql redis-server umap nginx

All four report active.

Terminal output showing postgresql, redis-server, umap and nginx all reporting active

Confirm the health endpoint answers:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/healthz

It returns 200.

Check that first boot completed. systemctl show reports the state and the outcome separately: a first boot that ran and succeeded shows ActiveState=active (it is a oneshot unit with RemainAfterExit=yes) together with Result=success.

systemctl show -p ActiveState -p Result -p ExecMainStatus umap-firstboot.service

The application server, the database and the cache are deliberately reachable only on the loopback interface. nginx on port 80 is the single public entry point:

sudo ss -tlnp | grep -E ':(80|8019|5432|6379)\b'

Terminal output showing gunicorn on 127.0.0.1:8019, PostgreSQL on 127.0.0.1:5432 and redis-server on 127.0.0.1:6379, with only nginx bound to port 80

Step 5 - Retrieve your per VM administrator password

Every VM generates its own administrator password on first boot and writes it to a root only file. Nothing is shared between deployments and no password is baked into the image.

sudo cat /root/umap-credentials.txt

The file lists UMAP_URL, UMAP_ADMIN_USER (which is umapadmin) and UMAP_ADMIN_PASSWORD, along with the map creator and Django admin addresses. Store the password in your password manager and change it after your first sign in.

The credentials file and the environment file that holds the Django secret key are both restricted, and the appliance ships a checker that proves no secret has leaked into the system journal, the authentication log, /var/backups, shell history or any other log:

sudo ls -l /root/umap-credentials.txt /srv/umap/umap.env
sudo bash /usr/local/sbin/umap-secret-leak-check.sh

The checker prints LEAKCHECK_OK.

Step 6 - Open uMap and the example map

Browse to http://<vm-public-ip>/. uMap opens on its home page: a search box, a summary of what uMap does, a Create a map button, and a feed of maps on the instance.

The uMap home page with a search box, the create a map button and a description of what uMap does

The image ships one example map, Welcome to uMap by cloudimg, so a real map is visible immediately. Open it from the feed. It shows markers for three cities and a route drawn between them, on OpenStreetMap tiles, with the Points of interest data layer listed in the panel.

The example map Welcome to uMap by cloudimg showing three city markers and a route on OpenStreetMap tiles, with the Points of interest data layer

Step 7 - Create a map with markers

Anonymous map creation is enabled by default, so you do not need an account to make a map. Choose Create a map on the home page. The editor opens with a whole world view, the drawing tools on the right (marker, line, polygon), and the map layers, import, settings and share controls.

The uMap editor for a new untitled map, showing the drawing toolbar with marker, line and polygon tools and the map management controls

Click the marker tool, click a point on the map to drop a marker, give it a name and a description, then choose Save. uMap creates the map, stores your marker as a GeoJSON data layer, and gives you a shareable URL for the map plus a secret edit link so you can come back and change it later. Set the visibility from Draft (private) to public in the map settings if you want it listed and viewable by others.

Step 8 - Sign in to the Django administration area

The Django admin is reserved for the single instance administrator and is used to moderate maps, manage licences and tile layers, and remove content. Browse to http://<vm-public-ip>/admin/ and sign in as umapadmin with the password from Step 5.

The Django administration dashboard signed in as umapadmin, showing the uMap section with Maps, Data layers, Licences, Pictograms, Teams and Tile layers

The uMap section lists Maps, Data layers, Licences, Pictograms, Teams and Tile layers. Use it to review or delete maps created on your instance, add custom tile layers, or manage the licences offered to map authors. Self service account sign up and third party sign in are switched off by default, so this administrator is the only account on the instance until you configure otherwise.

Step 9 - Verify the whole workflow yourself

The appliance ships the same end to end check that the image is built and tested with. It confirms PostGIS is present, proves a Redis cache round trip, creates a map with a marker through uMap's own models, re opens the map by its public URL and reads the data layer GeoJSON served through nginx to confirm the marker is there, proves an unauthenticated write to that data layer is rejected, and then removes the map it created so nothing is left behind:

sudo bash /usr/local/sbin/umap-verify-workflow.sh

It prints a single line, WORKFLOW_OK map=<id> marker_served=1 postgis=<version> redis=ok. Any failure prints BROKEN-<reason> instead and exits non zero.

Terminal output showing WORKFLOW_OK with the map id, marker served, the PostGIS version and redis ok

You can check the installed versions at any time:

/srv/umap/venv/bin/python -c "import django, importlib.metadata as m; print('Django', django.get_version(), '| uMap', m.version('umap-project'))"

Terminal output showing the installed Django and uMap versions and PostGIS reporting version 3.4

Step 10 - List the maps on your instance

You can list maps from the command line with the instance environment already loaded:

sudo /usr/local/sbin/umap-manage shell -c "from umap.models import Map; print([(m.id, m.slug, m.name) for m in Map.objects.all()])"

The shipped example map, welcome-to-umap, appears in the list.

Step 11 - Optional: turn on account sign up or OAuth sign in

By default the only way to sign in is the Django admin as the instance administrator; anonymous visitors can still create maps. If you want visitors to register their own uMap accounts, or to sign in with an external provider such as OpenStreetMap, edit the settings.

To allow local account login, set ENABLE_ACCOUNT_LOGIN in the environment file:

sudo nano /srv/umap/umap.env

Add the line, then restart uMap:

ENABLE_ACCOUNT_LOGIN=1
sudo systemctl restart umap

To use OpenStreetMap or another OAuth provider, add the provider's backend and keys to /etc/umap/umap.conf following the uMap configuration documentation, then restart uMap. Leave both switched off if you only need anonymous map creation and a single administrator.

Step 12 - Add a domain and TLS

For production, put your own certificate in front of uMap. Point a DNS record at the VM's public IP, then install a certificate with your preferred tool and configure nginx to serve it. The vhost lives at /etc/nginx/sites-available/cloudimg-umap.

Once you are serving HTTPS, tell uMap its public address, restrict the hostnames it answers on and switch the session and CSRF cookies to secure only, by editing the environment file:

UMAP_SITE_URL=https://umap.example.com
UMAP_ALLOWED_HOSTS=umap.example.com
UMAP_CSRF_TRUSTED_ORIGINS=https://umap.example.com
UMAP_SECURE_COOKIES=true

Then restart the service:

sudo systemctl restart umap

Leave UMAP_SECURE_COOKIES at false while you are still on plain HTTP. A secure only cookie is never returned by the browser over HTTP, so turning it on too early stops sign in and map creation from working.

Server Components

Component Version Purpose
uMap 3.7.3 The map creator: maps, data layers, imports, sharing and embedding
Django 5.2 LTS The GeoDjango web framework hosting uMap
Python 3.12 Runtime, in a dedicated virtual environment
gunicorn 26.x WSGI application server, bound to 127.0.0.1:8019
nginx 1.24 Reverse proxy on port 80, serves static files and map data layers
PostgreSQL 16 Map metadata database, bound to 127.0.0.1:5432
PostGIS 3.4 Spatial extension providing the geometry types uMap needs
Redis 7.0 Django cache, bound to 127.0.0.1:6379, password protected

Filesystem Layout

Path Size Purpose
/ 29 GB Root filesystem
/boot 881 MB Operating system kernel files
/boot/efi 105 MB UEFI boot partition (Gen2 Hyper V)
/mnt 8 GB Azure temporary resource disk (not persistent)

Key directories

Path Purpose
/srv/umap Application root, owned by the umap system user
/srv/umap/venv Python virtual environment holding uMap and its dependencies
/srv/umap/var/static Collected static files, served by nginx
/srv/umap/var/data Map data layers stored as GeoJSON files
/etc/umap/umap.conf The cloudimg appliance settings
/srv/umap/umap.env Per VM environment file: secret key, database and Redis passwords, host settings
/var/lib/postgresql/16/main PostgreSQL data directory
/etc/nginx/sites-available/cloudimg-umap The nginx vhost
/root/umap-credentials.txt Per VM administrator credentials, 0600 root:root

Managing the services

systemctl status umap --no-pager --lines=0
sudo systemctl restart umap
sudo systemctl restart nginx
sudo systemctl restart postgresql
sudo systemctl restart redis-server

Follow the application log:

sudo journalctl -u umap -f

Run any uMap or Django management command with the instance environment already loaded:

sudo /usr/local/sbin/umap-manage showmigrations umap | tail -5

Scripts and Log Files

Path Purpose
/usr/local/sbin/umap-firstboot.sh First boot: generates the per VM secrets, creates the administrator account, enables the public site
/usr/local/sbin/umap-manage Runs a uMap or Django management command with the instance environment
/usr/local/sbin/umap-verify-workflow.sh End to end workflow check (Step 9)
/usr/local/sbin/umap-secret-leak-check.sh Proves no per VM secret reached the journal, auth log, backups or history
/var/lib/cloudimg/umap-firstboot.done First boot sentinel; its presence stops first boot re-running
journalctl -u umap Application and gunicorn access log
journalctl -u umap-firstboot First boot log
/var/log/nginx/access.log, /var/log/nginx/error.log nginx logs

On Startup

umap-firstboot.service runs once, on the first boot of each VM, gated on the sentinel above. It resolves the VM's public address, rotates the PostgreSQL password and the Redis password, generates a unique Django secret key, applies any pending database migrations, creates the umapadmin administrator account with a random password, starts gunicorn on loopback, and only then enables the public nginx vhost. It finishes by proving the whole map workflow works and that no secret leaked into any log, and writes the sentinel.

Until that unit has completed, nothing is listening on port 80. This is deliberate: it means a newly launched VM is never reachable with a credential that anybody else could know. On later boots the unit is skipped and the normal service units start uMap.

Backups

Back up two things: the PostgreSQL database (map metadata, permissions, users) and the data directory (the GeoJSON files that hold your actual map features). Do not copy the environment file into a backup location, because it holds the Django secret key and the database password.

sudo install -d -o root -g root -m 0700 /var/backups/umap
sudo -i bash -c 'set -a; . /srv/umap/umap.env; set +a; umask 077; PGPASSWORD="$DB_PASSWORD" pg_dump -h 127.0.0.1 -U "$DB_USER" -d "$DB_NAME" | gzip > /var/backups/umap/umap-$(date -u +%Y%m%d).sql.gz'
sudo tar czf /var/backups/umap/umap-data-$(date -u +%Y%m%d).tar.gz -C /srv/umap/var data
sudo ls -l /var/backups/umap/

Copy both files off the VM to your own backup storage.

Troubleshooting

uMap does not answer on port 80. First boot may still be running. Check its state and its outcome:

systemctl show -p ActiveState -p Result -p ExecMainStatus umap-firstboot.service

ActiveState=active with Result=success means first boot finished. Result=exit-code or a non zero ExecMainStatus means it failed; read the log below.

sudo journalctl -u umap-firstboot --no-pager | tail -40

The public vhost is enabled by first boot, so if that unit failed, port 80 stays closed by design.

Sign in or map creation does not work in a browser but the site loads. Check that UMAP_SECURE_COOKIES is not set to true while you are still serving plain HTTP. A secure only cookie is never sent back over HTTP, so the session and the CSRF token are silently dropped.

A page reports a disallowed host. Add your hostname to UMAP_ALLOWED_HOSTS and your scheme plus hostname to UMAP_CSRF_TRUSTED_ORIGINS in /srv/umap/umap.env, then restart umap.

The administrator password is not accepted. Re-read it from /root/umap-credentials.txt. If the account has been lost entirely, create a replacement:

sudo DJANGO_SUPERUSER_PASSWORD='<new-password>' /usr/local/sbin/umap-manage createsuperuser --noinput --username newadmin --email you@example.com

Checking application errors.

sudo journalctl -u umap --no-pager | tail -60

Security Recommendations

  • Change the umapadmin password after your first sign in.
  • Anonymous map creation is on by default. If your instance should be private, either restrict 80/tcp and 443/tcp to trusted networks in the NSG, or turn anonymous creation off by setting UMAP_ALLOW_ANONYMOUS=0 in /etc/umap/umap.conf and restarting uMap.
  • Restrict 22/tcp to your management network in the NSG.
  • Terminate TLS with your own certificate and domain, then set UMAP_SITE_URL, UMAP_ALLOWED_HOSTS, UMAP_CSRF_TRUSTED_ORIGINS and UMAP_SECURE_COOKIES=true as described in Step 12.
  • Keep /root/umap-credentials.txt and /srv/umap/umap.env as they are shipped. The environment file holds the Django secret key; anyone who reads it can forge sessions.
  • Unattended security upgrades are enabled on the image. Leave them on, and reboot when a kernel update requires it.
  • Take regular database and data directory backups as described above, and store them off the VM.

Support

cloudimg provides 24/7 support for this image. Contact support@cloudimg.co.uk with your Azure subscription ID and the VM name.

uMap is open source software distributed under the GNU Affero General Public License version 3. uMap is a project of the OpenStreetMap community and OpenStreetMap is a trademark of the OpenStreetMap Foundation; cloudimg is not affiliated with, endorsed by or sponsored by either. The example map shown in this guide is included for illustration.