Deployment guide

Gramps Web

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. You also get pedigree and relationship charts, a map of the places in your tree, full-text search across everything, and reports you can generate on demand.

This SecureAnyCloud image installs the Gramps Web API and the Gramps.js frontend and serves them through gunicorn behind nginx. A background worker handles the slow jobs. On the first boot of every VM, the image creates exactly one owner account with a unique password. The family tree you sign in to is empty, and it is yours.

What you get:

  • 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 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.

Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting size and comfortably handles a tree of several thousand people.

Configure the network security group (NSG) to allow inbound 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 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).

Select Review + create, then 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:gramps-web-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name gramps-web \
  --image sac1647283583153:gramps-web-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 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 and builds the search index. Only then does it open port 80.

Step 3 - Confirm the appliance is healthy

SSH in as azureuser and check the four services. The API and Redis must listen 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 '

Expect four active lines, the shipped component versions, and 127.0.0.1:5000 plus 127.0.0.1:6379 alongside a public *:80.

Next, call the health endpoint. It needs no authentication, so you can use it 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 the credentials once and store them in your password manager:

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

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 also generated per VM.

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.

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 9.

After you sign in, the dashboard summarizes your tree: the home person, recent changes, anniversaries and the object counts.

Step 6 - Verify the security posture from the command line

Your family tree is not world-readable. The API refuses anonymous access and rejects guessable credentials, and self-registration returns 405 Registration is disabled. The per-VM owner password, by contrast, 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/

Expect 401 for the anonymous read, 403 for the guessed login and 405 for self-registration.

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.

The 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. Use it to confirm that the whole path works end to end before you load 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 sac 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 the import finishes, everyone in the tree is listed and sortable, and every row opens the full record:

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

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

Step 8 - Set a home person so the charts draw

Do not skip this step. 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 and click Set Home Person. Pick the person you want your charts centered 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:

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

Step 9 - Add the rest of the family

Self-registration is disabled, so you create accounts for other researchers yourself. Signed in as the owner, open the account menu in the top right and go to Settings -> Users. Add each person with the role that suits them: Guest (read-only), Member, Contributor, Editor, Owner or Administrator. Each person 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

Gramps Web reads the photographs, scans and documents referenced by your tree 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, then restart the services:

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 from the browser under Export. This 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 fine 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. Then update BASE_URL in /etc/gramps-web/config.cfg to your https:// address. Restart gramps-web.service so that 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, then 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 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 sales@secureanycloud.com 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.

Deploy on Azure

Launch Gramps Web 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

Deploy on Azure