Applications Azure

Gramps Web on Ubuntu 24.04 on Azure User Guide

| Product: Gramps Web on Ubuntu 24.04 LTS on Azure

Overview

Gramps Web is the self-hosted, collaborative way to research a family tree. It puts the Gramps genealogical database behind a modern browser interface, so a family or a research group can build one shared tree together instead of emailing files around. People, families, events, places, sources, citations, notes, repositories and media are kept as one connected record set, with pedigree and relationship charts, a map of the places in your tree, full-text search across everything, and reports you can generate on demand.

The cloudimg image installs the Gramps Web API and the Gramps.js frontend, serves them through gunicorn behind nginx, runs a background worker for the slow jobs, and creates exactly one owner account with a unique password on the first boot of every VM. The family tree you sign in to is empty and it is yours. Backed by 24/7 cloudimg support.

What is included:

  • Gramps Web API 3.18.0 and the Gramps.js frontend 26.7.0, both AGPL-3.0, on the Gramps 6.0 core library
  • nginx on port 80 as the single public entry point, reverse proxying a gunicorn worker pool bound to 127.0.0.1:5000
  • A background task worker (Celery, with Redis on loopback) so GEDCOM imports, exports, reports and media processing never block the interface
  • SQLite storage for the family tree, the user database and the full-text search index under /var/lib/gramps-web
  • Self-registration disabled, and exactly one owner account with a password generated uniquely for your VM on first boot
  • gramps-web.service, gramps-web-worker.service, nginx.service and redis-server.service as systemd units, enabled and active
  • 24/7 cloudimg support

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 good starting point and comfortably handles a tree of several thousand people. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web interface. Do not expose 5000/tcp or 6379/tcp — the API and Redis are bound to loopback by design and are not reachable from outside the VM.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Gramps Web 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 -> Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name gramps-web \
  --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 gramps-web --port 80 --priority 1010

First boot takes about a minute. In that time the VM generates its own session-signing key and owner password, creates your empty family tree, seeds the single owner account, builds the search index and only then opens port 80.

Step 3 - Confirm the appliance is healthy

SSH in as azureuser and check the four services. The API and Redis must be listening on loopback only; nginx is the sole public listener.

systemctl is-active gramps-web.service gramps-web-worker.service nginx.service redis-server.service
cat /opt/gramps-web/VERSIONS
ss -tln | grep -E ':80 |:5000 |:6379 '

Expected output — four active lines, the shipped component versions, and 127.0.0.1:5000 plus 127.0.0.1:6379 alongside a public *:80:

Service status, shipped versions and loopback-only listeners for Gramps Web on Ubuntu 24.04

The unauthenticated health endpoint is suitable for an Azure Load Balancer or Application Gateway probe:

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

Step 4 - Retrieve your per-VM owner password

This image ships with no account and no password. On first boot your VM generated its own. Read them once and store them in your password manager:

sudo cat /root/gramps-web-credentials.txt

The per-VM Gramps Web owner credentials file, root-only, with the password masked

The file is 0600 root:root. GRAMPS_WEB_OWNER_USER is owner and GRAMPS_WEB_OWNER_PASSWORD is unique to this VM — no other deployment of this image has the same one. The Flask session-signing key in /etc/gramps-web/config.cfg is generated per VM as well.

Step 5 - Sign in

Open http://<your-vm-public-ip>/ in a browser and sign in with the owner username and password from Step 4.

The Gramps Web sign-in screen, with self-registration disabled so no register option is offered

There is deliberately no Register new account option. Self-registration is disabled on this image, so nobody who finds your VM can create an account on it. You add researchers yourself in Step 8.

After signing in you land on the dashboard, which summarises your tree: the home person, recent changes, anniversaries and the object counts.

The Gramps Web dashboard showing family tree statistics after a tree has been imported

Step 6 - Verify the security posture from the command line

Your family tree is not world readable. Anonymous API access is refused, guessable credentials are rejected, and self-registration returns 405 Registration is disabled — while the per-VM owner password mints a real token and authenticates:

curl -s -o /dev/null -w 'anonymous /api/people/ : HTTP %{http_code}\n' http://127.0.0.1/api/people/
curl -s -o /dev/null -w 'guessed owner/owner    : HTTP %{http_code}\n' -X POST \
  -H 'Content-Type: application/json' \
  -d '{"username":"owner","password":"owner"}' http://127.0.0.1/api/token/
sleep 2
curl -s -o /dev/null -w 'self-registration      : HTTP %{http_code}\n' -X POST \
  -H 'Content-Type: application/json' \
  -d '{"email":"x@y.z","full_name":"x","password":"xxxxxxxxxxxx"}' \
  http://127.0.0.1/api/users/anyone/register/

Expected: 401 for the anonymous read, 403 for the guessed login, 405 for self-registration.

Anonymous access refused, guessed credentials rejected, self-registration disabled, and the per-VM owner password authenticating

Now mint a token with your own credentials and call the API as the owner. Substitute your values from Step 4:

BODY=$(printf '{"username":"%s","password":"%s"}' '<GRAMPS_WEB_OWNER_USER>' '<GRAMPS_WEB_OWNER_PASSWORD>')
TOKEN=$(curl -s -X POST -H 'Content-Type: application/json' -d "$BODY" \
  http://127.0.0.1/api/token/ \
  | sed -n 's/.*"access_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
curl -s -o /dev/null -w 'authenticated /api/users/-/ : HTTP %{http_code}\n' \
  -H "Authorization: Bearer $TOKEN" http://127.0.0.1/api/users/-/
curl -s -o /dev/null -w 'authenticated /api/metadata/ : HTTP %{http_code}\n' \
  -H "Authorization: Bearer $TOKEN" http://127.0.0.1/api/metadata/

Both authenticated calls return 200.

Step 7 - Import your family tree

Your tree starts empty. Bring your research in from a GEDCOM file (.ged, exported by virtually every genealogy program) or a Gramps XML file (.gramps, exported by Gramps desktop). In the browser use the + button and choose to import; from the command line, copy the file onto the VM and POST it to the importer endpoint.

Import runs in the background worker, so a large file returns immediately with a task reference and keeps loading while you carry on. The example below writes a tiny three-person GEDCOM and imports it so you can confirm the whole path works end to end before committing your real data:

BODY=$(printf '{"username":"%s","password":"%s"}' '<GRAMPS_WEB_OWNER_USER>' '<GRAMPS_WEB_OWNER_PASSWORD>')
TOKEN=$(curl -s -X POST -H 'Content-Type: application/json' -d "$BODY" \
  http://127.0.0.1/api/token/ \
  | sed -n 's/.*"access_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
cat > /tmp/sample.ged <<'GEDCOM'
0 HEAD
1 SOUR cloudimg
1 GEDC
2 VERS 5.5.1
2 FORM LINEAGE-LINKED
1 CHAR UTF-8
0 @I1@ INDI
1 NAME Ada /Lovelace/
1 SEX F
1 BIRT
2 DATE 10 DEC 1815
2 PLAC London, England
0 @I2@ INDI
1 NAME Byron /Lovelace/
1 SEX M
1 FAMC @F1@
0 @I3@ INDI
1 NAME William /Lovelace/
1 SEX M
0 @F1@ FAM
1 HUSB @I3@
1 WIFE @I1@
1 CHIL @I2@
0 TRLR
GEDCOM
curl -s -o /dev/null -w 'import: HTTP %{http_code}\n' \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/x-gedcom' \
  --data-binary @/tmp/sample.ged http://127.0.0.1/api/importers/ged/file
sleep 10
curl -s -D - -o /dev/null -H "Authorization: Bearer $TOKEN" \
  'http://127.0.0.1/api/people/?pagesize=1' | grep -i '^x-total-count'

The import POST returns 202 (accepted, running in the worker) and the people count then reflects the imported records. To import a Gramps XML file instead, use -H 'Content-Type: application/octet-stream' and the /api/importers/gramps/file endpoint.

Once loaded, everyone in the tree is listed and sortable, and every row opens the full record:

The Gramps Web People view listing an imported family tree with names, birth and death dates

Each person page gathers their events, relationships, parents, siblings, sources and media in one place:

A Gramps Web person page showing birth and death details, parents and siblings

You can count the whole tree, and search across every record type, straight from the API:

Family tree object counts and a full-text search result set returned by the Gramps Web REST API

Step 8 - Set a home person so the charts draw

This step is easy to miss. The Family Tree view is anchored on a home person, and until you choose one it renders blank even though your tree is fully loaded. Open Home in the left-hand menu, click Set Home Person, and pick the person you want your charts centred on — usually yourself or the most recent generation.

With a home person set, the Family Tree view draws the ancestor tree, and the tabs across the top switch between the descendant tree, hourglass graph, relationship graph and fan chart:

The Gramps Web ancestor tree chart drawn from the home person, showing parents and grandparents

Full-text search covers people, families, events, places, sources, citations and notes together:

Gramps Web full-text search results across the whole family tree

Step 9 - Add the rest of the family

Because self-registration is disabled, you create accounts for other researchers yourself. Signed in as the owner, open the account menu in the top right, go to Settings -> Users, and add each person with the role that suits them: Guest (read only), Member, Contributor, Editor, Owner or Administrator. Each gets their own username and password, and you can change or remove access at any time.

To create an account from the command line instead:

sudo -u grampsweb /opt/gramps-web/venv/bin/python -m gramps_webapi \
  --config /etc/gramps-web/config.cfg \
  user add <username> '<password>' --fullname '<Full Name>' --email <email> --role 3

Roles are numeric: 0 guest, 1 member, 2 contributor, 3 editor, 4 owner, 5 administrator.

Step 10 - Media files

Photographs, scans and documents referenced by your tree are read from /var/lib/gramps-web/media. Copy them there (owned by grampsweb) so that thumbnails and the media viewer resolve:

sudo rsync -av ./my-photos/ /var/lib/gramps-web/media/
sudo chown -R grampsweb:grampsweb /var/lib/gramps-web/media

If a media object in your tree points at a file that is not present, only that thumbnail request fails — the rest of the application is unaffected. GEDCOM files exported from another machine often carry media paths from that machine, so copying the files across is usually the missing step.

Step 11 - Back up your family tree

Everything that matters lives under /var/lib/gramps-web: the tree itself, the user database, the search index and your media. Stop the services for a consistent copy, archive the directory, and restart:

sudo systemctl stop gramps-web-worker gramps-web
sudo tar czf /var/backups/gramps-web-$(date +%F).tar.gz -C /var/lib gramps-web
sudo systemctl start gramps-web gramps-web-worker

You can also export the tree itself from the browser under Export, which produces a Gramps XML or GEDCOM file you can take anywhere.

Step 12 - Put HTTPS in front

The image serves plain HTTP on port 80, which is right for a first look but not for a family tree you reach over the internet. Point a DNS name at the VM, open 443/tcp, and obtain a certificate:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com

Certbot edits the nginx site in place and installs a renewal timer. Afterwards, update BASE_URL in /etc/gramps-web/config.cfg to your https:// address and restart gramps-web.service so password-reset links point at the right host.

Maintenance

The OS is fully patched at build time and continues to receive unattended security updates. To upgrade the application itself, update the Python package in the virtual environment and restart both units:

sudo -u grampsweb /opt/gramps-web/venv/bin/pip install --upgrade gramps-webapi
sudo systemctl restart gramps-web gramps-web-worker

Useful commands:

systemctl status gramps-web.service --no-pager | head -5
journalctl -u gramps-web-worker.service --no-pager -n 5

Rebuild the full-text search index if search results ever look stale:

sudo -u grampsweb env GRAMPS_API_CONFIG=/etc/gramps-web/config.cfg \
  GRAMPSHOME=/var/lib/gramps-web/gramps GRAMPS_DATABASE_PATH=/var/lib/gramps-web/db \
  /opt/gramps-web/venv/bin/python -m gramps_webapi \
  --config /etc/gramps-web/config.cfg search index-full

Support

24/7 technical support is included with this image. Email support@cloudimg.co.uk for help with deployment, GEDCOM and Gramps XML import, user accounts and access levels, media configuration, backups, upgrades and performance tuning. Critical issues receive a one hour average response time.

Gramps Web is developed by the Gramps project and distributed under the GNU Affero General Public License v3.0. Gramps and Gramps Web are trademarks of their respective owners.