---
title: "Nginstal Quire nganggo Docker Compose"
description: "Instal Quire ing infrastruktur dhewe nganggo Docker Compose."
image: "https://docs.quirelms.com/og.png"
---

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

# Nginstal Quire nganggo Docker Compose

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

Iki produk lengkap ing siji host: LMS, pagawean latar, layanan realtime lan nyunting bebarengan, lan saben layanan opsional ing mburine profil. Desaine iku `docs/architecture/23-ops.md` bagean 2.

Target liyane: [Vercel](/jv/ops/vercel/) lan [Cloudflare Workers](/jv/ops/cloudflare/) mbukak tier web wae. Upgrade ana ing [upgrade.md](/jv/ops/upgrade/), lan serep lan latihan restore ing [backup-restore.md](/jv/ops/backup-restore/).

## Apa sing dibutuhake <!--quire:what-you-need-->

- Docker Engine 27 utawa luwih anyar kanthi plugin Compose 2.30 utawa luwih anyar.
- 4 inti CPU lan 8 GB memori kanggo stack gawan; 8 inti lan 16 GB nganggo `--profile full` (ClamAV dhewe nyekel kira-kira 1.5 GB signature).
- Jeneng DNS kanggo tier web lan siji maneh kanggo konten sing ora dipercaya. Kudu host beda: paket SCORM lan HTML sing diunggah mlaku ing origin konten supaya ora tau bisa maca cookie LMS.
- Kanggo tes lokal, `lvh.me` lan `*.localhost` ngrampungake menyang 127.0.0.1, sing digunakake `docker/.env.example`. Layanan `proxy` stack dhewe ngladeni kalorone liwat https nganggo certificate authority lokal, supaya ora ana liyane sing diinstal (deleng "TLS").
- Port 80 lan 443 bebas ing host (`QUIRE_PROXY_HTTP_PORT` lan `QUIRE_PROXY_HTTPS_PORT` mindhah).

## Mlaku kapisan <!--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` nulis `docker/.env` saka `docker/.env.example` kanthi saben rahasia digawe (tembung sandhi database, kunci signing lan master, pasangan kunci launch konten) lan kunci signing checkpoint audit ing `docker/secrets/audit-signing-key.pem`, sing dipasang Compose menyang worker minangka rahasia. Mung butuh `sh`, `awk` lan `openssl`, lan nolak nimpa `docker/.env` sing wis ana. Salin kalorone file saka host: tanpa `QUIRE_MASTER_KEY` database sing dibalekake ora bisa dekripsi kredensial sing disimpen. Kanggo ngisi file kanthi tangan, `cp docker/.env.example docker/.env`; file ngandhani carane nggawe saben rahasia.

Kalorone origin kudu `https`: layanan konten nolak http biasa ing produksi, lan ora kudu nuduhake domain sing bisa didaftar. Layanan `proxy` mungkasi TLS kanggo kalorone (deleng "TLS"); `init-env.sh` nolak origin `http://`.

Stack miwiti kanthi urutan tetep, lan saben langkah ngenteni sing sadurunge:

1. `postgres` dadi sehat. Ing miwiti pisanan skrip init-e (`docker/postgres/init/90-passwords.sh`) nyetel tembung sandhi papat peran.
2. `migrate` ngetrapake saben migrasi lan bootstrap antrean pagawean ing database kontrol lan ing saben database tenant khusus, mriksa kabeh sarujuk, banjur metu (docs/ops/upgrade.md). Migrasi mlaku ing saben miwiti lan idempoten, dadi upgrade iku image anyar lan restart.
3. `init` (`apps/web/src/first-run.ts`) nyathet database aplikasi ing sangisore `QUIRE_DATABASE_ID` lan, nalika `QUIRE_SETUP_ADMIN_EMAIL` disetel, nggawe organisasi kapisan lan administratore. Alamat mlebu lan tembung sandhi sing digawe dicithak sapisan, ing `docker compose logs init`.
4. `web`, `content`, `worker`, `scheduler`, `collab` lan `centrifugo` miwiti.
5. `proxy` miwiti yen `web` lan `content` sehat.

Bukak `https://demo.` diterusake domain aplikasi panjenengan (log `init` nyithak alamat mlebu persis), lan mlebu. Ing instalasi lokal, percaya certificate authority proxy dhisik (deleng "TLS"). Ganti tembung sandhi sing digawe ing `/account/security`.

Proses sing diwiwiti tanpa rahasia sing dibutuhake nolak miwiti lan nyebutake setelan sing ilang ing loge. Ora ana sing miwiti setengah dikonfigurasi.

## Layanan lan profil <!--quire:services-and-profiles-->

| Layanan | Profil | Apa sing ditindakake |
| --- | --- | --- |
| postgres | always | Database (PostgreSQL 18 nganggo pgvector, dibangun saka `docker/postgres.Dockerfile`), kanthi WAL diarsipake wiwit boot kapisan |
| migrate, init | always | Sapisan: migrasi, banjur mlaku kapisan |
| web | always | LMS, ing `QUIRE_HTTP_PORT` (8080) |
| content | always | Origin konten sing ora dipercaya, ing `QUIRE_CONTENT_PORT` (8081) |
| worker | always | Pagawean latar: email, laporan, pangolahan file, webhook |
| scheduler | always | Pagawean rutin: ndhaftarake 64 jadwal runtime lan masrahake menyang worker; siji leader ing wektu |
| collab | always | Websocket nyunting bebarengan, ing `QUIRE_COLLAB_HTTP_PORT` (1234) |
| centrifugo | always | Fan-out realtime, ing `QUIRE_REALTIME_PORT` (8000) |
| proxy | always | Caddy, lawang ngarep TLS ing port 80 lan 443 (deleng "TLS") |
| valkey | `cache` | Cache lan wates rate |
| clamav | `scan` | Scan malware unggahan |
| gotenberg | `preview` | Pratinjau Office menyang PDF, rendering sertifikat |
| imgproxy | `images` | Gambar sing diowahi ukuran lan dikonversi |
| transcoder | `video` | Image worker kanthi ffmpeg mung-LGPL, kanggo rendisi video |
| seaweedfs | `storage` | Panyimpenan objek kompatibel S3 ing host iki |
| otelcol | `observability` | Kolektor OpenTelemetry |
| mailpit | `devmail` | Nyekel kabeh mail metu, kanggo nyoba Quire |
| backup | `backup` | Serep dasar sapisan; deleng backup-restore.md |
| backup-scheduler, backup-offsite | `backup` | Serep dasar saben `QUIRE_BACKUP_INTERVAL_HOURS`, lan salinan off-host dienkripsi kanthi latihan verifikasi mingguan |
| h5p | `h5p` | Image piranti H5P LTI 1.3 sing disedhiyakake ing `QUIRE_H5P_IMAGE`, ing `QUIRE_H5P_PORT` (8090); deleng "Connecting an H5P provider" |

`--profile full` miwiti saben layanan opsional kajaba `backup` lan `h5p`. Miwiti siji nganggo `docker compose -f docker/compose.yaml --profile scan up -d`. Tanpa layanan opsional Quire tetep mlaku lan ngandhani apa sing ilang: tanpa scanner tegese unggahan disimpen tanpa discan lan administrator dikandhani; tanpa Gotenberg tegese file nawakake undhuhan tinimbang pratinjau; tanpa transcoder tegese video muter minangka file asli.

Saben image pihak katelu lan kewajiban lisensine kadhaptar ing `docker/third-party-containers.yaml`.

### Nyambungake panyedhiya H5P <!--quire:connecting-an-h5p-provider-->

Quire ora nempelake utawa ngirim runtime utawa sidecar H5P (ADR 0019). Yen nggunakake H5P, sedhiyakake langganan sing dihosting dhewe utawa operasikake instance H5P sing dihosting dhewe kapisah saka Quire. Dhaptarake panyedhiya kasebut minangka piranti eksternal LTI 1.3 lan tambah kontene menyang kursus minangka aktivitas piranti. Quire ngijolake biji lan kemajuan aktivitas/pambiji liwat LTI Assignment and Grade Services (AGS). Yen panyedhiya uga ngirim pernyataan xAPI, konfigurasi kapisah kanggo statement store xAPI Quire; ijolan biji/kemajuan AGS ora ngirim pernyataan xAPI. Impor Moodle nglaporake aktivitas H5P butuh sambungan piranti LTI. Panyedhiya tetep tanggung jawab kanggo runtime H5P, authoring, bank konten lan riwayat jawabane.

Kanggo mbukak instance sing dihosting dhewe ing host iki, setel `QUIRE_H5P_IMAGE` menyang image-e lan miwiti profil `h5p`. Compose nerbitake ing `QUIRE_H5P_PORT` (8090) lan njaga datane ing volume `h5p-data`; image-e, lan kewajiban sing teka karo iku, tetep duweke panjenengan.

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

Saben proses maca `docker/.env`. Template, `docker/.env.example`, ndhaftar saben setelan kanthi gawane. Kelompoke:

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

| Setelan | Tegese |
| --- | --- |
| `QUIRE_APP_ORIGIN` | Alamat publik LMS, kayata `https://learn.example.com` |
| `QUIRE_CONTENT_ORIGIN` | Origin konten, host beda |
| `QUIRE_PLATFORM_DOMAINS` | Domain panggonan organisasi manggon, dipisahake koma |
| `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` | `compose` ing kene. Deleng pandhuan liyane kanggo `vercel` lan `cloudflare` |
| `QUIRE_TRUSTED_PROXY_CIDRS` | Proxy sing `X-Forwarded-For`-e dipercaya |

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

| Setelan | Tegese |
| --- | --- |
| `QUIRE_SECRET_KEY` | Nandatangani sesi lan token. 64 karakter hex |
| `QUIRE_MASTER_KEY` | Mbungkus kredensial sing disimpen kayata rahasia SSO lan webhook. 32 byte, base64. Tier web lan worker butuh nilai sing padha. Rotasi: [key-rotation.md](/jv/ops/key-rotation/) |
| `QUIRE_MASTER_KEY_VERSION` | Label versi kunci master, `v1` nalika ora disetel. Naikke nalika muter |
| `QUIRE_MASTER_KEY_RETIRED` | Kunci master sadurunge sing isih dibutuhake kanggo maca apa sing disegel, minangka `v1=<base64>`. Copot sawise rotasi rampung tanpa sing ora dirampungake |
| `QUIRE_COLLAB_SIGNING_KEY` | Dienggo bareng web lan collab kanggo nandatangani token nyunting |
| `QUIRE_BACKUP_SIGNING_KEY` | Nandatangani serep kursus (opsional) |

Simpen salinan `QUIRE_MASTER_KEY` ing papan liya saka host iki. Database sing dibalekake tanpane ora bisa dekripsi kredensial sing dicekel.

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

| Setelan | Tegese |
| --- | --- |
| `POSTGRES_PASSWORD` | Superuser, digunakake dening container lan serep |
| `QUIRE_DB_APP_PASSWORD`, `QUIRE_DB_MIGRATOR_PASSWORD`, `QUIRE_DB_REPORT_PASSWORD`, `QUIRE_DB_AUDIT_PASSWORD` | Tembung sandhi peran, disetel ing miwiti kapisan |
| `DATABASE_URL` | Peran aplikasi. Keamanan tingkat baris berlaku kanggo saben query sing digawe |
| `DATABASE_MIGRATOR_URL`, `QUIRE_MIGRATION_URL` | Peran migrator, kanggo `migrate` lan `init` |
| `QUIRE_SUPERUSER_URL` | Digunakake mung dening mlaku kapisan |
| `QUIRE_REPORT_DATABASE_URL` | Peran laporan read-only, kanggo laporan lan pembangun laporan |
| `QUIRE_AUDIT_DATABASE_URL` | Peran audit, kanggo konsol audit lan ekspor SIEM |
| `QUIRE_DATABASE_ID` | UUID apa wae, tetep suwene instalasi |

Tembung sandhi peran ditrapake mung nalika volume database pisanan digawe. Kanggo ngganti mengko, gunakake `ALTER ROLE` banjur nganyari URL sing cocog.

`QUIRE_REPORT_DATABASE_URL` digunakake kanggo database fisik sing dikonfigurasi dening `DATABASE_URL`. Kanggo database fisik kadhaptar liyane, setel URL sambungan `quire_report` dhewe ing lingkungan web lan worker, banjur lebokake jeneng variabel ing kolom **Reporting environment variable** database kasebut minangka `env:NAME`. Referensi kudu nuduhake database sing padha karo sambungan appe, idealnya replika wacane. Saben permukaan laporan ngetutake tenant menyang sambungan laporan database dhewe: pembangun laporan lan laporan sing disimpen, pangiriman terjadwal, ekspor laporan, analitik, log audit, sumber audit REST lan panelusuran audit asisten. Ora ana sing tau nyilih URL laporan database liya. Nalika database ora duwe sambungan laporan, laporan biasa mlaku ing sambungan aplikasi database dhewe, dene analitik lan saben wacan audit nolak lan ngandhani, amarga peran aplikasi ora bisa maca jejak audit.

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

| Setelan | Rilis iki | Cathetan |
| --- | --- | --- |
| `QUIRE_STORAGE_DRIVER` | `local` (gawan), `s3` utawa `azure` | `local` nyimpen file ing volume `files`. `s3` nyakup AWS S3, R2, interoperabilitas GCS lan toko kompatibel S3 liyane, kanthi unggahan multipart sing bisa diterusake |
| `QUIRE_REALTIME_DRIVER` | `inprocess` (gawan), `sse`, `centrifugo` utawa `durable_objects` | `inprocess` pas kanggo siji container web; gunakake `centrifugo` utawa `sse` nalika ana pirang |
| `QUIRE_CACHE_DRIVER` | `memory` (gawan), `postgres` utawa `valkey` | `memory` per proses; gunakake `valkey` utawa `postgres` supaya wates rate tahan ing antar container |
| `QUIRE_VIDEO_DRIVER` | `ffmpeg` (gawan) utawa `progressive_mp4` | Utawa panyedhiya sing dihosting: Cloudflare Stream, Mux utawa Bunny, miturut kunce |
| `QUIRE_IMAGE_DRIVER` | `noop` (gawan), `imgproxy` utawa `cloudflare` | `noop` ngladeni saben gambar ing ukuran asline. `imgproxy` butuh profil `images` lan setelan ing ngisor; `cloudflare` nggunakake Cloudflare Images |
| `QUIRE_MEETING_PROVIDER` | `bbb`, `zoom`, `teams`, `meet`, `jitsi` utawa `in_process` | Gawan platform kanggo sesi langsung. Ora disetel, sesi langsung ngandhani ora dikonfigurasi, nganti organisasi nyambungake akune dhewe ing sangisore Integrations, Live session provider. Akun dhewe organisasi tansah menang tinimbang nilai iki. Setelan dhewe saben panyedhiya (`BBB_URL` lan `BBB_SECRET`, variabel `ZOOM_*`, `TEAMS_*`, `GOOGLE_MEET_*` lan `JITSI_*`) diwaca mung kanggo panyedhiya sing dijenengi ing kene |
| `QUIRE_MEETING_REGIONS` | Dhaptar koma `eu`, `uk`, `us` | Panggonan panyedhiya gawan platform ngolah rapat. Ora disetel, ora dipriksa nglawan organisasi sing disematake menyang wilayah, kaya sadurunge. Akun dhewe organisasi nyatakake wilayahe ing kacane |

Nilai driver sing ora kalebu rilis iki ditolak nalika tier web miwiti, kanthi setelan dijenengi, tinimbang diganti meneng dening gawan.

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

Kaca njaluk gambar ing patang ukuran tetep liwat `/api/files/{id}/image/{size}`, sing mriksa akses sing padha karo file dhewe banjur ngalihake menyang layanan gambar. Saben organisasi bisa njaluk `QUIRE_IMAGE_SPECS_PER_HOUR` (gawan 2000) pasangan gambar lan ukuran anyar sejam; ukuran sing wis diprodhuksi jam kasebut ora dietung. Gunakake `valkey` utawa `postgres` kanggo `QUIRE_CACHE_DRIVER` kanthi luwih saka siji container web, supaya wates tahan ing antarane.

| Setelan | Driver | Cathetan |
| --- | --- | --- |
| `IMGPROXY_URL` | `imgproxy` | Alamat sing digayuh browser imgproxy, contone `https://images.example.org`. Profil `images` nerbitake ing `QUIRE_IMAGES_PORT` (8082) |
| `IMGPROXY_KEY`, `IMGPROXY_SALT` | `imgproxy` | String hex, nilai sing padha imgproxy diwiwiti. Gawe saben nganggo `openssl rand -hex 32`. Quire nandatangani saben alamat gambar karo iku, supaya imgproxy ora ngrender apa sing ora dijaluk Quire |
| `QUIRE_IMAGE_SOURCE_ORIGIN` | `imgproxy` kanthi panyimpenan lokal | Panggonan imgproxy njupuk asline. Compose nyetel `http://web:3000`. Kanthi panyimpenan `s3` utawa `azure` imgproxy njupuk saka bucket lan iki ora digunakake |
| `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_IMAGES_TOKEN`, `CLOUDFLARE_IMAGES_ACCOUNT_HASH` | `cloudflare` | Token API kanthi ijin edit Images, lan hash akun saka Images, Developer resources. Uripake varian fleksibel kanggo akun |
| `CLOUDFLARE_IMAGES_SIGNING_KEY` | `cloudflare` | Opsional. Nalika disetel, gambar pribadi lan saben alamat ditandatangani lan kadaluwarsa. Tanpane, gambar publik ing alamat sing asale saka `QUIRE_SECRET_KEY` sing ora bisa ditebak sapa wae |

Cloudflare Images nyimpen salinane dhewe saben asli sing dilayani. Nalika file dibusak, worker mbusak salinan kasebut sadurunge asline.

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

Pagawean latar nggunakake pg-boss ing database Postgres sing padha, supaya ora ana layanan antrean sing dibukak lan ora ana sing dikonfigurasi. Pagawean diantrekake ing transaksi sing padha karo owahan sing njalari, supaya crash ora bisa ilang siji utawa ngirim kaping pindho. `QUIRE_QUEUE_DRIVER` iku `pgboss` ing kene, gawane; `vercel` lan `cloudflare` mindhah mung pangiriman notifikasi lan webhook entheng menyang antreane platform dhewe, lan pandhuan Vercel lan Cloudflare njlentrehake lan carane tier web-e ngantrekake.

### Email <!--quire:email-->

Setel salah siji:

- `QUIRE_EMAIL_PROVIDER_CONFIG`: objek JSON sing nyebutake panyedhiya HTTP lan kredensiale, kayata `{"provider":"postmark","token":"..."}`. Postmark, Amazon SES, Mailgun, SendGrid lan Resend didhukung.
- `QUIRE_SMTP_URL`: `smtp://user:password@host:587`. Target iki wae; target serverless mblokir SMTP.

`QUIRE_MAIL_FROM` iku pangirim. Kanggo nyoba Quire, miwiti profil `devmail`, setel `QUIRE_SMTP_URL=smtp://mailpit:1025`, lan waca mail ing `http://localhost:8025`.

### Layanan opsional <!--quire:optional-services-->

| Setelan | Kanthi profil |
| --- | --- |
| `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` utawa `QUIRE_MEILISEARCH_URL` | Panelusuran eksternal; teks lengkap Postgres yen ora |
| `QUIRE_BREACH_CHECK_PROVIDER=off`, `QUIRE_BREACH_CHECK_URL` | Pamriksan pelanggaran tembung sandhi. Urip kanthi gawan nglawan `api.pwnedpasswords.com` (mung prefiks hash limang karakter sing dikirim); `off` mateni, lan URL nuduhake menyang range API sing dihosting dhewe |

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

`OTEL_EXPORTER_OTLP_ENDPOINT` nyebutake kolektor sing dikirimi saben proses trace lan metrik; kanthi profil `observability` iku `http://otelcol:4318`, lan `docker/otel-collector.yaml` iku panggonan nambah exporter kanggo backend panjenengan. Proses tier web, worker, scheduler, content lan collab ngekspor span liwat OTLP/HTTP (panjalukan web, transaksi database tenant, pagawean worker lan telpon metu) nalika disetel, lan metrik menyang endpoint sing padha saben menit (`OTEL_METRICS_EXPORTER=none` mateni). `OTEL_TRACES_SAMPLER_ARG` nyetel bagean trace sing disimpen. Log menyang output standar ing `LOG_LEVEL`, lan Compose muter. Trace ora tau nggawa data pribadi.

### Egress regional (residensi data EU) <!--quire:regional-egress-eu-data-residency-->

`QUIRE_REGION=eu` ngandhani stack ngladeni organisasi Uni Eropa. Worker banjur nyekel saben panjalukan metu sing digawe kanggo organisasi sing disematake menyang EU menyang allowlist (21-compliance.md bagean 8.1). Allowlist iku host sing dideklarasikake layanan sing dikonfigurasi kanggo wilayah (endpoint panyimpenan, panyedhiya email, panyedhiya video sing dihosting, target panyimpenan dhewe organisasi, panyedhiya AI lan akun email), host layanan apa wae ing sangisore derogasi aktif, lan host sing kadhaptar ing `QUIRE_EGRESS_ALLOW_HOSTS`. Panjalukan menyang host publik liyane ditolak sadurunge dikirim, penolakan ditulis menyang jejak audit organisasi minangka `privacy/egress_refused`, lan kadhaptar ing sangisore Compliance, Data residency.

| Setelan | Nilai | Efek |
| --- | --- | --- |
| `QUIRE_EGRESS_ALLOW_HOSTS` | Dhaptar koma hostname, utawa `*.example.org` kanggo saben subdomain | Host tambahan sing bisa digayuh organisasi EU. Endpoint webhook, xAPI lan SIEM, feed blog lan host Amazon SES kalebu ing kene, amarga iku pilihane organisasi dhewe lan ora ana layanan sing mendeklarasikake. Loopback, alamat pribadi lan jeneng label tunggal kayata `web` utawa `clamav` iku jaringan dhewe lan ora tau dipriksa |

Organisasi UK lan US ora dicekel menyang dhaptar host; njaga pamriksan wilayah layanan. Setel dhaptar ing worker; kaca admin maca ing tier web kanggo nuduhake allowlist, dadi lebokake ing `docker/.env`, sing diwaca saben layanan.

Pamriksan aplikasi menehi kaluputan cetha lan entri audit, lan dudu jaminan: kode bisa salah. Jaminane iku jaringan. Compose ora negakake kanggo panjenengan. Kanggo stack regional, lebokake layanan `worker` lan `web` ing jaringan `internal: true` sing mung rute metune proxy egress (contone Squid utawa container tinyproxy) sing ngidinake host sing padha karo `QUIRE_EGRESS_ALLOW_HOSTS` ditambah host layanan sing dikonfigurasi, lan setel `HTTPS_PROXY` kanggo layanan kasebut. Kaca residensi ndhaftar host persis sing diidinake aplikasi, supaya kalorone dhaptar bisa dibandhingake.

## Kesehatan <!--quire:health-->

| Endpoint | Tegese |
| --- | --- |
| `/healthz` | Liveness: prosese mangsuli. Pamriksan kesehatan Compose nggunakake iki |
| `/readyz` | Readiness: dependensi bisa digayuh, lan saben layanan opsional dilaporake dikonfigurasi utawa ora. Tudingake load balancer ing kene |

`docker compose -f docker/compose.yaml ps` nuduhake kesehatane saben layanan.

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

Layanan `proxy` (Caddy, Apache-2.0, `docker/caddy/Caddyfile`) iku bagean saka stack gawan. Mangsuli ing port 80 lan 443 lan rute:

| Host utawa path | Menyang |
| --- | --- |
| `QUIRE_PROXY_CONTENT_HOST` | `content` |
| `QUIRE_PROXY_APP_HOST`, saben subdomain tenant lan domain khusus | `web` |
| `/_collab/` ing host kasebut | `collab` (websocket, `QUIRE_COLLAB_URL`) |
| `/_realtime/connection/` ing host kasebut | websocket klien `centrifugo`; API servere ora tau diekspos |
| `/_images/` ing host kasebut | `imgproxy`, kanthi profil `images` (`IMGPROXY_URL`) |

`init-env.sh` nurunake `QUIRE_PROXY_APP_HOST`, `QUIRE_PROXY_CONTENT_HOST`, `QUIRE_PROXY_HTTPS_PORT`, `QUIRE_COLLAB_URL` lan `IMGPROXY_URL` saka kalorone origin, supaya ora bisa beda. Owahi bebarengan yen ngganti origin kanthi tangan.

Sertifikat ngetutake `QUIRE_PROXY_TLS`:

- `internal` (gawan): certificate authority Caddy dhewe, kanggo `localhost`, `*.localhost` lan `lvh.me`. Percaya roote sapisan, banjur browsing:

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

  Tambah `quire-local-ca.crt` menyang trust store sistem utawa browser. `curl` nampa karo `--cacert`.
- Alamat email: sertifikat ACME otomatis (Let's Encrypt, banjur ZeroSSL) kanggo hostname asli. DNS kanggo kalorone origin lan saben host tenant kudu nuduhake ing kene, lan port 80 lan 443 kudu bisa digayuh saka internet.

Host tenant diterbitake nalika dibutuhake, ing kunjungan kapisan, lan mung nalika web konfirmasi jenenge kagungane instalasi iki (`/tls-allowed`, ditakokake ing jaringan Compose). Ora butuh sertifikat wildcard utawa plugin panyedhiya DNS, lan wong asing sing nuduhake jeneng menyang host ora bisa nggawe njaluk sertifikat. Sertifikat lan authority lokal manggon ing volume `caddy-data`; serep karo liyane yen nggunakake `internal`.

Web percaya `X-Forwarded-For` saka proxy wae: proxy duwe alamat tetep (`QUIRE_PROXY_ADDRESS`, gawan `172.29.64.10`) ing subnet tetep (`QUIRE_COMPOSE_SUBNET`), lan `QUIRE_TRUSTED_PROXY_CIDRS` nyebutake alamat kasebut. Yen subnet tabrakan karo jaringan ing host, ganti kalorone lan mbukak `docker compose down` sadurunge `up`.

## Ing mburine reverse proxy dhewe <!--quire:behind-your-own-reverse-proxy-->

Kanggo nggunakake load balancer utawa proxy sing wis dibukak, jarake `proxy` (`docker compose up -d --scale proxy=0`) lan mungkasi TLS ing ngarepe `web` (8080), `content` (8081), `collab` (1234, websocket) lan `centrifugo` (8000, websocket). Setel alamat publik ing `QUIRE_APP_ORIGIN`, `QUIRE_CONTENT_ORIGIN` lan `QUIRE_COLLAB_URL` (`wss://`), lan rentang alamat proxye ing `QUIRE_TRUSTED_PROXY_CIDRS`.

## Ngatasi masalah <!--quire:troubleshooting-->

- `init` metu kanthi "QUIRE_DATABASE_ID is not a UUID": setel nganggo `uuidgen`.
- `web` miwiti maneh kanthi "did not start on compose": log ndhaftar saben setelan sing ora bisa dihormati lan apa sing digunakake.
- Ngganti tembung sandhi peran ing `.env` sawise miwiti kapisan ora nindakake apa-apa: skrip init mlaku sapisan. Gunakake `ALTER ROLE`.
- Unggahan gagal kanthi kaluputan scan selagi `CLAMAV_URL` disetel: ClamAV ngundhuh signature ing miwiti kapisan, sing butuh sawetara menit.

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