---
title: "Docker Compose দিয়ে Quire ইনস্টল"
description: "নিজস্ব পরিকাঠামোয় Docker Compose দিয়ে Quire ইনস্টল করুন।"
image: "https://docs.quirelms.com/og.png"
---

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

# Docker Compose দিয়ে Quire ইনস্টল

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

এক হোস্টে এটিই সম্পূর্ণ পণ্য: LMS, এর পটভূমির কাজ, রিয়েলটাইম ও যৌথ সম্পাদনার সেবা, এবং প্রোফাইলের মাধ্যমে চালু করা সব ঐচ্ছিক সেবা। নকশা `docs/architecture/23-ops.md`-এর section 2-এ।

অন্য লক্ষ্য: [Vercel](/bn/ops/vercel/) ও [Cloudflare Workers](/bn/ops/cloudflare/) শুধু web tier চালায়। Upgrade নির্দেশনা [upgrade.md](/bn/ops/upgrade/)-তে; পরিবর্তনের আগে backup ও restore drill-এর জন্য [backup-restore.md](/bn/ops/backup-restore/) কাছে রাখুন।

## যা প্রয়োজন <!--quire:what-you-need-->

- Docker Engine 27 বা পরের সংস্করণ এবং Compose plugin 2.30 বা পরের সংস্করণ।
- Default stack-এর জন্য 4 CPU core ও 8 GB memory; `--profile full`-সহ 8 core ও 16 GB (শুধু ClamAV-এর signature প্রায় 1.5 GB জায়গা নেয়)।
- Web tier-এর জন্য একটি DNS name এবং অবিশ্বস্ত content-এর জন্য আরেকটি। আলাদা host হতে হবে: SCORM package ও upload করা HTML content origin-এ চলে, যাতে কখনো LMS-এর cookie পড়তে না পারে।
- Local test-এর জন্য `lvh.me` ও `*.localhost` 127.0.0.1-এ resolve হয়; `docker/.env.example` এটিই ব্যবহার করে। Stack-এর `proxy` service local certificate authority-সহ https-এ দুটিই পরিবেশন করে, তাই অন্য কিছু ইনস্টল করতে হয় না (দেখুন “TLS”)।
- Host-এ 80 ও 443 port খালি রাখুন (`QUIRE_PROXY_HTTP_PORT` ও `QUIRE_PROXY_HTTPS_PORT` দিয়ে বদলানো যায়)।

## প্রথমবার চালানো <!--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` `docker/.env`-এ সব secret তৈরি করে এবং `docker/.env.example` থেকে file লেখে (database password, signing ও master key, content launch key pair); পাশাপাশি audit checkpoint signing key রাখে `docker/secrets/audit-signing-key.pem`-এ, যা Compose secret হিসেবে worker-এ mount করে। শুধু `sh`, `awk` ও `openssl` দরকার; বিদ্যমান `docker/.env` overwrite করতে এটি অস্বীকার করে। দুটি file-ই host-এর বাইরে copy করে রাখুন: `QUIRE_MASTER_KEY` ছাড়া restore করা database তার credential decrypt করতে পারে না। হাতে file পূরণ করতে চাইলে `cp docker/.env.example docker/.env` চালান; প্রতিটি secret তৈরির নির্দেশ file-এ আছে।

দুটি origin-ই `https` হতে হবে: production-এ content service plain http প্রত্যাখ্যান করে এবং দুটির registrable domain এক হতে পারবে না। `proxy` service দুটির TLS termination করে (দেখুন “TLS”); `init-env.sh` `http://` origin প্রত্যাখ্যান করে।

Stack নির্দিষ্ট ক্রমে চলে এবং প্রতিটি ধাপ আগেরটি শেষ হওয়া পর্যন্ত অপেক্ষা করে:

1. `postgres` healthy হয়। প্রথম startup-এ এর init script (`docker/postgres/init/90-passwords.sh`) চারটি role password সেট করে।
2. `migrate` control database ও প্রতিটি নির্দিষ্ট tenant database-এ সব migration চালায় এবং job queue bootstrap করে; এগুলো মিলছে কি না পরীক্ষা করে, তারপর শেষ হয় (docs/ops/upgrade.md)। প্রতিটি startup-এ migration চলে এবং idempotent, তাই upgrade মানে নতুন image ও restart।
3. `init` (`apps/web/src/first-run.ts`) application database-কে `QUIRE_DATABASE_ID`-এর অধীনে নথিভুক্ত করে এবং `QUIRE_SETUP_ADMIN_EMAIL` সেট করা থাকলে প্রথম organisation ও তার administrator তৈরি করে। Sign-in address ও তৈরি করা password একবারই `docker compose logs init`-এ ছাপা হয়।
4. `web`, `content`, `worker`, `scheduler`, `collab` ও `centrifugo` চালু হয়।
5. `proxy` চালু হয়, যখন `web` ও `content` সুস্থ হয়।

`https://demo.`-এর পরে আপনার application domain লিখে খুলুন (সঠিক sign-in address `init` log-এ ছাপা হয়), তারপর sign in করুন। Local install-এ আগে proxy-র certificate authority-কে trust করুন (দেখুন “TLS”)। তৈরি করা password `/account/security`-এ বদলান।

প্রয়োজনীয় secret ছাড়া শুরু করা process startup প্রত্যাখ্যান করে এবং log-এ অনুপস্থিত setting-এর নাম জানায়। কোনো কিছু আংশিক configuration নিয়ে চালু হয় না।

## সেবা ও profile <!--quire:services-and-profiles-->

| সেবা | Profile | কাজ |
| --- | --- | --- |
| postgres | সবসময় | Database (PostgreSQL 18 ও pgvector, `docker/postgres.Dockerfile` থেকে তৈরি), প্রথম boot থেকেই WAL archive হয় |
| migrate, init | সবসময় | একবার চলে: migration, তারপর প্রথম setup |
| web | সবসময় | LMS, `QUIRE_HTTP_PORT`-এ (8080) |
| content | সবসময় | অবিশ্বস্ত content origin, `QUIRE_CONTENT_PORT`-এ (8081) |
| worker | সবসময় | Background job: email, report, file processing, webhook |
| scheduler | সবসময় | পুনরাবৃত্ত কাজ: 64টি runtime schedule নিবন্ধন করে worker-কে দেয়; একবারে একজন leader |
| collab | সবসময় | যৌথ সম্পাদনার websocket, `QUIRE_COLLAB_HTTP_PORT`-এ (1234) |
| centrifugo | সবসময় | Realtime fan-out, `QUIRE_REALTIME_PORT`-এ (8000) |
| proxy | সবসময় | Caddy, 80 ও 443 port-এর TLS front door (দেখুন “TLS”) |
| valkey | `cache` | Cache ও rate limit |
| clamav | `scan` | Upload-এ malware scan |
| gotenberg | `preview` | Office থেকে PDF preview, certificate rendering |
| imgproxy | `images` | Image resize ও conversion |
| transcoder | `video` | Video rendition-এর জন্য শুধু LGPL-যুক্ত ffmpeg-সহ worker image |
| seaweedfs | `storage` | এই host-এ S3-compatible object storage |
| otelcol | `observability` | OpenTelemetry collector |
| mailpit | `devmail` | Quire পরীক্ষা করার জন্য সব outgoing mail ধরে |
| backup | `backup` | একবারের base backup; দেখুন backup-restore.md |
| backup-scheduler, backup-offsite | `backup` | প্রতি `QUIRE_BACKUP_INTERVAL_HOURS`-এ base backup এবং সাপ্তাহিক যাচাই drill-সহ host-এর বাইরে encrypted copy |
| h5p | `h5p` | আপনার দেওয়া H5P LTI 1.3 tool image `QUIRE_H5P_IMAGE`-এ, `QUIRE_H5P_PORT`-এ (8090); দেখুন “H5P provider যুক্ত করা” |

`--profile full` `backup` ও `h5p` বাদে সব ঐচ্ছিক service চালায়। একটি service চালাতে `docker compose -f docker/compose.yaml --profile scan up -d` ব্যবহার করুন। ঐচ্ছিক service ছাড়াও Quire চলে এবং কী নেই তা জানায়: scanner না থাকলে upload scan ছাড়াই সংরক্ষিত হয় এবং administrator-কে জানানো হয়; Gotenberg না থাকলে file preview-এর বদলে download করা যায়; transcoder না থাকলে video মূল file হিসেবেই চলে।

প্রতিটি third-party image ও এর license-এর শর্ত `docker/third-party-containers.yaml`-এ তালিকাভুক্ত।

### H5P provider যুক্ত করা <!--quire:connecting-an-h5p-provider-->

Quire H5P runtime বা sidecar embed বা ship করে না (ADR 0019)। H5P ব্যবহার করলে নিজস্ব hosted subscription নিন অথবা Quire থেকে আলাদা নিজস্ব self-hosted H5P instance পরিচালনা করুন। Provider-কে LTI 1.3 external tool হিসেবে নিবন্ধন করে tool activity হিসেবে course-এ এর content যোগ করুন। Quire LTI Assignment and Grade Services (AGS) দিয়ে grade ও activity/grading progress আদানপ্রদান করে। Provider xAPI statement-ও পাঠালে Quire-এর xAPI statement store-এর জন্য আলাদাভাবে configure করুন; AGS grade/progress বিনিময়ে xAPI statement পাঠানো হয় না। Moodle import-এ H5P activity-র জন্য LTI tool connection প্রয়োজন বলে report হয়। H5P runtime, authoring, content bank ও attempt history-এর দায় provider-এর।

এই host-এ নিজস্ব self-hosted instance চালাতে `QUIRE_H5P_IMAGE`-এ এর image দিয়ে `h5p` profile চালু করুন। Compose এটি `QUIRE_H5P_PORT`-এ (8090) প্রকাশ করে এবং data `h5p-data` volume-এ রাখে; image ও এর সঙ্গে আসা দায় আপনার।

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

প্রতিটি process `docker/.env` পড়ে। Template `docker/.env.example`-এ প্রতিটি setting ও default-এর তালিকা আছে। বিভাগগুলো:

### ঠিকানা <!--quire:addresses-->

| Setting | অর্থ |
| --- | --- |
| `QUIRE_APP_ORIGIN` | LMS-এর public address, যেমন `https://learn.example.com` |
| `QUIRE_CONTENT_ORIGIN` | content origin, অন্য host-এ |
| `QUIRE_PLATFORM_DOMAINS` | প্রতিষ্ঠানের domain, comma দিয়ে আলাদা |
| `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`। `vercel` ও `cloudflare`-এর জন্য অন্য guide দেখুন |
| `QUIRE_TRUSTED_PROXY_CIDRS` | যেসব proxy-র `X-Forwarded-For` বিশ্বাস করা হয় |

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

| Setting | অর্থ |
| --- | --- |
| `QUIRE_SECRET_KEY` | Session ও token sign করে। 64টি hex character |
| `QUIRE_MASTER_KEY` | SSO ও webhook secret-এর মতো সংরক্ষিত credential wrap করে। 32 byte, base64। Web tier ও worker-এ একই মান লাগবে। Rotation: [key-rotation.md](/bn/ops/key-rotation/) |
| `QUIRE_MASTER_KEY_VERSION` | Master key-এর version label; unset হলে `v1`। Rotation করলে এটি বাড়ান |
| `QUIRE_MASTER_KEY_RETIRED` | আগে seal করা তথ্য পড়তে লাগতে পারে এমন পুরোনো master key, যেমন `v1=<base64>`। Unresolved কিছু না রেখে rotation শেষ হলে সরান |
| `QUIRE_COLLAB_SIGNING_KEY` | Editing token sign করতে web ও collab-এর মধ্যে ভাগ করা key |
| `QUIRE_BACKUP_SIGNING_KEY` | Course backup sign করে (ঐচ্ছিক) |

এই host ছাড়া অন্য কোথাও `QUIRE_MASTER_KEY`-এর copy রাখুন। এটি ছাড়া restore করা database তার credential decrypt করতে পারে না।

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

| Setting | অর্থ |
| --- | --- |
| `POSTGRES_PASSWORD` | Superuser, container ও backup ব্যবহার করে |
| `QUIRE_DB_APP_PASSWORD`, `QUIRE_DB_MIGRATOR_PASSWORD`, `QUIRE_DB_REPORT_PASSWORD`, `QUIRE_DB_AUDIT_PASSWORD` | Role password, প্রথম startup-এ সেট হয় |
| `DATABASE_URL` | Application role। এর প্রতিটি query-তে row-level security প্রযোজ্য |
| `DATABASE_MIGRATOR_URL`, `QUIRE_MIGRATION_URL` | `migrate` ও `init`-এর migrator role |
| `QUIRE_SUPERUSER_URL` | শুধু প্রথমবার ব্যবহৃত হয় |
| `QUIRE_REPORT_DATABASE_URL` | Report ও report builder-এর read-only report role |
| `QUIRE_AUDIT_DATABASE_URL` | Audit console ও SIEM export-এর audit role |
| `QUIRE_DATABASE_ID` | যেকোনো UUID, install-এর পুরো সময় একই থাকে |

Database volume প্রথম তৈরি হলেই role password প্রয়োগ হয়। পরে বদলাতে `ALTER ROLE` ব্যবহার করে সংশ্লিষ্ট URL-ও update করুন।

`QUIRE_REPORT_DATABASE_URL` `DATABASE_URL` দিয়ে configure করা physical database-এর জন্য ব্যবহৃত হয়। অন্য কোনো নিবন্ধিত physical database হলে web ও worker environment-এ সেটির নিজস্ব `quire_report` connection URL দিন, তারপর database-এর **Reporting environment variable** field-এ variable name `env:NAME` হিসেবে লিখুন। Reference-টি app connection-এর একই database-এ যেতে হবে, সম্ভব হলে তার read replica-তে। Report surface-গুলো tenant-কে তার নিজস্ব database-এর report connection-এ পাঠায়: report builder ও saved report, scheduled delivery, report export, analytics, audit log, REST audit resource এবং assistant-এর audit search। এগুলোর কোনোটিই অন্য database-এর report URL ধার করে না। Database-এ report connection না থাকলে সাধারণ report সেই database-এর নিজস্ব application connection-এ চলে; analytics ও audit read প্রত্যাখ্যান করে তা জানায়, কারণ application role audit trail পড়তে পারে না।

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

| Setting | এই release | বিবরণ |
| --- | --- | --- |
| `QUIRE_STORAGE_DRIVER` | `local` (default), `s3` অথবা `azure` | `local` file `files` volume-এ রাখে। `s3` AWS S3, R2, GCS interoperability ও অন্যান্য S3-compatible storage সমর্থন করে, resumable multipart upload-সহ |
| `QUIRE_REALTIME_DRIVER` | `inprocess` (default), `sse`, `centrifugo` অথবা `durable_objects` | একটি web container-এ `inprocess` উপযুক্ত; একাধিক হলে `centrifugo` অথবা `sse` ব্যবহার করুন |
| `QUIRE_CACHE_DRIVER` | `memory` (default), `postgres` অথবা `valkey` | `memory` প্রতি process-এর নিজস্ব; container জুড়ে rate limit বজায় রাখতে `valkey` বা `postgres` ব্যবহার করুন |
| `QUIRE_VIDEO_DRIVER` | `ffmpeg` (default) অথবা `progressive_mp4` | অথবা key-সহ hosted provider: Cloudflare Stream, Mux বা Bunny |
| `QUIRE_IMAGE_DRIVER` | `noop` (default), `imgproxy` অথবা `cloudflare` | `noop` প্রতিটি image মূল আকারে পরিবেশন করে। `imgproxy`-তে `images` profile ও নিচের setting লাগে; `cloudflare` Cloudflare Images ব্যবহার করে |
| `QUIRE_MEETING_PROVIDER` | `bbb`, `zoom`, `teams`, `meet`, `jitsi` অথবা `in_process` | Live session-এর platform default provider। Unset থাকলে প্রতিষ্ঠান Integrations, Live session provider-এ নিজস্ব account যুক্ত না করা পর্যন্ত live session configure করা নেই বলে জানায়। প্রতিষ্ঠানের নিজস্ব account সবসময় এই মানের ওপর অগ্রাধিকার পায়। প্রতিটি provider-এর নিজস্ব setting (`BBB_URL` ও `BBB_SECRET`, `ZOOM_*`, `TEAMS_*`, `GOOGLE_MEET_*` এবং `JITSI_*` variable) এখানে নাম দেওয়া provider-এর জন্যই শুধু পড়া হয় |
| `QUIRE_MEETING_REGIONS` | `eu`, `uk`, `us`-এর comma-separated list | Platform default provider meeting কোথায় process করে। Unset থাকলে আগের মতো region-এ বাঁধা প্রতিষ্ঠানের বিরুদ্ধে পরীক্ষা হয় না। প্রতিষ্ঠানের নিজস্ব account-এর page-এ তার region জানানো হয় |

এই release-এ নেই এমন driver value পেলে web tier startup প্রত্যাখ্যান করে এবং setting-এর নাম জানায়; default দিয়ে নীরবে বদলে দেয় না।

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

Page চারটি নির্দিষ্ট আকারে image চায় `/api/files/{id}/image/{size}` দিয়ে। এটি file-এর মতোই access পরীক্ষা করে তারপর image service-এ redirect করে। প্রতিটি প্রতিষ্ঠান প্রতি ঘণ্টায় `QUIRE_IMAGE_SPECS_PER_HOUR` (default 2000)টি নতুন image ও size-এর জোড়া চাইতে পারে; ওই ঘণ্টায় আগে তৈরি size গণনায় আসে না। একাধিক web container থাকলে `valkey` বা `postgres` ব্যবহার করুন `QUIRE_CACHE_DRIVER`-এর জন্য, যাতে সীমা সব container-এ কার্যকর হয়।

| Setting | Driver | বিবরণ |
| --- | --- | --- |
| `IMGPROXY_URL` | `imgproxy` | Browser থেকে imgproxy-তে যাওয়ার address, যেমন `https://images.example.org`। `images` profile এটিকে `QUIRE_IMAGES_PORT`-এ (8082) প্রকাশ করে |
| `IMGPROXY_KEY`, `IMGPROXY_SALT` | `imgproxy` | Hex string; imgproxy চালানোর সময় ব্যবহৃত মানের সঙ্গে একই। প্রতিটি তৈরি করতে `openssl rand -hex 32` চালান। Quire প্রতিটি image address এগুলো দিয়ে sign করে, তাই Quire না চাইলে imgproxy কিছু render করে না |
| `QUIRE_IMAGE_SOURCE_ORIGIN` | local storage-সহ `imgproxy` | imgproxy কোথা থেকে original আনে। Compose `http://web:3000` সেট করে। `s3` বা `azure` storage হলে imgproxy bucket থেকে আনে, তাই এটি ব্যবহৃত হয় না |
| `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_IMAGES_TOKEN`, `CLOUDFLARE_IMAGES_ACCOUNT_HASH` | `cloudflare` | Images edit permission-সহ API token এবং Images, Developer resources থেকে account hash। Account-এ flexible variant চালু করুন |
| `CLOUDFLARE_IMAGES_SIGNING_KEY` | `cloudflare` | ঐচ্ছিক। সেট করা থাকলে image private হয় এবং প্রতিটি address sign করা ও মেয়াদযুক্ত হয়। এটি না থাকলে image public এবং `QUIRE_SECRET_KEY` থেকে তৈরি অনুমান করা যায় না এমন address-এ থাকে |

Cloudflare Images পরিবেশিত প্রতিটি original-এর নিজস্ব copy রাখে। কোনো file delete হলে worker original-এর আগে ওই copy মুছে দেয়।

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

Background job একই Postgres database-এ pg-boss ব্যবহার করে, তাই আলাদা queue service চালাতে হয় না বা configuration লাগে না। যে পরিবর্তনের কারণে job তৈরি হয় তার একই transaction-এ এটি queue-তে যায়, ফলে crash হলে হারায় না বা দুবার পাঠানো হয় না। এখানে `QUIRE_QUEUE_DRIVER`-এর default `pgboss`; `vercel` ও `cloudflare` শুধু হালকা notification ও webhook delivery-কে platform-এর নিজস্ব queue-তে সরায়। Vercel ও Cloudflare guide-এ এগুলো এবং তাদের web tier কীভাবে enqueue করে তা বলা আছে।

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

নিচের একটি সেট করুন:

- `QUIRE_EMAIL_PROVIDER_CONFIG`: HTTP provider ও credential উল্লেখ করা JSON object, যেমন `{"provider":"postmark","token":"..."}`। Postmark, Amazon SES, Mailgun, SendGrid ও Resend সমর্থিত।
- `QUIRE_SMTP_URL`: `smtp://user:password@host:587`। শুধু এই target-এ; serverless target SMTP আটকে দেয়।

প্রেরক `QUIRE_MAIL_FROM`। Quire পরীক্ষা করতে `devmail` profile চালু করে `QUIRE_SMTP_URL=smtp://mailpit:1025` সেট করুন এবং `http://localhost:8025`-এ mail পড়ুন।

### ঐচ্ছিক service <!--quire:optional-services-->

| Setting | 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` অথবা `QUIRE_MEILISEARCH_URL` | External search; নইলে Postgres full text |
| `QUIRE_BREACH_CHECK_PROVIDER=off`, `QUIRE_BREACH_CHECK_URL` | Password breach check। Default-এ `api.pwnedpasswords.com`-এর বিরুদ্ধে চালু (hash-এর শুধু পাঁচ character-এর prefix পাঠানো হয়); `off` বন্ধ করে, URL আপনার host করা range API-তে নির্দেশ করে |

### পর্যবেক্ষণ <!--quire:observability-->

`OTEL_EXPORTER_OTLP_ENDPOINT` collector-এর address দেয়, যেখানে প্রতিটি process trace ও metric পাঠায়; `observability` profile-এ এটি `http://otelcol:4318` এবং আপনার backend-এর exporter যোগ করার জায়গা `docker/otel-collector.yaml`। সেটি থাকলে web tier, worker, scheduler, content ও collab process OTLP/HTTP দিয়ে span পাঠায় (web request, tenant database transaction, worker job ও outbound call) এবং একই endpoint-এ প্রতি মিনিটে metric পাঠায় (`OTEL_METRICS_EXPORTER=none` এগুলো বন্ধ করে)। `OTEL_TRACES_SAMPLER_ARG` কত ভাগ trace রাখা হবে ঠিক করে। `LOG_LEVEL`-এ log standard output-এ যায়; Compose তা rotate করে। Trace-এ কখনো ব্যক্তিগত তথ্য থাকে না।

### আঞ্চলিক egress (EU data residency) <!--quire:regional-egress-eu-data-residency-->

`QUIRE_REGION=eu` জানায় stack ইউরোপীয় ইউনিয়নের প্রতিষ্ঠান পরিবেশন করে। তখন EU অঞ্চলে বাঁধা প্রতিষ্ঠানের হয়ে worker-এর প্রতিটি outbound request allowlist-এ সীমিত থাকে (21-compliance.md section 8.1)। Configured service-গুলো region-এর জন্য যে host ঘোষণা করে (storage endpoint, email provider, hosted video provider, প্রতিষ্ঠানের নিজস্ব storage target, AI provider ও email account), সক্রিয় derogation-এ থাকা service-এর host এবং আপনার `QUIRE_EGRESS_ALLOW_HOSTS`-এ দেওয়া host—এগুলোই allowlist। অন্য কোনো public host-এ request পাঠানোর আগেই প্রত্যাখ্যাত হয়; প্রত্যাখ্যান প্রতিষ্ঠানের audit trail-এ `privacy/egress_refused` হিসেবে লেখা হয় এবং Compliance, Data residency-তে দেখায়।

| Setting | মান | প্রভাব |
| --- | --- | --- |
| `QUIRE_EGRESS_ALLOW_HOSTS` | comma দিয়ে আলাদা hostname-এর তালিকা, অথবা সব subdomain-এর জন্য `*.example.org` | EU প্রতিষ্ঠান যে অতিরিক্ত host-এ পৌঁছাতে পারে। Webhook, xAPI ও SIEM endpoint, blog feed এবং Amazon SES host এখানে রাখুন, কারণ প্রতিষ্ঠানের নিজের পছন্দে সেগুলো আসে এবং কোনো service সেগুলো ঘোষণা করে না। Loopback, private address ও `web` বা `clamav`-এর মতো এক label-এর name আপনার নিজের network; এগুলো কখনো পরীক্ষা হয় না |

যুক্তরাজ্য ও যুক্তরাষ্ট্রের প্রতিষ্ঠান host list-এ আবদ্ধ নয়; তাদের ক্ষেত্রে service region পরীক্ষা থাকে। Worker-এ তালিকা সেট করুন; allowlist দেখাতে admin page web tier-এ এটি পড়ে, তাই সব service যে `docker/.env` পড়ে, সেখানেই রাখুন।

Application check স্পষ্ট error ও audit entry দেয়, কিন্তু এটিই নিশ্চয়তা নয়: code ভুল হতে পারে। নিশ্চয়তা network। Compose নিজে এটি enforce করে না। আঞ্চলিক stack-এর জন্য `worker` ও `web` service-কে `internal: true` network-এ রাখুন; বাইরের একমাত্র পথ হবে এমন egress proxy (যেমন Squid বা tinyproxy container), যা `QUIRE_EGRESS_ALLOW_HOSTS`-এর একই host এবং configure করা service-এর host অনুমোদন করে। ওই service-গুলোর জন্য `HTTPS_PROXY` সেট করুন। Residency page-এ application ঠিক কোন host অনুমোদন করে তা দেখা যায়, তাই দুই তালিকা তুলনা করা যায়।

## স্বাস্থ্য <!--quire:health-->

| Endpoint | অর্থ |
| --- | --- |
| `/healthz` | Liveness: process উত্তর দেয়। Compose health check এটি ব্যবহার করে |
| `/readyz` | Readiness: dependency পৌঁছানো যায় এবং প্রতিটি ঐচ্ছিক service configure আছে কি না জানানো হয়। Load balancer এখানে নির্দেশ করুন |

`docker compose -f docker/compose.yaml ps` প্রতিটি service-এর health দেখায়।

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

`proxy` service (Caddy, Apache-2.0, `docker/caddy/Caddyfile`) default stack-এর অংশ। এটি 80 ও 443 port-এ সাড়া দেয় এবং route করে:

| Host বা path | কোথায় যায় |
| --- | --- |
| `QUIRE_PROXY_CONTENT_HOST` | `content` |
| `QUIRE_PROXY_APP_HOST`, প্রতিটি tenant subdomain ও custom domain | `web` |
| ওই host-গুলোর `/_collab/` | `collab` (websocket, `QUIRE_COLLAB_URL`) |
| ওই host-গুলোর `/_realtime/connection/` | `centrifugo`-র client websocket; এর server API কখনো প্রকাশ করা হয় না |
| ওই host-গুলোর `/_images/` | `imgproxy`, `images` profile-সহ (`IMGPROXY_URL`) |

`init-env.sh` দুটি origin থেকে `QUIRE_PROXY_APP_HOST`, `QUIRE_PROXY_CONTENT_HOST`, `QUIRE_PROXY_HTTPS_PORT`, `QUIRE_COLLAB_URL` ও `IMGPROXY_URL` তৈরি করে, তাই মানগুলো পরস্পর থেকে বিচ্যুত হয় না। হাতে origin বদলালে এগুলোও একসঙ্গে সম্পাদনা করুন।

Certificate `QUIRE_PROXY_TLS` অনুযায়ী চলে:

- `internal` (default): `localhost`, `*.localhost` ও `lvh.me`-এর জন্য Caddy-র নিজস্ব certificate authority। এর root একবার trust করে তারপর browse করুন:

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

  System বা browser-এর trust store-এ `quire-local-ca.crt` যোগ করুন। `curl`-এ এটি `--cacert` দিয়ে দিন।
- Email address: বাস্তব hostname-এর জন্য স্বয়ংক্রিয় ACME certificate (আগে Let's Encrypt, পরে ZeroSSL)। দুই origin ও প্রতিটি tenant host-এর DNS-কে এখানে নির্দেশ করতে হবে এবং 80 ও 443 port ইন্টারনেট থেকে পৌঁছানো যায় এমন হতে হবে।

Tenant host-এর certificate প্রথমবার আসলে চাওয়া হয়, এবং web যে নাম এই install-এর বলে নিশ্চিত করলেই শুধু (`/tls-allowed`, Compose network-এ জিজ্ঞাসা করা হয়)। Wildcard certificate বা DNS provider plugin দরকার নেই; কেউ host-এ নাম নির্দেশ করলেও certificate request করাতে পারবে না। Certificate ও local authority `caddy-data` volume-এ থাকে; `internal` ব্যবহার করলে বাকি backup-এর সঙ্গে এটিও backup করুন।

Web শুধু proxy থেকে পাওয়া `X-Forwarded-For` বিশ্বাস করে: proxy-র স্থির address (`QUIRE_PROXY_ADDRESS`, default `172.29.64.10`) স্থির subnet-এ (`QUIRE_COMPOSE_SUBNET`) থাকে এবং `QUIRE_TRUSTED_PROXY_CIDRS` সেই address-টি উল্লেখ করে। Subnet host-এর কোনো network-এর সঙ্গে মিলে গেলে দুটোই বদলে `docker compose down` চালান, তারপর `up` করুন।

## নিজের reverse proxy-র পেছনে <!--quire:behind-your-own-reverse-proxy-->

আগে থেকে চালানো load balancer বা proxy ব্যবহার করতে `proxy` বাদ দিন (`docker compose up -d --scale proxy=0`) এবং `web` (8080), `content` (8081), `collab` (1234, websocket) ও `centrifugo` (8000, websocket)-এর সামনে TLS termination করুন। Public address দিন `QUIRE_APP_ORIGIN`, `QUIRE_CONTENT_ORIGIN` ও `QUIRE_COLLAB_URL`-এ (`wss://`), আর proxy-র address range দিন `QUIRE_TRUSTED_PROXY_CIDRS`-এ।

## সমস্যা সমাধান <!--quire:troubleshooting-->

- `init` "QUIRE_DATABASE_ID is not a UUID" দিয়ে শেষ হলে `uuidgen` দিয়ে এটি সেট করুন।
- `web` "did not start on compose" দিয়ে restart হলে log-এ মানতে পারে না এমন প্রতিটি setting ও বিকল্প লেখা থাকে।
- প্রথম startup-এর পর `.env`-এ role password বদলালে কিছু হয় না: init script একবারই চলে। `ALTER ROLE` ব্যবহার করুন।
- `CLAMAV_URL` সেট থাকা অবস্থায় scan error-এ upload ব্যর্থ হলে, প্রথম startup-এ ClamAV signature download করছে; কয়েক মিনিট লাগবে।

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