Repository navigation
Deploying.nl
🌐 English · Deutsch · Español · Français · 简体中文
deploy/ is de echte vorm van Gramps Connect voor meerdere gebruikers:
een gecontaineriseerde frontend app/ + backend
gramps-web-api, met een echte Postgres als basis (via de add-on
SharedPostgreSQL) en Caddy ervoor voor TLS, bedoeld om echt ergens te
worden gehost — echte geheimen, een echt domein/certificaat en meerdere
gebruikers, elk met een eigen login. Het is ook de enige manier om live
samenwerken in actie te zien; de
zelfstandige desktopversie is bewust voor één gebruiker,
dus er zijn geen bewerkingen van anderen om te zien verschijnen.
De backend is het officiële, ongewijzigde image dmstraub/gramps-webapi
— hetzelfde dat de eigen CI van gramps-project/gramps-web-api bij elke
release publiceert, en waarop gramps-project/gramps-web zelf bouwt —
geen build vanuit de broncode die deze repository onderhoudt. Zo blijft
deze installatie een gewone, standaard gramps-web-api-instantie waarmee
elke compatibele client kan praten, niet alleen de eigen frontend van
Gramps Connect. De add-ons SharedPostgreSQL/PostgreSQL/FilterRules/JSON,
ondersteuning voor meerdere stambomen en gecompileerde vertalingen komen
allemaal al uit dat image; het enige wat deploy/Dockerfile toevoegt is
de frontend van app/, als statische bestanden erbovenop gelegd. De
keerzijde: dat upstream-image is gebouwd op gramps-web-base (~4,3 GB —
torch, sentence-transformers, opencv, 45 taalpakketten voor tesseract),
met AI-extra's die onvoorwaardelijk zijn geïnstalleerd, omdat er geen
officiële slanke variant is om in plaats daarvan op te halen.
Frontend en backend delen standaard één container/origin (gramps_webapi
serveert de gebouwde SPA via STATIC_PATH en handelt /api/* in
hetzelfde proces af), dus Gramps Connect zelf heeft geen
CORS-configuratie nodig — dat is alleen relevant voor een andere, apart
gehoste frontend die aanroepen doet (stel in dat geval
GRAMPSWEB_CORS_ORIGINS in deploy/.env in).
Services: caddy (TLS-afhandeling, het enige gepubliceerde
toegangspunt), app (gunicorn, serveert de frontend + /api/*),
worker (Celery, voert taken voor import/media/herindexering van de
zoekfunctie uit), postgres (stamboomgegevens via SharedPostgreSQL),
redis (Celery-broker).
cp deploy/.env.example deploy/.env # bewerk het daarna -- zie de opmerkingen in het bestand
docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -d --buildGa daarna naar https://localhost (Caddy geeft zichzelf automatisch een
lokaal, zelfondertekend certificaat — accepteer de waarschuwing van de
browser).
Bouw één keer op de runners van GitHub in plaats van op de host
(gh workflow run build-docker.yml, of start het vanuit het tabblad
Actions), wat ghcr.io/<owner>/gramps-connect:latest pusht, en dan op
de host:
cp deploy/.env.example deploy/.env # deze keer echte geheimen/domein
docker compose -f deploy/docker-compose.yml --env-file deploy/.env pull
docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -dLaat een domein naar de host verwijzen en bewerk deploy/Caddyfile (zie
TLS hieronder) om een echt certificaat te krijgen in plaats van
het zelfondertekende.
Een stappenplan van een lege account tot een live site op een kleine cloudserver (DigitalOcean noemt die "droplets"). Elke VPS-aanbieder werkt op dezelfde manier zodra u een Linux-server met Docker hebt.
Kies in de DigitalOcean-console Create → Droplets:
- Image: Marketplace → Docker on Ubuntu (met Docker voorgeïnstalleerd).
-
Size: Basic, 1 vCPU / 2 GB RAM (
s-1vcpu-2gb) is de kleinste die goed werkt. Het backend-image is groot (~4 GB, zie hierboven), dus ga niet kleiner. - Region: welke het dichtst bij uw gebruikers ligt.
- Authentication: een SSH-sleutel (veiliger dan een wachtwoord).
Daarna Networking → Firewalls → Create Firewall: sta alleen inkomend verkeer op 22, 80 en 443 toe, en pas de firewall toe op de droplet.
2 GB RAM is krap tijdens imports en het opstarten, dus voeg een
swapbestand van 2 GB toe (log via SSH in als root):
fallocate -l 2G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstabOptioneel maar aanbevolen, omdat u daarmee een echt HTTPS-certificaat
krijgt. Een gratis naam van DuckDNS werkt,
net als een A-record op een domein dat u bezit. Laat het hoe dan ook
naar het publieke IP-adres van de droplet verwijzen.
git clone https://github.com/dsblank/gramps-connect /opt/gramps-connect
cd /opt/gramps-connect/deploy
cp .env.example .env
# bewerk .env: GRAMPSWEB_ADMIN_USER / GRAMPSWEB_ADMIN_PASSWORD,
# PUBLIC_URL=https://uw.domein en de e-mailinstellingen (zie hieronder)
docker compose pull
docker compose up -dDit haalt het kant-en-klare, publieke image
ghcr.io/dsblank/gramps-connect:latest op, dus er wordt niets op de
droplet zelf gebouwd.
Bewerk deploy/Caddyfile zoals beschreven onder TLS: vervang
het blok :443 { tls internal ... } door
uw.domein { reverse_proxy app:5000 } en verwijder het blok :80, en
voer daarna opnieuw docker compose up -d uit. Caddy vraagt zelf een
certificaat van Let's Encrypt aan en vernieuwt het.
Volg Eerste installatie en
Gegevens importeren
hieronder, met https://uw.domein in plaats van https://localhost.
- Nieuwe DigitalOcean-accounts blokkeren uitgaande SMTP. Gebruik een relay op poort 587 (bijv. Brevo, SendGrid, Mailgun) en open, als de e-mail nog steeds niet verstuurd wordt, een supportticket bij DigitalOcean (categorie Networking) om de blokkade te laten opheffen.
-
GRAMPSWEB_EMAIL_USE_STARTTLSmoettruein kleine letters zijn.Truewordt ongemerkt als tekenreeks gelezen en de verbinding mislukt. - De SMTP-login van Brevo is een gegenereerd adres (zoals
xxxx@smtp-brevo.com), niet het e-mailadres waarmee u zich hebt aangemeld.
Zie Een gehoste server bijwerken hieronder.
Deze stappen gelden voor elke host die de deploy/-stack draait, of het
nu een droplet is of niet. De commando's gaan ervan uit dat de
repository in /opt/gramps-connect is gekloond en dat u in de map
deploy/ daarvan staat.
cd /opt/gramps-connect/deploy
# Stamboomgegevens (Postgres)
docker compose exec -T postgres sh -c 'pg_dumpall -U "$POSTGRES_USER"' > backup-$(date +%F).sql
# Gebruikersaccounts (sqlite) en mediabestanden
docker compose cp app:/app/users ./backup-users-$(date +%F)
docker compose cp app:/app/media ./backup-media-$(date +%F)Updates kunnen bij het opstarten databasemigraties uitvoeren die niet terug te draaien zijn, dus een back-up is de enige manier om een update volledig ongedaan te maken.
De server haalt ghcr.io/dsblank/gramps-connect:latest op, dat alleen
verandert wanneer de workflow build-docker.yml draait. Controleer
direct na het taggen van een release of de Docker-build klaar is (het
tabblad Actions van de repository) voordat u bijwerkt, anders haalt
u de vorige versie op.
cd /opt/gramps-connect && git pull # bijgewerkte configuratie van deploy/
cd deploy
docker compose pull app # het nieuwe image
docker compose up -d # maakt opnieuw aan wat is gewijzigdAlleen app en worker worden opnieuw aangemaakt. postgres, redis en
caddy blijven draaien, en alle gegevens blijven in Docker-volumes.
Voeg nooit -v toe aan docker compose down, tenzij u echt alles wilt
verwijderen (zie Opmerkingen).
Als git pull weigert vanwege lokale wijzigingen: een host met een
echt domein heeft een bewerkte deploy/Caddyfile (zie TLS) die
nooit wordt gecommit, dus een update die de kopie in de repository
wijzigt, botst daarmee. Behoud uw wijzigingen met:
git stash && git pull && git stash pop # los daarna eventuele conflicten in deploy/Caddyfile opdeploy/.env staat in gitignore, dus daar wordt nooit aan geraakt.
diff .env.example .envKopieer nieuw toegevoegde instellingen uit .env.example naar .env, en
voer daarna opnieuw docker compose up -d uit als u iets hebt gewijzigd.
docker compose ps # app en worker onlangs aangemaakt, "Up"
docker compose logs --tail 50 app # gunicorn-regels "Booting worker", geen tracebacksLaad daarna de site en log in. Eén regel Gtk-CRITICAL ... gtk_icon_theme_get_for_screen per gunicorn-worker is normaal en
onschuldig: de server heeft GTK geïnstalleerd maar geen beeldscherm.
Elk image wordt ook getagd met de commit waaruit het is gebouwd,
ghcr.io/dsblank/gramps-connect:<commit-sha> (zie de pagina
Packages van de repository voor de lijst). Om terug te gaan,
verandert u de regel image: onder x-app-image bovenaan
deploy/docker-compose.yml in die tag, en daarna:
docker compose up -dAls de nieuwere versie de database heeft gemigreerd, zet dan ook de back-up uit stap 1 terug.
Een fork publiceert zijn eigen image. Voer build-docker.yml uit in uw
fork (dat ghcr.io/<uw-gebruiker>/gramps-connect pusht) en laat de regel
image: hierboven daarnaar verwijzen, of bouw op de server met
docker compose up -d --build. Bouwen op de server vergt meer schijfruimte
en geheugen dan de droplet van 2 GB hierboven, omdat het basisimage
alleen al zo'n 4 GB is.
Alle commando's hieronder gaan ervan uit dat u in de hoofdmap van de
repository staat. docker compose is de pluginvorm; als uw
Docker-installatie alleen het losse programma heeft, gebruik dan
docker-compose (in beide gevallen dezelfde opties).
# Status van alle vijf de services
docker compose -f deploy/docker-compose.yml ps
# Logs (voeg -f toe om te volgen, --tail=100 om te beperken)
docker compose -f deploy/docker-compose.yml logs app
docker compose -f deploy/docker-compose.yml logs worker
# Eén service herstarten (bijv. na een wijziging in deploy/.env)
docker compose -f deploy/docker-compose.yml up -d
# ^ maakt elke service opnieuw aan waarvan de configuratie (image, env,
# volumes) is gewijzigd; voeg eerst --build toe als u deploy/Dockerfile
# of de backendbroncode in app/ hebt bewerkt.
# Alles stoppen, gegevens behouden (volumes blijven bestaan)
docker compose -f deploy/docker-compose.yml down
# Stoppen en alle gegevens verwijderen (let op -- wist Postgres, media, gebruikers enz.)
docker compose -f deploy/docker-compose.yml down -v
# Een shell openen in een draaiende container
docker compose -f deploy/docker-compose.yml exec app shDeze Docker-installatie heeft geen standaardwachtwoord — u stelt
GRAMPSWEB_ADMIN_USER/GRAMPSWEB_ADMIN_PASSWORD zelf in deploy/.env
in vóór de eerste keer opstarten, en dat is wat het entrypoint aanmaakt.
Niets genereert of toont een wachtwoord voor u.
Dat is anders dan bij gramps-connect-desktop (de
PyInstaller-demoversie voor één gebruiker, los van deze
Docker-installatie): die maakt altijd een vaste account admin/admin
aan, wat daar prima is omdat het een lokale wegwerpdemo is, niet iets
wat op een echte server bereikbaar is.
Bij de eerste keer opstarten (de eerste keer dat de volumes
app-users/app-db leeg zijn) doet het entrypoint het volgende:
- het genereert en bewaart een geheime Flask-sleutel als
GRAMPSWEB_SECRET_KEYleeg is gelaten - het voert de migraties van de gebruikersdatabase uit
- het maakt een sitebeheerder aan (zonder stamboom, rol 5) op basis
van
GRAMPSWEB_ADMIN_USER/GRAMPSWEB_ADMIN_PASSWORD(beide verplicht indeploy/.env— geen terugval opadmin/admin)
Een sitebeheerder zonder stamboom kan via de API stambomen
aanmaken/opsommen/verwijderen, maar — omdat de frontend van app/ geen
interface heeft om een stamboom te kiezen en altijd verwacht dat de JWT
van de ingelogde gebruiker al een stamboom bevat — kan de gegevens van
een stamboom pas bekijken als hij eraan is toegewezen. Er is geen
CLI-commando om in de modus met meerdere stambomen een stamboom aan te
maken, dus maak de eerste stamboom aan via de API (eenmalig):
TOKEN=$(curl -sk -X POST https://localhost/api/token/ \
-H 'Content-Type: application/json' \
-d '{"username":"<admin>","password":"<admin password>"}' \
| python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")
TREE_ID=$(curl -sk -X POST https://localhost/api/trees/ \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"name":"My Family Tree"}' \
| python3 -c "import sys,json;print(json.load(sys.stdin)['id'])")
echo "$TREE_ID"Wijs daarna ofwel de sitebeheerder zelf aan die stamboom toe, ofwel (aanbevolen — houdt sitebeheer en eigendom van de stamboom gescheiden) maak een aparte gebruiker aan die aan de stamboom is gebonden:
# Optie A: de bestaande sitebeheerder aan de stamboom toewijzen.
curl -sk -X PUT "https://localhost/api/users/<admin>/" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d "{\"tree\":\"$TREE_ID\"}"
# Optie B: in plaats daarvan een aparte gewone gebruiker aanmaken die aan
# de stamboom is gebonden. email en full_name zijn verplicht volgens het
# schema, ook als ze niet worden gebruikt.
curl -sk -X POST "https://localhost/api/users/<username>/" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d "{\"email\":\"<email>\",\"full_name\":\"<full name>\",\"password\":\"<password>\",\"role\":4,\"tree\":\"$TREE_ID\"}"role is een geheel getal (gramps_webapi.auth.const):
| Rol | Waarde | Opmerkingen |
|---|---|---|
| ADMIN | 5 | Sitebeheerder; de enige rol die zonder stamboom mag zijn |
| OWNER | 4 | Volledige controle over de eigen stamboom |
| EDITOR | 3 | Kan stamboomgegevens bewerken |
| CONTRIBUTOR | 2 | Kan gegevens toevoegen |
| MEMBER | 1 | Leestoegang |
| GUEST | 0 | Minimale leestoegang |
Log daarna in op https://localhost/ als de gebruiker die u zojuist hebt
aangemaakt — bestaande tokens die vóór een toewijzing aan een stamboom
zijn uitgegeven bevatten die niet, dus log uit en opnieuw in als u een
sessie hergebruikt die al open was.
TOKEN=$(curl -sk -X POST https://localhost/api/token/ \
-H 'Content-Type: application/json' \
-d '{"username":"<owner>","password":"<password>"}' \
| python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")
# 1. Importeer het .gramps XML-bestand. BELANGRIJK: dit endpoint leest de
# ruwe body van het verzoek en schrijft die direct naar schijf -- het
# verwerkt GEEN multipart/form-data, dus gebruik --data-binary, niet
# -F/--form van curl (een multipart-upload beschadigt het bestand
# ongemerkt met boundary-/headerbytes, en de importeur van Gramps
# mislukt dan met een algemene, nietszeggende "Import failed" zonder
# verdere details).
curl -sk -X POST https://localhost/api/importers/gramps/file \
-H "Authorization: Bearer $TOKEN" --data-binary @example.gramps
# Het antwoord is een taakhandle (de import draait op de service `worker`
# via Celery) -- poll die tot "state":"SUCCESS" (of "FAILURE"):
curl -sk https://localhost/api/tasks/<task_id> -H "Authorization: Bearer $TOKEN"
# 2. Media: zip de mediabestanden waarnaar wordt verwezen (elke
# deelverzameling/superverzameling is prima -- bestanden worden op
# controlesom gematcht, bestanden zonder verwijzing worden genegeerd)
# en upload het archief, opnieuw als ruwe body:
curl -sk -X POST https://localhost/api/media/archive/upload/zip \
-H "Authorization: Bearer $TOKEN" --data-binary @media.zip
# 3. Controleren: aantallen objecten in de stamboom van de ingelogde gebruiker.
curl -sk https://localhost/api/metadata/ -H "Authorization: Bearer $TOKEN" \
| python3 -m json.toolBekende valkuil: met GRAMPSWEB_MEDIA_PREFIX_TREE=True (standaard
ingesteld in dit compose-bestand) worden media opgeslagen onder
MEDIA_BASE_DIR/<tree_id>/, maar niets maakt die submap per stamboom
automatisch aan — noch POST /api/trees/, noch de taak voor het uploaden
van mediaarchieven. Media uploaden voor een stamboom mislukt met
Directory /app/media/<tree_id> does not exist totdat die één keer is
aangemaakt:
docker compose -f deploy/docker-compose.yml exec app mkdir -p /app/media/<tree_id>Grote media-uploads die een time-out krijgen: zowel het
uploadendpoint voor losse bestanden (POST /api/media/) als dat voor
ZIP-archieven (POST /api/media/archive/upload/zip) leest de body van het
verzoek synchroon in de gunicorn-worker van app voordat het aan Celery
wordt overgedragen, dus een groot bestand of een trage verbinding kan
langer duren dan de time-out per verzoek van gunicorn — de browser ziet
dan een kale Internal Server Error zonder JSON-body (van gunicorn/Caddy,
niet van Gramps). deploy/docker-compose.yml zet GUNICORN_TIMEOUT
standaard op 600 s; verhoog die verder via GUNICORN_TIMEOUT in
deploy/.env (zie de opmerkingen in dat bestand) als uploads nog steeds
mislukken.
caddy is de enige service met gepubliceerde poorten (80/443) en handelt
TLS af vóór app. Zonder geconfigureerd domein genereert en bewaart hij
zijn eigen lokale CA en geeft daaruit automatisch een zelfondertekend
certificaat uit (tls internal in deploy/Caddyfile) — browsers tonen
de eerste keer een vertrouwenswaarschuwing; accepteer die om door te gaan
(of voeg de gegenereerde CA toe aan het vertrouwensarchief van uw
systeem/browser als u de waarschuwing kwijt wilt zonder echt domein).
Poort 80 verwijst door naar 443.
Zodra een echt domein naar deze server verwijst, bewerkt u
deploy/Caddyfile: vervang het blok :443 { tls internal ... } door
example.com { reverse_proxy app:5000 } en verwijder het blok :80 —
Caddy regelt voor een echt domein automatisch de ACME-uitgifte, de
vernieuwing en de doorverwijzing van poort 80, zonder dat een
tls-directive nodig is. Werk ook PUBLIC_URL in deploy/.env bij naar
het echte https://-domein, en voer daarna
docker compose -f deploy/docker-compose.yml up -d uit om beide
wijzigingen over te nemen.
Standaard niet ingesteld (er is geen service grampsweb in
docker-compose.yml), maar goed om te weten: omdat de backend een
gewone, ongewijzigde gramps-web-api-instantie is, kan
gramps-project/gramps-web (de andere officiële frontend van Gramps) ook
op dezelfde backend draaien — dezelfde gegevens, dezelfde
stambomen/gebruikers, alleen een andere interface op een andere poort.
gramps-web publiceert precies hiervoor
ghcr.io/gramps-project/grampsjs:latest: nginx die alleen de statische
build serveert, zonder ingebouwde backend. De nginx-configuratie stuurt
/api bij het starten van de container door naar een omgevingsvariabele
API_HOST, dus vanuit de browser gezien is het dezelfde origin — voor dit
pad is specifiek geen GRAMPSWEB_CORS_ORIGINS nodig (die instelling is
voor een gramps-web-instantie die helemaal ergens anders wordt gehost en
cross-origin aanroepen doet in plaats van via deze proxy).
Om die toe te voegen, een service in docker-compose.yml in deze trant:
grampsweb:
image: ghcr.io/gramps-project/grampsjs:latest
environment:
API_HOST: http://app:5000
# De ingebouwde DNS-resolver van Docker -- de directive `resolver` in
# default.conf.template vereist dat dit expliciet wordt ingesteld.
NAME_SERVER: 127.0.0.11
depends_on:
- app
restart: unless-stoppedgepubliceerd op een eigen poort (ofwel direct, bijv.
ports: ["8081:80"], ofwel achter Caddy met een tweede blok in de trant
van :8443 { reverse_proxy grampsweb:80 } in deploy/Caddyfile, voor
TLS zoals bij de service app).
- Gegevens blijven bewaard in benoemde Docker-volumes (
app-db,app-media,app-indexdir,app-users,app-secret,app-cache,app-tmp,postgres-data,caddy-data,caddy-config).docker compose down(zonder-v) behoudt ze;docker compose down -vverwijdert alles (inclusief de gegenereerde CA — dat betekent bij de volgende start een nieuwe vertrouwenswaarschuwing in de browser, niet alleen gegevensverlies). -
redis(Celery-broker/resultaatbackend) is vereist voor achtergrondtaken (indexering voor de zoekfunctie, grote import-/ exporttaken) — niet optioneel. Dat geldt ook voor de serviceworker: importeren, uploaden van mediaarchieven en herindexeren voor de zoekfunctie lopen allemaal via Celery zodraGRAMPSWEB_CELERY_CONFIG__*is ingesteld, en blijven voor altijd als onafgehandelde taak staan als niets de wachtrij verwerkt.app-cache(/app/cache) moet om dezelfde reden een volume zijn datappenworkerdelen: de verzoekafhandeling van de app-container schrijft het geüploade bestand daar, en daarna leest de Celery-taak van de worker-container het terug. - De service
workerdraait met--pool=solo(geen forking), uit voorzichtigheid omdat de standaard prefork-pool van Celery een proces forkt dat al echte PyGObject-/GTK-status heeft aangeraakt (de import vangi.repository.GLibdoor Gramps). Eén workerinstantie heeft de gelijktijdigheid die--pool=soloopgeeft niet nodig, maar dit is het heroverwegen waard als de doorvoer van de worker ooit een bottleneck wordt. - De lokale cache van
app/aan de browserzijde (sql.js, OPFS) heeft een vaste bestandsnaam per weergave als sleutel, niet de backend-URL of het stamboom-id — zie Architectuur voor de valkuil die dit veroorzaakt en hoe u die wist. -
.github/workflows/build-docker.yml(handmatig te starten —gh workflow run build-docker.yml, of het tabblad Actions) bouwtdeploy/Dockerfileen pushtghcr.io/<owner>/gramps-connect:latest.docker compose -f deploy/docker-compose.yml --env-file deploy/.env pullhaalt dat op in plaats van lokaal te bouwen;up -d --buildblijft er voor lokaal itereren aan de Dockerfile zelf.
Gramps Connect is part of the family of Gramps-based software.
Using the app
- Overview
- Installing
- Deploying
- Messaging
- GOQL (advanced search)
- Gramplets & Add-on Store
- Data Model & Editing
- FAQ
Building & contributing