---
title: "Quire ynstallearje mei Docker Compose"
description: "Ynstallearje Quire op dyn eigen ynfrastruktuer mei Docker Compose."
image: "https://docs.quirelms.com/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.quirelms.com/fy/llms.txt
> Use this file to discover all available pages before exploring further.

# Quire ynstallearje mei Docker Compose

<span id="installing-quire-with-docker-compose"></span>

Dit is it folsleine produkt op ien host: de LMS, it eftergrûnwurk, de tsjinsten foar realtime
en tegearre bewurkjen en alle opsjonele tsjinsten ûnder in profile. De ûntwerpbesluten steane
yn seksje 2 fan `docs/architecture/23-ops.md`.

Oare doelen: [Vercel](/fy/ops/vercel/) en [Cloudflare Workers](/fy/ops/cloudflare/) rinne allinnich de web tier.
Upgrades steane yn [upgrade.md](/fy/ops/upgrade/), reservekopyen en de restore drill yn
[backup-restore.md](/fy/ops/backup-restore/).

## Watsto nedich hast <!--quire:what-you-need-->

- Docker Engine 27 of nijer mei Compose-plugin 2.30 of nijer.
- 4 CPU-kearnen en 8 GB ûnthâld foar de standertstack; 8 kearnen en 16 GB
  mei `--profile full` (ClamAV allinnich brûkt sa'n 1.5 GB oan hantekeningen).
- In DNS-namme foar de web tier en in twadde foar net-fertroude ynhâld. Dat moatte
  ferskillende hosts wêze: SCORM-pakketten en uploade HTML draaie op de content-origin,
  sadat se de cookies fan de LMS nea lêze kinne.
- Foar in lokale test ferwize `lvh.me` en `*.localhost` nei 127.0.0.1, lykas
  `docker/.env.example` brûkt. De eigen `proxy`-tsjinst fan de stack biedt beide oer
  https mei in lokale certificate authority oan; der hoecht dus neat oars ynstallearre
  te wurden (sjoch "TLS").
- De porten 80 en 443 moatte frij wêze op de host (`QUIRE_PROXY_HTTP_PORT` en
  `QUIRE_PROXY_HTTPS_PORT` kinne dy feroarje).

## Earste start <!--quire:first-run-->

```sh
QUIRE_APP_ORIGIN=https://learn.example.org \
QUIRE_CONTENT_ORIGIN=https://content.example-content.org \
QUIRE_SETUP_ADMIN_EMAIL=you@example.org \
  docker/scripts/init-env.sh
docker compose -f docker/compose.yaml up -d --build
docker compose -f docker/compose.yaml logs init
```

`docker/scripts/init-env.sh` makket `docker/.env` út `docker/.env.example`, mei alle
geheimen oanmakke (databasewachtwurden, signing- en masterkaaien, it keypair foar content
launch) en de audit checkpoint signing key yn `docker/secrets/audit-signing-key.pem`,
dy't Compose as secret yn de workers mount. Allinnich `sh`, `awk` en `openssl` binne
nedich; it skript wegeret in besteande `docker/.env` te oerskriuwen. Kopiearje beide
bestannen fan dizze host ôf: sûnder `QUIRE_MASTER_KEY` kin in weromsette database de
bewarre bewiisgegevens net ûntsiferje. Om it bestân mei de hân yn te foljen, brûk
`cp docker/.env.example docker/.env`; yn it bestân stiet hoe'tst elk geheim meitsje kinst.

Beide origins moatte `https` wêze: de content service wegeret plain http yn produksje,
en de registrable domain fan beide mei net itselde wêze. De `proxy`-tsjinst makket TLS
foar beide ôf (sjoch "TLS"); `init-env.sh` wegeret in `http://`-origin.

De stack begjint yn in fêste folchoarder; elke stap wachtet op de foargeande:

1. `postgres` wurdt sûn. By de earste start stelt it init-skript
   (`docker/postgres/init/90-passwords.sh`) de fjouwer rolwachtwurden yn.
2. `migrate` past alle migrations ta en bootstrapt de job queue yn de control-database
   en elke tawijde tenant-database, kontrolearret dat se oerienkomme en einiget dan
   (docs/ops/upgrade.md). Migrations rinne by elke start en binne idempotint, dus in
   upgrade is in nije image en in trochstart.
3. `init` (`apps/web/src/first-run.ts`) registrearret de application database ûnder
   `QUIRE_DATABASE_ID` en makket, as `QUIRE_SETUP_ADMIN_EMAIL` ynsteld is, de earste
   organisaasje en behearder oan. It oanmeldadres en in oanmakke wachtwurd wurde ien
   kear printe yn `docker compose logs init`.
4. `web`, `content`, `worker`, `scheduler`, `collab` en `centrifugo` begjinne.
5. `proxy` begjint as `web` en `content` sûn binne.

Iepenje `https://demo.` folge troch dyn applikaasjedomein (it `init`-logboek print it
eksakte oanmeldadres) en meld dy oan. By in lokale ynstallaasje moatst earst de certificate
authority fan de proxy fertrouwe (sjoch "TLS"). Feroarje it oanmakke wachtwurd op
`/account/security`.

In proses sûnder fereaske geheim wegeret te starten en neamt de ûntbrekkende ynstelling yn
it logboek. Der start neat heal ynsteld.

## Tsjinsten en profiles <!--quire:services-and-profiles-->

| Tsjinst | Profile | Funksje |
| --- | --- | --- |
| postgres | always | De database (PostgreSQL 18 mei pgvector, boud fan `docker/postgres.Dockerfile`), mei WAL-argyf fan de earste boot ôf |
| migrate, init | always | Ien kear: migrations en dêrnei de earste start |
| web | always | De LMS op `QUIRE_HTTP_PORT` (8080) |
| content | always | De net-fertroude content-origin op `QUIRE_CONTENT_PORT` (8081) |
| worker | always | Eftergrûntaken: e-mail, rapporten, bestânsferwurking, webhooks |
| scheduler | always | Weromkommende taken: registrearret de 64 runtime-skema's en jout dy troch oan de worker; ien lieder tagelyk |
| collab | always | Websocket foar tegearre bewurkjen op `QUIRE_COLLAB_HTTP_PORT` (1234) |
| centrifugo | always | Realtime fan-out op `QUIRE_REALTIME_PORT` (8000) |
| proxy | always | Caddy, de TLS-yngong op porten 80 en 443 (sjoch "TLS") |
| valkey | `cache` | Cache en taryflimiten |
| clamav | `scan` | Kontrolearjen fan uploads op malware |
| gotenberg | `preview` | Office-nei-PDF-foarbylden en sertifikaten rendere |
| imgproxy | `images` | Ofbyldings opnij skaalje en omsette |
| transcoder | `video` | Worker-image mei allinnich LGPL-ffmpeg, foar fideorenditions |
| seaweedfs | `storage` | S3-kompatibele objektopslach op dizze host |
| otelcol | `observability` | OpenTelemetry-collector |
| mailpit | `devmail` | Fangt alle útgeande e-mail op om Quire út te probearjen |
| backup | `backup` | Ien kear in base backup; sjoch backup-restore.md |
| backup-scheduler, backup-offsite | `backup` | Base backup elke `QUIRE_BACKUP_INTERVAL_HOURS`, plus fersifere kopyen bûten de host mei in wyklikse ferifikaasjedrill |
| h5p | `h5p` | H5P LTI 1.3-toolimage dy'tst yn `QUIRE_H5P_IMAGE` leverest, op `QUIRE_H5P_PORT` (8090); sjoch "In H5P-provider ferbine" |

`--profile full` start alle opsjonele tsjinsten útsein `backup` en `h5p`.
Start ien mei `docker compose -f docker/compose.yaml --profile scan up -d`.
Sûnder in opsjonele tsjinst wurket Quire noch en jout oan wat ûntbrekt: sûnder scanner
wurde uploads net-scanne opslein en wurdt de behearder warskôge; sûnder Gotenberg kinne
bestannen wol downloade mar net foarbesjoen wurde; sûnder transcoder spilet fideo ôf as
it orizjinele bestân.

Elke ôfbylding fan in tredde partij en de lisinsjeferplichtingen steane yn
`docker/third-party-containers.yaml`.

### In H5P-provider ferbine <!--quire:connecting-an-h5p-provider-->

Quire befettet of ferstjoert gjin H5P-runtime of sidecar (ADR 0019). As H5P brûkt wurdt,
soargje foar in eigen hosted abonnemint of in selsbehearde H5P-ynstallaasje apart fan Quire.
Registrearje de provider as LTI 1.3 external tool en foegje de ynhâld ta oan kursussen as
toolaktiviteiten. Quire wikselet sifers en aktiviteit-/beoardielingsfuortgong út fia LTI
Assignment and Grade Services (AGS). As de provider ek xAPI-statements stjoert, konfigurearje
dat apart foar de xAPI-statement store fan Quire; it útwikseljen fan sifer/foartgong fia AGS
stjoert gjin xAPI-statements. Moodle-ymporten melde dat H5P-aktiviteiten in LTI-toolferbining
nedich hawwe. De provider bliuwt ferantwurdlik foar de H5P-runtime, it meitsjen fan ynhâld,
de ynhâldsbank en skiednis fan besykjen.

Om op dizze host in eigen selsbehearde ynstallaasje te draaien, set `QUIRE_H5P_IMAGE` op de
image en start it `h5p`-profile. Compose publisearret it op `QUIRE_H5P_PORT` (8090) en bewarret
de gegevens yn it `h5p-data`-volume; de image en byhearrende ferplichtingen bliuwe dyn eigen.

## Ynstellingen <!--quire:settings-->

Elke proses lêst `docker/.env`. It sjabloan `docker/.env.example` list elke ynstelling mei
de standertwearde. De groepen:

### Adressen <!--quire:addresses-->

| Ynstelling | Betsjutting |
| --- | --- |
| `QUIRE_APP_ORIGIN` | It iepenbiere adres fan de LMS, bygelyks `https://learn.example.com` |
| `QUIRE_CONTENT_ORIGIN` | De content-origin, op in oare host |
| `QUIRE_PLATFORM_DOMAINS` | Domeinen dêr't organisaasjes ûnder falle, skieden troch komma's |
| `QUIRE_MARKETING_ORIGIN` | Optional. The marketing site, default `https://quirelms.com`. The only origin the waitlist form (`POST /api/waitlist`, `POST /waitlist`) accepts and redirects to. Comma separated; a `www.` variant is allowed only if listed |
| `QUIRE_DEPLOY_TARGET` | Hjir `compose`. Sjoch oare hantliedingen foar `vercel` en `cloudflare` |
| `QUIRE_TRUSTED_PROXY_CIDRS` | Proxy's waans `X-Forwarded-For` fertroud wurdt |

### Geheimen <!--quire:secrets-->

| Ynstelling | Betsjutting |
| --- | --- |
| `QUIRE_SECRET_KEY` | Undertekenet sesjes en tokens. 64 hextekens |
| `QUIRE_MASTER_KEY` | Ferpakt bewarre bewiisgegevens lykas SSO- en webhookgeheimen. 32 byte, base64. Web tier en worker moatte deselde wearde hawwe. Rotaasje: [key-rotation.md](/fy/ops/key-rotation/) |
| `QUIRE_MASTER_KEY_VERSION` | Ferzjelabel fan de master key, `v1` as it net ynsteld is. Ferheegje by rotaasje |
| `QUIRE_MASTER_KEY_RETIRED` | Eardere masterkaaien dy't noch nedich binne om te lêzen wat se ferpakt hawwe, as `v1=<base64>`. Fuortsmite as de rotaasje foltôge is en neat mear net oplost is |
| `QUIRE_COLLAB_SIGNING_KEY` | Dield troch web en collab foar ûndertekenjen fan bewurkjetokens |
| `QUIRE_BACKUP_SIGNING_KEY` | Undertekenet kursusreservekopyen (opsjoneel) |

Hâld in kopy fan `QUIRE_MASTER_KEY` earne oars as op dizze host. In database dy't sûnder
dy kaai weromset is, kin de bewarre bewiisgegevens net ûntsiferje.

### Database <!--quire:database-->

| Ynstelling | Betsjutting |
| --- | --- |
| `POSTGRES_PASSWORD` | De superuser, brûkt troch container en reservekopyen |
| `QUIRE_DB_APP_PASSWORD`, `QUIRE_DB_MIGRATOR_PASSWORD`, `QUIRE_DB_REPORT_PASSWORD`, `QUIRE_DB_AUDIT_PASSWORD` | Rolwachtwurden, ynsteld by de earste start |
| `DATABASE_URL` | De applikaasjerol. Row-level security jildt foar elke query dy't dy docht |
| `DATABASE_MIGRATOR_URL`, `QUIRE_MIGRATION_URL` | De migratorrol foar `migrate` en `init` |
| `QUIRE_SUPERUSER_URL` | Allinnich brûkt by de earste start |
| `QUIRE_REPORT_DATABASE_URL` | De read-only rapportrol foar rapporten en rapportbouwer |
| `QUIRE_AUDIT_DATABASE_URL` | De auditrol foar auditkonsole en SIEM-eksport |
| `QUIRE_DATABASE_ID` | Elke UUID, fêst foar de hiele libbensdoer fan de ynstallaasje |

Rolwachtwurden wurde allinnich tapast as it databasevolume foar it earst oanmakke wurdt.
Om ien letter te feroarjen, brûk `ALTER ROLE` en fernij dêrnei de byhearrende URL.

`QUIRE_REPORT_DATABASE_URL` wurdt brûkt foar de fysike database dy't `DATABASE_URL` ynstelt.
Stel foar oare registrearre fysike databases de eigen `quire_report`-ferbinings-URL yn de
web- en workeromjouwing yn; set dêrnei de fariabelenamme yn it fjild **Reporting environment
variable** fan dy database as `env:NAME`. De ferwizing moat nei deselde database as de appferbining
wize, leafst de lêsreplika. Elk rapportflak folget de tenant nei de rapportferbining fan syn
eigen database: rapportbouwer en bewarre rapporten, plande leveringen, rapporteksporten,
analytics, auditlogboek, REST-auditboarnen en sykjen yn de assistant-audit. Gjin fan dy brûkt
ea de rapport-URL fan in oare database. As in database gjin rapportferbining hat, rinne gewoane
rapporten op de eigen applikaasjeferbining fan dy database, wylst analytics en elke auditlêzing
wegerje en dat meidiele, om't de applikaasjerol it auditspoar net lêze kin.

### Drivers <!--quire:drivers-->

| Ynstelling | Dizze release | Taljochting |
| --- | --- | --- |
| `QUIRE_STORAGE_DRIVER` | `local` (standert), `s3` of `azure` | `local` hâldt bestannen yn it `files`-volume. `s3` omfiemet AWS S3, R2, GCS-ynteroperabiliteit en oare S3-kompatibele stores, mei hervatbere multipart-uploads |
| `QUIRE_REALTIME_DRIVER` | `inprocess` (standert), `sse`, `centrifugo` of `durable_objects` | `inprocess` past by ien webcontainer; brûk `centrifugo` of `sse` as der mear binne |
| `QUIRE_CACHE_DRIVER` | `memory` (standert), `postgres` of `valkey` | `memory` is per proses; brûk `valkey` of `postgres` sadat taryflimiten jilde oer containers hinne |
| `QUIRE_VIDEO_DRIVER` | `ffmpeg` (standert) of `progressive_mp4` | Of in hosted provider: Cloudflare Stream, Mux of Bunny, mei harren kaaien |
| `QUIRE_IMAGE_DRIVER` | `noop` (standert), `imgproxy` of `cloudflare` | `noop` tsjinnet elke ôfbylding yn orizjinele grutte. `imgproxy` freget it `images`-profile en de ynstellingen hjirûnder; `cloudflare` brûkt Cloudflare Images |
| `QUIRE_MEETING_PROVIDER` | `bbb`, `zoom`, `teams`, `meet`, `jitsi` of `in_process` | Standertprovider fan it platfoarm foar live sesjes. As dy net ynsteld is, binne live sesjes net konfigurearre oant in organisaasje in eigen akkount ferbynt ûnder Integrations, Live session provider. It eigen akkount fan in organisaasje giet altyd foar dizze wearde. Eigen ynstellingen fan elke provider (`BBB_URL` en `BBB_SECRET`, de fariabelen `ZOOM_*`, `TEAMS_*`, `GOOGLE_MEET_*` en `JITSI_*`) wurde allinnich lêzen foar de hjir neamde provider |
| `QUIRE_MEETING_REGIONS` | Kommalist mei `eu`, `uk`, `us` | Regio's dêr't de standertprovider fan it platfoarm gearkomsten ferwurket. As dit net ynsteld is, wurdt it lykas earder net kontrolearre tsjin in organisaasje dy't oan in regio bûn is. De side fan in organisaasje jout de regio's fan har eigen akkount oan |

In driverwearde dy't dizze release net befettet, wurdt ôfwiisd as de web tier begjint;
de ynstelling wurdt neamd ynstee fan stilwei ferfongen te wurden troch de standertwearde.

### Ofbyldings <!--quire:images-->

Siden freegje ôfbyldings yn fjouwer fêste maten fia
`/api/files/{id}/image/{size}`; dat kontrolearret deselde tagong as it bestân sels en
ferwiist dêrnei troch nei de ôfbyldingstsjinst. Elke organisaasje kin yn in oere
`QUIRE_IMAGE_SPECS_PER_HOUR` (standert 2000) nije kombinaasjes fan ôfbylding en grutte
oanfreegje; maten dy't dat oere al makke binne telle net mei. Brûk `valkey` of `postgres`
foar `QUIRE_CACHE_DRIVER` as der mear as ien webcontainer is, sadat de limyt foar alle
containers jildt.

| Ynstelling | Driver | Taljochting |
| --- | --- | --- |
| `IMGPROXY_URL` | `imgproxy` | Adres dêr't browsers imgproxy berikke, bygelyks `https://images.example.org`. It `images`-profile publisearret it op `QUIRE_IMAGES_PORT` (8082) |
| `IMGPROXY_KEY`, `IMGPROXY_SALT` | `imgproxy` | Hextekenrige, deselde wearden dêr't imgproxy mei begjint. Meitsje elke wearde mei `openssl rand -hex 32`. Quire ûndertekenet elk ôfbyldingsadres dêrmei, sadat imgproxy neat rendert dêr't Quire net om frege hat |
| `QUIRE_IMAGE_SOURCE_ORIGIN` | `imgproxy` mei lokale opslach | Wêr't imgproxy de orizjinelen ophellet. Compose set `http://web:3000` yn. By `s3`- of `azure`-opslach hellet imgproxy se út de bucket en wurdt dit net brûkt |
| `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_IMAGES_TOKEN`, `CLOUDFLARE_IMAGES_ACCOUNT_HASH` | `cloudflare` | API-token mei bewurkingsrjochten foar Images en de account-hash út Images, Developer resources. Skeakelje fleksibele farianten foar it akkount yn |
| `CLOUDFLARE_IMAGES_SIGNING_KEY` | `cloudflare` | Opsjoneel. As dy ynsteld is, binne ôfbyldings privee en is elk adres ûndertekene en ferrint it. Sûnder dizze kaai binne ôfbyldings iepenbier op adressen ôflaat fan `QUIRE_SECRET_KEY` dy't net te rieden binne |

Cloudflare Images hâldt in eigen kopy fan elk orizjineel dat it tsjinnet. As in bestân
fuorthelle wurdt, wisket de worker dy kopy foardat it orizjineel fuortgiet.

### Wachtrige <!--quire:queue-->

Eftergrûntaken brûke pg-boss yn deselde Postgres-database, dus der hoecht gjin wachtrigetsjinst
draaid of ynsteld te wurden. Taken wurde yn deselde transaksje op de wachtrige set as de
feroaring dy't se feroarsake, sadat in crash gjin taak kwytreitsje of dûbel ferstjoere kin.
`QUIRE_QUEUE_DRIVER` is hjir standert `pgboss`; `vercel` en `cloudflare` ferpleatse allinnich
lichte meidielingen en webhookleveringen nei de eigen wachtrige fan it platfoarm. De Vercel-
en Cloudflare-hantliedingen beskriuwe dy en hoe't harren web tiers taken op de wachtrige sette.

### E-mail <!--quire:email-->

Stel ien fan dizze yn:

- `QUIRE_EMAIL_PROVIDER_CONFIG`: in JSON-objekt mei de namme fan in HTTP-provider en syn
  bewiis, lykas `{"provider":"postmark","token":"..."}`. Postmark, Amazon SES, Mailgun,
  SendGrid en Resend wurde stipe.
- `QUIRE_SMTP_URL`: `smtp://user:password@host:587`. Allinnich foar dizze target; de
  serverless-targets blokkearje SMTP.

`QUIRE_MAIL_FROM` is de ôfstjoerder. Om Quire út te probearjen, start it `devmail`-profile,
set `QUIRE_SMTP_URL=smtp://mailpit:1025` yn en lês de e-mail op `http://localhost:8025`.

### Opsjonele tsjinsten <!--quire:optional-services-->

| Ynstelling | Mei profile |
| --- | --- |
| `CLAMAV_URL=tcp://clamav:3310` | `scan` |
| `GOTENBERG_URL=http://gotenberg:3000` | `preview` |
| `IMGPROXY_KEY`, `IMGPROXY_SALT` | `images` |
| `VALKEY_URL=redis://valkey:6379` | `cache` |
| `QUIRE_OPENSEARCH_URL` of `QUIRE_MEILISEARCH_URL` | Eksterne syktsjinst; oars folsleine tekstsykjen yn Postgres |
| `QUIRE_BREACH_CHECK_PROVIDER=off`, `QUIRE_BREACH_CHECK_URL` | Kontrolearje oft in wachtwurd by in datalek belutsen is. Standert oan tsjin `api.pwnedpasswords.com` (allinnich in hashprefix fan fiif tekens wurdt ferstjoerd); `off` set de kontrôle út en de URL ferwiist nei in range-API dy'tsto sels hostest |

### Observability <!--quire:observability-->

`OTEL_EXPORTER_OTLP_ENDPOINT` neamt de collector dêr't elk proses traces en metrics
hinne stjoert; mei it `observability`-profile is dat `http://otelcol:4318`, en yn
`docker/otel-collector.yaml` foegest de exporter foar dyn backend ta. As dit ynsteld is,
eksportearje de web tier, worker, scheduler, content- en collabprosessen spans fia OTLP/HTTP
(webfersiken, tenant-databasetransaksjes, workertaken en útgeande oproppen) en elke minút
metrics nei itselde einpunt (`OTEL_METRICS_EXPORTER=none` skeakelet dy út).
`OTEL_TRACES_SAMPLER_ARG` set it oanpart fan traces dat bewarre wurdt. Logs geane nei
standard output op `LOG_LEVEL` en Compose draait se troch. Traces befetsje gjin persoansgegevens.

### Regionale egress (dataferbliuw yn de EU) <!--quire:regional-egress-eu-data-residency-->

`QUIRE_REGION=eu` jout oan dat de stack organisaasjes yn de Europeeske Uny betsjinnet.
De worker beheint dan elk útgeand fersyk foar in organisaasje dy't oan de EU ferbûn is ta in
allowlist (21-compliance.md seksje 8.1). Dêr steane hosts op dy't ynstelde tsjinsten foar de
regio oanjouwe (opslach-einpunt, e-mailprovider, hosted fideoprovider, eigen opslachdoelen
fan de organisaasje, AI-providers en e-mailakkount), hosts fan tsjinsten ûnder in aktive
ôfwiking en hosts dy'tst yn `QUIRE_EGRESS_ALLOW_HOSTS` neamst. In fersyk nei in oare iepenbiere
host wurdt wegere foardat it ferstjoerd wurdt; de wegere poging komt as `privacy/egress_refused`
yn it auditspoar fan de organisaasje en wurdt neamd ûnder Neilibjen, Data residency.

| Ynstelling | Wearden | Effekt |
| --- | --- | --- |
| `QUIRE_EGRESS_ALLOW_HOSTS` | Kommalist mei hostnammen, of `*.example.org` foar elk subdomein | Oanfoljende hosts dy't in EU-organisaasje berikke mei. Webhook-, xAPI- en SIEM-einpunten, blogfeeds en Amazon SES-hosts hearre hjirby, om't de organisaasje dy sels kiest en gjin tsjinst se oanjout. Loopback, priveeadressen en nammen mei ien label lykas `web` of `clamav` binne dyn eigen netwurk en wurde nea kontrolearre |

Organisaasjes yn it Feriene Keninkryk en de FS binne net bûn oan in hostlist; de regio-
kontrôles fan de tsjinst bliuwe jilde. Stel de list yn by de worker; de behearderside lêst
him yn de web tier om de allowlist sjen te litten, dus set him yn `docker/.env` dat elke
tsjinst lêst.

De kontrôle yn de applikaasje jout in dúdlike flater en audit-yngong, mar is net de garânsje:
koade kin ferkeard wêze. It netwurk is de garânsje. Compose hanthavenet it net foar dy.
Foar in regionale stack, set de tsjinsten `worker` en `web` op in netwurk `internal: true`
dat allinnich fia in egress-proxy nei bûten kin (bygelyks Squid of in tinyproxy-container).
Lit dy deselde hosts tastean as `QUIRE_EGRESS_ALLOW_HOSTS` plus de hosts fan dyn ynstelde
tsjinsten en set `HTTPS_PROXY` foar dy tsjinsten yn. De side oer dataferbliuw list de eksakte
hosts dy't de applikaasje tastiet, sadatst beide listen ferlykje kinst.

## Sûnens <!--quire:health-->

| Einpunt | Betsjutting |
| --- | --- |
| `/healthz` | Liveness: it proses antwurdet. Compose-healthchecks brûke dit |
| `/readyz` | Readiness: ôfhinklikheden binne berikber en elke opsjonele tsjinst is al of net as ynsteld rapportearre. Lit dyn load balancer hjirhinne wize |

`docker compose -f docker/compose.yaml ps` lit de sûnens fan elke tsjinst sjen.

## TLS <!--quire:tls-->

De `proxy`-tsjinst (Caddy, Apache-2.0, `docker/caddy/Caddyfile`) heart by de standertstack.
Hy antwurdet op porten 80 en 443 en stjoert troch:

| Host of paad | Giet nei |
| --- | --- |
| `QUIRE_PROXY_CONTENT_HOST` | `content` |
| `QUIRE_PROXY_APP_HOST`, elk tenantsubdomein en oanpast domein | `web` |
| `/_collab/` op dy hosts | `collab` (websocket, `QUIRE_COLLAB_URL`) |
| `/_realtime/connection/` op dy hosts | client-websocket fan `centrifugo`; syn server-API wurdt nea bleatsteld |
| `/_images/` op dy hosts | `imgproxy`, mei it `images`-profile (`IMGPROXY_URL`) |

`init-env.sh` liedt `QUIRE_PROXY_APP_HOST`, `QUIRE_PROXY_CONTENT_HOST`,
`QUIRE_PROXY_HTTPS_PORT`, `QUIRE_COLLAB_URL` en `IMGPROXY_URL` ôf fan de twa origins,
sadat se net útinoar rinne kinne. Bewurkje se mei-inoar as in origin mei de hân feroare wurdt.

Sertifikaten folgje `QUIRE_PROXY_TLS`:

- `internal` (standert): de eigen certificate authority fan Caddy, foar `localhost`,
  `*.localhost` en `lvh.me`. Fertrou ien kear de root en blêdzje dan:

  ```sh
  docker compose -f docker/compose.yaml cp \
    proxy:/data/caddy/pki/authorities/local/root.crt ./quire-local-ca.crt
  ```

  Foegje `quire-local-ca.crt` ta oan it fertrouwe-sertifikatenopslach fan it systeem of de
  browser. `curl` brûkt it mei `--cacert`.
- In e-mailadres: automatyske ACME-sertifikaten (Let's Encrypt, dêrnei ZeroSSL) foar echte
  hostnammen. DNS fan beide origins en elke tenanthost moat hjirhinne wize; porten 80 en 443
  moatte fan it ynternet ôf berikber wêze.

Tenanthosts krije op oanfraach in sertifikaat by it earste besyk en allinnich as web befêstiget
dat de namme by dizze ynstallaasje heart (`/tls-allowed`, oanroppen oer it Compose-netwurk).
In wildcard-sertifikaat of DNS-providerplugin is net nedich; in frjemde namme dy't nei de host
wize lit, kin gjin sertifikaatoanfraach útlokje. Sertifikaten en de lokale authority steane yn
it `caddy-data`-volume; meitsje dêr tegearre mei de rest in reservekopy fan as `internal` brûkt wurdt.

Web fertrout allinnich de `X-Forwarded-For` fan de proxy: dy hat in fêst adres
(`QUIRE_PROXY_ADDRESS`, standert `172.29.64.10`) op in fêst subnet (`QUIRE_COMPOSE_SUBNET`),
en `QUIRE_TRUSTED_PROXY_CIDRS` neamt dat adres. As it subnet botst mei in netwurk op de host,
feroarje beide en fier `docker compose down` út foar `up`.

## Efter dyn eigen reverse proxy <!--quire:behind-your-own-reverse-proxy-->

Om ynstee in load balancer of proxy te brûken dy'tst al draait, lit `proxy` fuort
(`docker compose up -d --scale proxy=0`) en beëinigje TLS foar `web` (8080), `content` (8081),
`collab` (1234, websocket) en `centrifugo` (8000, websocket). Stel de iepenbiere adressen
yn `QUIRE_APP_ORIGIN`, `QUIRE_CONTENT_ORIGIN` en `QUIRE_COLLAB_URL` (`wss://`) yn, en it
adresberik fan de proxy yn `QUIRE_TRUSTED_PROXY_CIDRS`.

## Problemen oplosse <!--quire:troubleshooting-->

- `init` einiget mei "QUIRE_DATABASE_ID is not a UUID": set it mei `uuidgen` yn.
- `web` start hieltyd opnij mei "did not start on compose": it logboek list elke ynstelling
  dy't net útfierd wurde kin en watst ynstee brûke moatst.
- In rolwachtwurd feroarje yn `.env` nei de earste start docht neat: it init-skript rint mar
  ien kear. Brûk `ALTER ROLE`.
- Uploads mislearje mei in scanflater wylst `CLAMAV_URL` ynsteld is: ClamAV downloadt de
  hantekeningen by de earste start en dat duorret in pear minuten.

Source: https://docs.quirelms.com/fy/ops/install/index.mdx
