---
title: "Memasang Quire dengan Docker Compose"
description: "Pasang Quire pada infrastruktur anda sendiri dengan Docker Compose."
image: "https://docs.quirelms.com/og.png"
---

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

# Memasang Quire dengan Docker Compose

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

Ini ialah produk penuh pada satu hos: LMSnya, kerja latar belakangnya, perkhidmatan
masa nyata dan penyuntingan kolaboratif, dan setiap perkhidmatan pilihan di belakang
satu profil. Reka bentuknya ialah `docs/architecture/23-ops.md` seksyen 2.

Sasaran lain: [Vercel](/ms/ops/vercel/) dan [Cloudflare Workers](/ms/ops/cloudflare/)
menjalankan lapisan web sahaja. Naik taraf terdapat dalam [upgrade.md](/ms/ops/upgrade/),
dan sandaran serta latihan pemulihan dalam [backup-restore.md](/ms/ops/backup-restore/).

## Apa yang anda perlukan <!--quire:what-you-need-->

- Docker Engine 27 atau lebih baru dengan plugin Compose 2.30 atau lebih baru.
- 4 teras CPU dan 8 GB memori untuk tindanan lalai; 8 teras dan 16 GB dengan
  `--profile full` (ClamAV sahaja memegang kira-kira 1.5 GB tandatangan).
- Satu nama DNS untuk lapisan web dan satu lagi untuk kandungan tidak dipercayai.
  Keduanya mesti hos yang berbeza: pakej SCORM dan HTML yang dimuat naik berjalan
  pada asal kandungan supaya ia tidak pernah boleh membaca kuki LMS.
- Untuk satu ujian setempat, `lvh.me` dan `*.localhost` menyelesaikan kepada
  127.0.0.1, yang merupakan apa yang digunakan oleh `docker/.env.example`.
  Perkhidmatan `proxy` tindanan itu sendiri menyajikan kedua-duanya melalui https
  dengan satu autoriti sijil setempat, jadi tiada apa-apa yang lain dipasang
  (lihat "TLS").
- Port 80 dan 443 bebas pada hos (`QUIRE_PROXY_HTTP_PORT` dan
  `QUIRE_PROXY_HTTPS_PORT` memindahkannya).

## Jalanan pertama <!--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` menulis `docker/.env` daripada `docker/.env.example`
dengan setiap rahsia yang dijana (kata laluan pangkalan data, kunci tandatangan
dan kunci induk, pasangan kunci pelancaran kandungan) serta kunci tandatangan
titik semak audit dalam `docker/secrets/audit-signing-key.pem`, yang dipasang
Compose ke dalam para pekerja sebagai satu rahsia. Ia hanya memerlukan `sh`, `awk`
dan `openssl`, dan ia enggan menulis gantikan satu `docker/.env` yang sedia ada.
Salin kedua-dua fail itu keluar daripada hos: tanpa `QUIRE_MASTER_KEY`, sebuah
pangkalan data yang dipulihkan tidak dapat menyahsulit kelayakannya yang disimpan.
Untuk mengisi fail itu dengan tangan sebaliknya, `cp docker/.env.example docker/.env`;
fail itu menyatakan cara menjana setiap rahsia.

Kedua-dua asal mesti `https`: perkhidmatan kandungan menolak http biasa dalam
produksi, dan kedua-duanya tidak boleh berkongsi satu domain boleh didaftarkan.
Perkhidmatan `proxy` menamatkan TLS untuk kedua-duanya (lihat "TLS");
`init-env.sh` menolak satu asal `http://`.

Tindanan bermula dalam tetap urutan, dan setiap langkah menunggu yang sebelumnya:

1. `postgres` menjadi sihat. Pada but pertama, skrip ininya
   (`docker/postgres/init/90-passwords.sh`) menetapkan empat kata laluan peranan.
2. `migrate` memohon setiap migrasi dan membina semula giliran tugas dalam
   pangkalan data kawalan dan dalam setiap pangkalan data penyewa khusus, menyemak
   bahawa kesemuanya bersetuju, kemudian keluar (docs/ops/upgrade.md).
   Migrasi berjalan pada setiap permulaan dan adalah idempoten, jadi satu naik
   taraf ialah satu imej baharu dan satu mula semula.
3. `init` (`apps/web/src/first-run.ts`) merekod pangkalan data aplikasi di bawah
   `QUIRE_DATABASE_ID` dan, apabila `QUIRE_SETUP_ADMIN_EMAIL` ditetapkan, mencipta
   organisasi pertama dan pentadbirnya. Alamat log masuk dan satu kata laluan
   yang dijana dicetak sekali, dalam `docker compose logs init`.
4. `web`, `content`, `worker`, `scheduler`, `collab` dan `centrifugo` bermula.
5. `proxy` bermula sebaik sahaja `web` dan `content` sihat.

Buka `https://demo.` diikuti oleh domain aplikasi anda (log `init` mencetak alamat
log masuk yang tepat), dan log masuk. Pada sebuah pemasangan setempat, percayai
autoriti sijil proxy terlebih dahulu (lihat "TLS"). Tukar kata laluan yang dijana
di `/account/security`.

Sebuah proses yang dimulakan tanpa satu rahsia yang diperlukan enggan bermula dan
menamakan tetapan yang hilang dalam lognya. Tiada apa-apa bermula separuh
dikonfigurasi.

## Perkhidmatan dan profil <!--quire:services-and-profiles-->

| Perkhidmatan | Profil | Apa yang ia lakukan |
| --- | --- | --- |
| postgres | sentiasa | Pangkalan data (PostgreSQL 18 dengan pgvector, dibina daripada `docker/postgres.Dockerfile`), dengan WAL diarkibkan daripada but pertama |
| migrate, init | sentiasa | Sekali jalan: migrasi, kemudian jalanan pertama |
| web | sentiasa | LMS, pada `QUIRE_HTTP_PORT` (8080) |
| content | sentiasa | Asal kandungan tidak dipercayai, pada `QUIRE_CONTENT_PORT` (8081) |
| worker | sentiasa | Tugas latar belakang: e-mel, laporan, pemprosesan fail, webhook |
| scheduler | sentiasa | Tugas berulang: mendaftarkan 64 jadual masa laksana dan menyerahkannya kepada pekerja; satu pemimpin pada satu masa |
| collab | sentiasa | Websocket penyuntingan kolaboratif, pada `QUIRE_COLLAB_HTTP_PORT` (1234) |
| centrifugo | sentiasa | Pengedaran masa nyata, pada `QUIRE_REALTIME_PORT` (8000) |
| proxy | sentiasa | Caddy, pintu hadapan TLS pada port 80 dan 443 (lihat "TLS") |
| valkey | `cache` | Cache dan had kadar |
| clamav | `scan` | Pengimbasan perisian hasad untuk muat naik |
| gotenberg | `preview` | Pratonton Office ke PDF, rendering sijil |
| imgproxy | `images` | Imej yang diubah saiz dan ditukar format |
| transcoder | `video` | Imej pekerja dengan ffmpeg sahaja-LGPL, untuk rendition video |
| seaweedfs | `storage` | Storan objek serasi S3 pada hos ini |
| otelcol | `observability` | Sebuah pengumpul OpenTelemetry |
| mailpit | `devmail` | Menangkap semua mel keluar, untuk mencuba Quire |
| backup | `backup` | Sandaran asas sekali jalan; lihat backup-restore.md |
| backup-scheduler, backup-offsite | `backup` | Satu sandaran asas setiap `QUIRE_BACKUP_INTERVAL_HOURS`, dan salinan bersulit di luar hos dengan satu latihan pengesahan mingguan |
| h5p | `h5p` | Imej alat LTI 1.3 H5P yang anda bekalkan dalam `QUIRE_H5P_IMAGE`, pada `QUIRE_H5P_PORT` (8090); lihat "Menyambungkan satu pembekal H5P" |

`--profile full` memulakan setiap perkhidmatan pilihan kecuali `backup` dan `h5p`.
Mulakan satu dengan `docker compose -f docker/compose.yaml --profile scan up -d`.
Tanpa satu perkhidmatan pilihan, Quire masih berfungsi dan menyatakan apa yang
hilang: tiada pengimbas bermakna muat naik disimpan tanpa diimbas dan pentadbir
diberitahu; tiada Gotenberg bermakna fail menawarkan muat turun dan bukannya satu
pratonton; tiada transcoder bermakna video dimainkan sebagai fail asal.

Setiap imej pihak ketiga dan obligasi lesennya disenaraikan dalam
`docker/third-party-containers.yaml`.

### Menyambungkan satu pembekal H5P <!--quire:connecting-an-h5p-provider-->

Quire tidak membenam atau menghantar satu masa laksana H5P atau sidecar (ADR
0019). Jika anda menggunakan H5P, bekalkan langganan hos anda sendiri atau
kendalikan satu instans H5P kendiri anda sendiri secara berasingan daripada Quire.
Daftarkan pembekal itu sebagai satu alat luaran LTI 1.3 dan tambahkan
kandungannya kepada kursus sebagai aktiviti alat. Quire bertukar gred dan kemajuan
aktiviti/penandaan melalui LTI Assignment and Grade Services (AGS). Jika pembekal
itu juga menghantar pernyataan xAPI, konfigurasikannya secara berasingan untuk
stor pernyataan xAPI Quire; pertukaran gred/kemajuan AGS tidak menghantar pernyataan
xAPI. Import Moodle melaporkan aktiviti H5P sebagai memerlukan satu sambungan alat
LTI. Pembekal itu kekal bertanggungjawab ke atas masa laksana H5Pnya, pembuatan
kandungan, bank kandungan dan sejarah percubaannya.

Untuk menjalankan satu instans kendiri anda sendiri pada hos ini, tetapkan
`QUIRE_H5P_IMAGE` kepada imejnya dan mulakan profil `h5p`. Compose menerbitkannya
pada `QUIRE_H5P_PORT` (8090) dan menyimpan datanya dalam volum `h5p-data`; imej
itu, dan obligasi yang datang dengannya, kekal milik anda.

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

Setiap proses membaca `docker/.env`. Templat, `docker/.env.example`, menyenaraikan
setiap tetapan dengan lalainya. Kumpulannya:

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

| Tetapan | Makna |
| --- | --- |
| `QUIRE_APP_ORIGIN` | Alamat awam LMS, seperti `https://learn.example.com` |
| `QUIRE_CONTENT_ORIGIN` | Asal kandungan, hos yang berbeza |
| `QUIRE_PLATFORM_DOMAINS` | Domain tempat organisasi berada, dipisahkan 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` di sini. Lihat panduan lain untuk `vercel` dan `cloudflare` |
| `QUIRE_TRUSTED_PROXY_CIDRS` | Proksi yang `X-Forwarded-For`nya dipercayai |

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

| Tetapan | Makna |
| --- | --- |
| `QUIRE_SECRET_KEY` | Menandatangani sesi dan token. 64 aksara heksadesimal |
| `QUIRE_MASTER_KEY` | Membungkus kelayakan tersimpan seperti rahsia SSO dan webhook. 32 bait, base64. Lapisan web dan pekerja memerlukan nilai yang sama. Pusingan: [key-rotation.md](/ms/ops/key-rotation/) |
| `QUIRE_MASTER_KEY_VERSION` | Label versi kunci induk, `v1` apabila tidak ditetapkan. Naikkan ia apabila anda memutarkan |
| `QUIRE_MASTER_KEY_RETIRED` | Kunci induk terdahulu yang masih diperlukan untuk membaca apa yang mereka sulitkan, sebagai `v1=<base64>`. Buang selepas satu pusingan selesai tanpa apa-apa yang tidak selesai |
| `QUIRE_COLLAB_SIGNING_KEY` | Dikongsi oleh web dan collab untuk menandatangani token penyuntingan |
| `QUIRE_BACKUP_SIGNING_KEY` | Menandatangani sandaran kursus (pilihan) |

Simpan satu salinan `QUIRE_MASTER_KEY` di tempat lain selain hos ini. Sebuah
pangkalan data yang dipulihkan tanpanya tidak dapat menyahsulit kelayakan yang
disimpannya.

### Pangkalan data <!--quire:database-->

| Tetapan | Makna |
| --- | --- |
| `POSTGRES_PASSWORD` | Superuser, digunakan oleh bekas dan sandaran |
| `QUIRE_DB_APP_PASSWORD`, `QUIRE_DB_MIGRATOR_PASSWORD`, `QUIRE_DB_REPORT_PASSWORD`, `QUIRE_DB_AUDIT_PASSWORD` | Kata laluan peranan, ditetapkan pada but pertama |
| `DATABASE_URL` | Peranan aplikasi. Keselamatan tahap baris terpakai pada setiap pertanyaan yang dibuatnya |
| `DATABASE_MIGRATOR_URL`, `QUIRE_MIGRATION_URL` | Peranan migrator, untuk `migrate` dan `init` |
| `QUIRE_SUPERUSER_URL` | Digunakan hanya oleh jalanan pertama |
| `QUIRE_REPORT_DATABASE_URL` | Peranan laporan hanya-baca, untuk laporan dan pembina laporan |
| `QUIRE_AUDIT_DATABASE_URL` | Peranan audit, untuk konsol audit dan eksport SIEM |
| `QUIRE_DATABASE_ID` | Sebarang UUID, ditetapkan sepanjang hayat pemasangan |

Kata laluan peranan hanya dipohon apabila volum pangkalan data mula-mula dicipta.
Untuk mengubah salah satunya kemudian, gunakan `ALTER ROLE` dan kemudian kemas kini
URL yang sepadan.

`QUIRE_REPORT_DATABASE_URL` digunakan untuk pangkalan data fizikal yang
dikonfigurasi oleh `DATABASE_URL`. Bagi mana-mana pangkalan data fizikal berdaftar
yang lain, tetapkan URL sambungan `quire_report`nya sendiri dalam persekitaran web
dan pekerja, kemudian letakkan nama pemboleh ubah dalam medan **Pemboleh ubah
persekitaran pelaporan** pangkalan data itu sebagai `env:NAME`. Rujukan itu mesti
menunjuk kepada pangkalan data yang sama dengan sambungan aplikasinya, sebaiknya
replika bacaannya. Setiap permukaan laporan mengikut penyewa ke sambungan laporan
pangkalan datanya sendiri: pembina laporan dan laporan yang disimpan, penghantaran
berjadual, eksport laporan, analitik, log audit, sumber audit REST dan carian audit
pembantu. Tiada satu pun daripadanya meminjam URL laporan pangkalan data yang lain.
Apabila sebuah pangkalan data tiada sambungan laporan, laporan biasa berjalan pada
sambungan aplikasi pangkalan data itu sendiri, manakala analitik dan setiap bacaan
audit menolak dan menyatakan demikian, kerana peranan aplikasi tidak dapat membaca
jejak audit.

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

| Tetapan | Keluaran ini |Nota |
| --- | --- | --- |
| `QUIRE_STORAGE_DRIVER` | `local` (lalai), `s3` atau `azure` | `local` menyimpan fail dalam volum `files`. `s3` merangkumi AWS S3, R2, interoperabiliti GCS dan stor serasi S3 lain, dengan muat naik berbilang bahagian boleh disambung semula |
| `QUIRE_REALTIME_DRIVER` | `inprocess` (lalai), `sse`, `centrifugo` atau `durable_objects` | `inprocess` sesuai untuk satu bekas web; gunakan `centrifugo` atau `sse` apabila ada beberapa |
| `QUIRE_CACHE_DRIVER` | `memory` (lalai), `postgres` atau `valkey` | `memory` adalah setiap proses; gunakan `valkey` atau `postgres` supaya had kadar kekal merentas bekas |
| `QUIRE_VIDEO_DRIVER` | `ffmpeg` (lalai) atau `progressive_mp4` | Atau satu pembekal hos: Cloudflare Stream, Mux atau Bunny, mengikut kunci mereka |
| `QUIRE_IMAGE_DRIVER` | `noop` (lalai), `imgproxy` atau `cloudflare` | `noop` menyajikan setiap imej pada saiz asalnya. `imgproxy` memerlukan profil `images` dan tetapan di bawah; `cloudflare` menggunakan Cloudflare Images |
| `QUIRE_MEETING_PROVIDER` | `bbb`, `zoom`, `teams`, `meet`, `jitsi` atau `in_process` | Lalai platform untuk sesi langsung. Tidak ditetapkan, sesi langsung menyatakan ia tidak dikonfigurasi, sehinggalah sebuah organisasi menyambungkan akaunnya sendiri di bawah Integrasi, Pembekal sesi langsung. Akaun sesebuah organisasi sentiasa mengatasi nilai ini. Tetapan pembekal masing-masing (`BBB_URL` dan `BBB_SECRET`, pemboleh ubah `ZOOM_*`, `TEAMS_*`, `GOOGLE_MEET_*` dan `JITSI_*`) dibaca hanya untuk pembekal yang dinamakan di sini |
| `QUIRE_MEETING_REGIONS` | Senarai koma `eu`, `uk`, `us` | Di mana pembekal lalai platform memproses mesyuarat. Tidak ditetapkan, ia tidak disemak terhadap sebuah organisasi yang disemat pada satu wilayah, seperti sebelumnya. Akaun sesebuah organisasi menyatakan wilayahnya pada halamannya |

Sebuah nilai pemandu yang tidak termasuk dalam keluaran ini ditolak apabila
lapisan web bermula, dengan tetapan itu dinamakan, dan bukannya digantikan secara
senyap oleh lalai.

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

Halaman meminta imej pada empat saiz tetap melalui
`/api/files/{id}/image/{size}`, yang menyemak akses yang sama seperti fail itu
sendiri dan kemudian mengalihkan ke perkhidmatan imej. Setiap organisasi boleh
meminta `QUIRE_IMAGE_SPECS_PER_HOUR` (lalai 2000) pasangan imej dan saiz baharu
sejam; saiz yang sudah dihasilkan pada jam itu tidak dikira. Gunakan `valkey` atau
`postgres` untuk `QUIRE_CACHE_DRIVER` dengan lebih daripada satu bekas web, supaya
had itu kekal di antaranya.

| Tetapan | Pemandu | Nota |
| --- | --- | --- |
| `IMGPROXY_URL` | `imgproxy` | Alamat yang dicapai oleh peramban untuk imgproxy, contohnya `https://images.example.org`. Profil `images` menerbitkannya pada `QUIRE_IMAGES_PORT` (8082) |
| `IMGPROXY_KEY`, `IMGPROXY_SALT` | `imgproxy` | Rentetan heksadesimal, nilai yang sama dengan yang imgproxy dimulakan. Jana setiap satu dengan `openssl rand -hex 32`. Quire menandatangani setiap alamat imej dengannya, jadi imgproxy tidak merender apa-apa yang tidak diminta oleh Quire |
| `QUIRE_IMAGE_SOURCE_ORIGIN` | `imgproxy` dengan storan setempat | Di mana imgproxy mendapatkan imej asal. Compose menetapkan `http://web:3000`. Dengan storan `s3` atau `azure`, imgproxy mendapatkan daripada baldi dan ini tidak digunakan |
| `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_IMAGES_TOKEN`, `CLOUDFLARE_IMAGES_ACCOUNT_HASH` | `cloudflare` | Satu token API dengan keizinan edit Images, dan hash akaun daripada Images, sumber pembangun. Hidupkan varian fleksibel untuk akaun tersebut |
| `CLOUDFLARE_IMAGES_SIGNING_KEY` | `cloudflare` | Pilihan. Apabila ditetapkan, imej adalah peribadi dan setiap alamat ditandatangani dan tamat tempoh. Tanpanya, imej adalah awam pada alamat yang diturunkan daripada `QUIRE_SECRET_KEY` yang tidak dapat diteka oleh sesiapa |

Cloudflare Images menyimpan salinannya sendiri bagi setiap imej asal yang disajikannya.
Apabila sebuah fail dipadam, pekerja memadam salinan itu sebelum yang asal.

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

Tugas latar belakang menggunakan pg-boss dalam pangkalan data Postgres yang sama,
jadi tiada perkhidmatan giliran untuk dijalankan dan tiada apa-apa untuk
dikonfigurasi. Tugas dijadualkan dalam transaksi yang sama dengan perubahan yang
menyebabkannya, jadi satu kras tidak boleh kehilangan satu tugas atau menghantar
satu dua kali. `QUIRE_QUEUE_DRIVER` ialah `pgboss` di sini, lalainya; `vercel`
dan `cloudflare` hanya memindahkan penghantaran notis ringan dan webhook ke
giliran platform itu sendiri, dan panduan Vercel serta Cloudflare menerangkannya
serta cara lapisan web mereka menjadualkan.

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

Tetapkan salah satu daripada:

- `QUIRE_EMAIL_PROVIDER_CONFIG`: satu objek JSON yang menamakan satu pembekal HTTP
  dan kelayakannya, seperti `{"provider":"postmark","token":"..."}`. Postmark,
  Amazon SES, Mailgun, SendGrid dan Resend disokong.
- `QUIRE_SMTP_URL`: `smtp://user:password@host:587`. Sasaran ini sahaja;
  sasaran tanpa pelayan menghalang SMTP.

`QUIRE_MAIL_FROM` ialah penghantar. Untuk mencuba Quire, mulakan profil
`devmail`, tetapkan `QUIRE_SMTP_URL=smtp://mailpit:1025`, dan baca mel di
`http://localhost:8025`.

### Perkhidmatan pilihan <!--quire:optional-services-->

| Tetapan | Dengan 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` atau `QUIRE_MEILISEARCH_URL` | Carian luar; selain itu teks penuh Postgres |
| `QUIRE_BREACH_CHECK_PROVIDER=off`, `QUIRE_BREACH_CHECK_URL` | Semakan kebocoran kata laluan. Hidup secara lalai terhadap `api.pwnedpasswords.com` (hanya satu awalan hash lima aksara dihantar); `off` mematikannya, dan URL menunjuk kepada satu API julat yang anda kendalikan |

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

`OTEL_EXPORTER_OTLP_ENDPOINT` menamakan pengumpul yang setiap proses menghantar
jejak dan metrik kepadanya; dengan profil `observability` ia ialah
`http://otelcol:4318`, dan `docker/otel-collector.yaml` ialah tempat anda menambah
pengeksport untuk belakang anda. Proses lapisan web, pekerja, perancang, kandungan
dan collab mengeksport span melalui OTLP/HTTP (permintaan web, transaksi pangkalan
data penyewa, tugas pekerja dan panggilan keluar) apabila ia ditetapkan, dan metrik
ke titik akhir yang sama setiap minit (`OTEL_METRICS_EXPORTER=none` mematikannya).
`OTEL_TRACES_SAMPLER_ARG` menetapkan bahagian jejak yang disimpan. Log pergi ke
output piawai pada `LOG_LEVEL`, dan Compose memutarkannya. Jejak tidak pernah
membawa data peribadi.

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

`QUIRE_REGION=eu` menyatakan bahawa tindanan itu melayani organisasi Kesatuan
Eropah. Pekerja kemudian mengehadkan setiap permintaan keluar yang dibuat bagi
sebuah organisasi yang disemat pada EU kepada satu senarai benarkan (21-compliance.md
seksyen 8.1). Senarai benarkan ialah hos yang diisytiharkan oleh perkhidmatan yang
dikonfigurasi untuk wilayah tersebut (titik akhir storan, pembekal e-mel, satu
pembekal video hos, sasaran storan organisasi itu sendiri, pembekal AI dan akaun
e-mel), hos mana-mana perkhidmatan di bawah satu pengecualian aktif, serta hos yang
anda senaraikan dalam `QUIRE_EGRESS_ALLOW_HOSTS`. Satu permintaan kepada mana-mana
hos awam yang lain ditolak sebelum ia dihantarkan, penolakan itu ditulis ke dalam
jejak audit organisasi sebagai `privacy/egress_refused`, dan ia disenaraikan di
bawah Pematuhan, Tempat tinggal data.

| Tetapan | Nilai | Kesannya |
| --- | --- | --- |
| `QUIRE_EGRESS_ALLOW_HOSTS` | Senarai koma nama hos, atau `*.example.org` untuk setiap subdomain | Hos tambahan yang boleh dicapai oleh sebuah organisasi EU. Titik akhir webhook, xAPI dan SIEM, umpan blog dan hos Amazon SES tergolong di sini, kerana ia ialah pilihan organisasi itu sendiri dan tiada perkhidmatan yang mengisytiharkannya. Gelung getar, alamat persendirian dan nama label tunggal seperti `web` atau `clamav` ialah rangkaian anda sendiri dan tidak pernah disemak |

Organisasi UK dan AS tidak terikat kepada satu senarai hos; mereka mengekalkan
semakan wilayah perkhidmatan. Tetapkan senarai itu pada pekerja; halaman pentadbir
membacanya pada lapisan web untuk memaparkan senarai benarkan, jadi letakkannya
dalam `docker/.env`, yang dibaca oleh setiap perkhidmatan.

Semakan aplikasi memberikan satu ralat yang jelas dan satu entri audit, dan ia
bukan jaminannya: kod boleh salah. Jaminannya ialah rangkaian. Compose tidak
menguatkuasakannya untuk anda. Untuk satu tindanan serantau, letakkan perkhidmatan
`worker` dan `web` pada satu rangkaian `internal: true` yang satu-satunya laluannya
keluar ialah satu proksi egress (contohnya satu bekas Squid atau tinyproxy) yang
membenarkan hos yang sama seperti `QUIRE_EGRESS_ALLOW_HOSTS` ditambah hos
perkhidmatan yang dikonfigurasi, dan tetapkan `HTTPS_PROXY` untuk perkhidmatan
tersebut. Halaman tempat tinggal menyenaraikan hos yang tepat yang dibenarkan oleh
aplikasi, supaya kedua-dua senarai itu boleh dibandingkan.

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

| Titik akhir | Makna |
| --- | --- |
| `/healthz` | Kelayakan hidup: proses menjawab. Semakan kesihatan Compose menggunakan ini |
| `/readyz` | Kesediaan: kebergantungan boleh dicapai, dan setiap perkhidmatan pilihan melaporkan sama ada dikonfigurasi atau tidak. Tunjukkan pengimbang beban anda ke sini |

`docker compose -f docker/compose.yaml ps` menunjukkan kesihatan setiap
perkhidmatan.

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

Perkhidmatan `proxy` (Caddy, Apache-2.0, `docker/caddy/Caddyfile`) ialah
sebahagian daripada tindanan lalai. Ia menjawab pada port 80 dan 443 dan
menghala kepada:

| Hos atau laluan | Pergi ke |
| --- | --- |
| `QUIRE_PROXY_CONTENT_HOST` | `content` |
| `QUIRE_PROXY_APP_HOST`, setiap subdomain penyewa dan domain tersuai | `web` |
| `/_collab/` pada hos tersebut | `collab` (websocket, `QUIRE_COLLAB_URL`) |
| `/_realtime/connection/` pada hos tersebut | websocket klien `centrifugo`; API pelayannya tidak pernah didedahkan |
| `/_images/` pada hos tersebut | `imgproxy`, dengan profil `images` (`IMGPROXY_URL`) |

`init-env.sh` menurunkan `QUIRE_PROXY_APP_HOST`, `QUIRE_PROXY_CONTENT_HOST`,
`QUIRE_PROXY_HTTPS_PORT`, `QUIRE_COLLAB_URL` dan `IMGPROXY_URL` daripada kedua-dua
asal, supaya ia tidak boleh bergerak jauh antara satu sama lain. Edit mereka
bersama jika anda mengubah satu asal dengan tangan.

Sijil mengikut `QUIRE_PROXY_TLS`:

- `internal` (lalai): autoriti sijil Caddy sendiri, untuk `localhost`,
  `*.localhost` dan `lvh.me`. Percayai akarnya sekali, kemudian layari:

  ```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` ke stor kepercayaan sistem atau peramban. `curl`
  menerimanya dengan `--cacert`.
- Satu alamat e-mel: sijil ACME automatik (Let's Encrypt, kemudian ZeroSSL) untuk
  nama hos sebenar. DNS untuk kedua-dua asal dan setiap hos penyewa mesti
  menunjuk ke sini, dan port 80 dan 443 mesti boleh dicapai daripada internet.

Hos penyewa dikeluarkan atas permintaan, pada lawatan pertama, dan hanya apabila
web mengesahkan bahawa nama itu milik pemasangan ini (`/tls-allowed`, ditanya pada
rangkaian Compose). Tiada sijil wildcard atau plugin pembekal DNS yang diperlukan,
dan seseorang asing yang menunjuk satu nama ke hos itu tidak boleh menyebabkannya
meminta sijil. Sijil dan autoriti setempat berada dalam volum `caddy-data`;
sandarkannya bersama yang lain jika anda menggunakan `internal`.

Web mempercayai `X-Forwarded-For` daripada proxy sahaja: proxy mempunyai satu
alamat tetap (`QUIRE_PROXY_ADDRESS`, lalai `172.29.64.10`) pada satu subringkas
tetap (`QUIRE_COMPOSE_SUBNET`), dan `QUIRE_TRUSTED_PROXY_CIDRS` menamakan alamat
itu. Jika subringkas itu berlanggar dengan satu rangkaian pada hos, ubah kedua-duanya
dan jalankan `docker compose down` sebelum `up`.

## Di belakang proksi songsang anda sendiri <!--quire:behind-your-own-reverse-proxy-->

Untuk menggunakan satu pengimbang beban atau proksi yang sudah anda jalankan
sebaliknya, keluarkan `proxy` (`docker compose up -d --scale proxy=0`) dan tamatkan
TLS di hadapan `web` (8080), `content` (8081), `collab` (1234, websocket) dan
`centrifugo` (8000, websocket). Tetapkan alamat awam dalam `QUIRE_APP_ORIGIN`,
`QUIRE_CONTENT_ORIGIN` dan `QUIRE_COLLAB_URL` (`wss://`), serta julat alamat
proksi anda dalam `QUIRE_TRUSTED_PROXY_CIDRS`.

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

- `init` keluar dengan "QUIRE_DATABASE_ID is not a UUID": tetapkannya dengan
  `uuidgen`.
- `web` bermula semula dengan "did not start on compose": log itu menyenaraikan
  setiap tetapannya yang tidak dapat dihormati dan apa yang perlu digunakan
  sebaliknya.
- Mengubah kata laluan peranan dalam `.env` selepas but pertama tidak melakukan
  apa-apa: skrip init berjalan sekali. Gunakan `ALTER ROLE`.
- Muat naik gagal dengan satu ralat imbasan semasa `CLAMAV_URL` ditetapkan:
  ClamAV memuat turun tandatangannya pada but pertama, yang mengambil beberapa
  minit.

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