---
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/hi/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` के अनुभाग 2 में है।

अन्य लक्ष्य: [Vercel](/hi/ops/vercel/) और [Cloudflare Workers](/hi/ops/cloudflare/) केवल web tier चलाते हैं। अपग्रेड [upgrade.md](/hi/ops/upgrade/) में हैं, और बैकअप तथा पुनर्स्थापन अभ्यास [backup-restore.md](/hi/ops/backup-restore/) में।

## आवश्यकताएँ <!--quire:what-you-need-->

- Compose plugin 2.30 या नया सहित Docker Engine 27 या नया।
- डिफ़ॉल्ट स्टैक के लिए 4 CPU core और 8 GB memory; `--profile full` के साथ 8 core और 16 GB (केवल ClamAV लगभग 1.5 GB signatures रखता है)।
- web tier के लिए एक DNS नाम और अविश्वसनीय सामग्री के लिए दूसरा। ये अलग host होने चाहिए: SCORM पैकेज और अपलोड किया HTML content origin पर चलता है, इसलिए वह LMS की cookies कभी नहीं पढ़ सकता।
- स्थानीय परीक्षण के लिए `lvh.me` और `*.localhost` का पता 127.0.0.1 पर जाता है; `docker/.env.example` यही उपयोग करता है। स्टैक की अपनी `proxy` सेवा स्थानीय certificate authority के साथ https पर दोनों की सेवा देती है, इसलिए और कुछ इंस्टॉल नहीं करना पड़ता ("TLS" देखें)।
- होस्ट पर 80 और 443 पोर्ट खाली हों (`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`, हर सीक्रेट (डेटाबेस पासवर्ड, signing और master कुंजियाँ, content launch key pair) जनरेट करके `docker/.env` बनाता है; उसका आधार `docker/.env.example` है। यह audit checkpoint signing key को `docker/secrets/audit-signing-key.pem` में भी लिखता है, जिसे Compose workers में secret के रूप में माउंट करता है। इसे केवल `sh`, `awk` और `openssl` चाहिए; यह मौजूदा `docker/.env` को अधिलेखित करने से मना करता है। दोनों फ़ाइलें होस्ट के बाहर कॉपी करें: `QUIRE_MASTER_KEY` के बिना पुनर्स्थापित डेटाबेस अपने संग्रहीत क्रेडेंशियल डिक्रिप्ट नहीं कर सकता। फ़ाइल हाथ से बनाने के लिए `cp docker/.env.example docker/.env` चलाएँ; हर सीक्रेट कैसे बनाएँ यह फ़ाइल में लिखा है।

दोनों origin `https` होने चाहिए: production में content सेवा सादा http अस्वीकार करती है, और उनके registrable domain साझा नहीं होने चाहिए। `proxy` सेवा दोनों के लिए TLS समाप्त करती है ("TLS" देखें); `init-env.sh`, `http://` origin को अस्वीकार करता है।

स्टैक तय क्रम से शुरू होता है; हर चरण अगले से पहले वाले के तैयार होने की प्रतीक्षा करता है:

1. `postgres` स्वस्थ होता है। पहली शुरुआत पर उसकी init script (`docker/postgres/init/90-passwords.sh`) चार role पासवर्ड सेट करती है।
2. `migrate`, हर migration लागू करता है और control database तथा प्रत्येक समर्पित tenant database में job queue आरंभ करता है; फिर पुष्टि करता है कि वे सभी सहमत हैं और बाहर निकल जाता है (docs/ops/upgrade.md)। हर शुरुआत पर migration चलती हैं और idempotent हैं, इसलिए अपग्रेड का अर्थ नया image और restart है।
3. `init` (`apps/web/src/first-run.ts`), application database को `QUIRE_DATABASE_ID` में दर्ज करता है और `QUIRE_SETUP_ADMIN_EMAIL` सेट होने पर पहला संगठन तथा उसका administrator बनाता है। साइन-इन पता और जनरेट किया गया पासवर्ड एक बार `docker compose logs init` में दिखते हैं।
4. `web`, `content`, `worker`, `scheduler`, `collab` और `centrifugo` शुरू होते हैं।
5. `proxy` तब शुरू होता है जब `web` और `content` स्वस्थ हों।

`https://demo.` के बाद अपना application domain खोलें (सटीक साइन-इन पता `init` log में है) और साइन इन करें। स्थानीय इंस्टॉल में पहले proxy की certificate authority पर भरोसा करें ("TLS" देखें)। जनरेट किया पासवर्ड `/account/security` पर बदलें।

आवश्यक सीक्रेट न मिलने पर प्रक्रिया शुरू होने से मना करती है और log में उस सेटिंग का नाम बताती है। स्टैक आधी-अधूरी सेटिंगों के साथ शुरू नहीं होता।

## सेवाएँ और प्रोफ़ाइल <!--quire:services-and-profiles-->

| सेवा | प्रोफ़ाइल | काम |
| --- | --- | --- |
| postgres | हमेशा | डेटाबेस (PostgreSQL 18 और pgvector, `docker/postgres.Dockerfile` से निर्मित), पहले बूट से WAL संग्रहित करता है |
| migrate, init | हमेशा | एक बार चलने वाले काम: migration, फिर पहली शुरुआत |
| web | हमेशा | LMS, `QUIRE_HTTP_PORT` (8080) पर |
| content | हमेशा | अविश्वसनीय content origin, `QUIRE_CONTENT_PORT` (8081) पर |
| worker | हमेशा | पृष्ठभूमि कार्य: ईमेल, रिपोर्ट, फ़ाइल संसाधन, webhook |
| scheduler | हमेशा | आवर्ती कार्य: 64 runtime schedule दर्ज कर worker को सौंपता है; एक समय में एक leader |
| collab | हमेशा | सहयोगी संपादन websocket, `QUIRE_COLLAB_HTTP_PORT` (1234) पर |
| centrifugo | हमेशा | रीयलटाइम वितरण, `QUIRE_REALTIME_PORT` (8000) पर |
| proxy | हमेशा | Caddy, 80 और 443 पोर्ट पर TLS प्रवेश-द्वार ("TLS" देखें) |
| valkey | `cache` | कैश और rate limit |
| clamav | `scan` | अपलोड में malware की जाँच |
| gotenberg | `preview` | Office से PDF पूर्वावलोकन, प्रमाणपत्र रेंडरिंग |
| imgproxy | `images` | छवियों का आकार बदलना और रूपांतरण |
| transcoder | `video` | video rendition के लिए LGPL-only ffmpeg सहित worker image |
| seaweedfs | `storage` | इस होस्ट पर S3-संगत object storage |
| otelcol | `observability` | OpenTelemetry collector |
| mailpit | `devmail` | Quire आज़माने के लिए सभी भेजे जाने वाले मेल पकड़ता है |
| backup | `backup` | एक बार चलने वाला base backup; backup-restore.md देखें |
| backup-scheduler, backup-offsite | `backup` | हर `QUIRE_BACKUP_INTERVAL_HOURS` पर base backup और साप्ताहिक सत्यापन अभ्यास सहित एन्क्रिप्टेड, होस्ट-बाहरी प्रतियाँ |
| h5p | `h5p` | आपके दिए H5P LTI 1.3 tool image को `QUIRE_H5P_IMAGE` से चलाता है, `QUIRE_H5P_PORT` (8090) पर; "H5P provider से जुड़ना" देखें |

`--profile full` `backup` और `h5p` छोड़कर हर वैकल्पिक सेवा शुरू करता है। एक सेवा शुरू करने के लिए `docker compose -f docker/compose.yaml --profile scan up -d` चलाएँ। वैकल्पिक सेवा न होने पर भी Quire चलता है और बताता है कि क्या अनुपस्थित है: scanner के बिना अपलोड बिना जाँच संग्रहित होते हैं और administrator को सूचना मिलती है; Gotenberg न हो तो फ़ाइल का पूर्वावलोकन के बजाय डाउनलोड मिलता है; transcoder न हो तो video मूल फ़ाइल के रूप में चलता है।

हर तृतीय-पक्ष image और उसके लाइसेंस दायित्व `docker/third-party-containers.yaml` में सूचीबद्ध हैं।

### H5P provider से जुड़ना <!--quire:connecting-an-h5p-provider-->

Quire H5P runtime या sidecar (ADR 0019) को शामिल या वितरित नहीं करता। H5P का उपयोग करने पर अपनी hosted subscription दें या Quire से अलग अपना self-hosted H5P instance चलाएँ। उस provider को LTI 1.3 external tool के रूप में पंजीकृत करें और उसकी सामग्री को पाठ्यक्रमों में tool activity के रूप में जोड़ें। Quire, LTI Assignment and Grade Services (AGS) के जरिए grade और activity/grading progress का आदान-प्रदान करता है। यदि provider xAPI statement भी भेजता है, तो Quire के xAPI statement store के लिए उसे अलग से कॉन्फ़िगर करें; AGS grade/progress आदान-प्रदान xAPI statement नहीं भेजता। Moodle import में H5P activity को ऐसे दिखाया जाता है जिसे LTI tool connection चाहिए। H5P runtime, authoring, content bank और प्रयास का इतिहास provider की ज़िम्मेदारी है।

इस होस्ट पर अपना self-hosted instance चलाने के लिए `QUIRE_H5P_IMAGE` को उसके image पर सेट करें और `h5p` प्रोफ़ाइल शुरू करें। Compose इसे `QUIRE_H5P_PORT` (8090) पर प्रकाशित करता है और डेटा `h5p-data` वॉल्यूम में रखता है; image और उससे जुड़े दायित्व आपके हैं।

## सेटिंगें <!--quire:settings-->

हर प्रक्रिया `docker/.env` पढ़ती है। टेम्पलेट `docker/.env.example` में हर सेटिंग और उसका डिफ़ॉल्ट दिया गया है। समूह:

### पते <!--quire:addresses-->

| सेटिंग | अर्थ |
| --- | --- |
| `QUIRE_APP_ORIGIN` | LMS का सार्वजनिक पता, जैसे `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` के लिए अन्य गाइड देखें |
| `QUIRE_TRUSTED_PROXY_CIDRS` | वे proxy जिनके `X-Forwarded-For` पर भरोसा किया जाता है |

### सीक्रेट <!--quire:secrets-->

| सेटिंग | अर्थ |
| --- | --- |
| `QUIRE_SECRET_KEY` | session और token पर हस्ताक्षर करती है। 64 hexadecimal वर्ण |
| `QUIRE_MASTER_KEY` | SSO और webhook secret जैसे संग्रहीत क्रेडेंशियल को लपेटती है। 32 bytes, base64। web tier और worker में एक ही मान होना चाहिए। रोटेशन: [key-rotation.md](/hi/ops/key-rotation/) |
| `QUIRE_MASTER_KEY_VERSION` | master key का संस्करण लेबल; सेट न होने पर `v1`। रोटेशन पर इसे बढ़ाएँ |
| `QUIRE_MASTER_KEY_RETIRED` | पहले की master कुंजियाँ, जिन्हें उनसे सील की गई चीज़ पढ़ने के लिए अभी चाहिए, `v1=<base64>` के रूप में। रोटेशन पूरा हो और कुछ अनसुलझा न बचे तो हटाएँ |
| `QUIRE_COLLAB_SIGNING_KEY` | editing token पर हस्ताक्षर करने के लिए web और collab में साझा |
| `QUIRE_BACKUP_SIGNING_KEY` | course backup पर हस्ताक्षर करती है (वैकल्पिक) |

`QUIRE_MASTER_KEY` की प्रति इस होस्ट से अलग जगह रखें। इसके बिना पुनर्स्थापित डेटाबेस अपने क्रेडेंशियल डिक्रिप्ट नहीं कर सकता।

### डेटाबेस <!--quire:database-->

| सेटिंग | अर्थ |
| --- | --- |
| `POSTGRES_PASSWORD` | superuser पासवर्ड, जिसका उपयोग container और backup करते हैं |
| `QUIRE_DB_APP_PASSWORD`, `QUIRE_DB_MIGRATOR_PASSWORD`, `QUIRE_DB_REPORT_PASSWORD`, `QUIRE_DB_AUDIT_PASSWORD` | role पासवर्ड, पहली शुरुआत पर सेट होते हैं |
| `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, इंस्टॉल के पूरे जीवनकाल में स्थिर |

Role पासवर्ड केवल डेटाबेस वॉल्यूम पहली बार बनने पर लागू होते हैं। बाद में बदलने के लिए `ALTER ROLE` चलाएँ और फिर उससे मेल खाने वाला URL अपडेट करें।

`QUIRE_REPORT_DATABASE_URL`, `DATABASE_URL` से कॉन्फ़िगर किए गए भौतिक डेटाबेस के लिए है। किसी दूसरे पंजीकृत भौतिक डेटाबेस के लिए web और worker वातावरण में उसका अपना `quire_report` connection URL सेट करें, फिर उस डेटाबेस के **Reporting environment variable** फ़ील्ड में चर का नाम `env:NAME` के रूप में डालें। संदर्भ उसी डेटाबेस पर होना चाहिए जिस पर उसका app connection है; बेहतर है कि वह उसका read replica हो। हर report surface tenant के पीछे उसी डेटाबेस के report connection तक जाती है: report builder और सहेजी रिपोर्टें, नियोजित वितरण, report export, analytics, audit log, REST audit resource और assistant की audit खोज। इनमें से कोई भी दूसरे डेटाबेस का report URL कभी नहीं लेता। डेटाबेस में report connection न हो तो सामान्य report उसी डेटाबेस के अपने application connection पर चलती हैं, जबकि analytics और हर audit पढ़ाई मना करके इसका कारण बताती हैं, क्योंकि application role audit trail नहीं पढ़ सकती।

### ड्राइवर <!--quire:drivers-->

| सेटिंग | इस रिलीज़ में | टिप्पणियाँ |
| --- | --- | --- |
| `QUIRE_STORAGE_DRIVER` | `local` (डिफ़ॉल्ट), `s3` या `azure` | `local` फ़ाइलें `files` वॉल्यूम में रखता है। `s3` AWS S3, R2, GCS interoperability और अन्य S3-संगत संग्रहों के लिए है, resumable multipart upload सहित |
| `QUIRE_REALTIME_DRIVER` | `inprocess` (डिफ़ॉल्ट), `sse`, `centrifugo` या `durable_objects` | एक web container के लिए `inprocess` ठीक है; कई हों तो `centrifugo` या `sse` उपयोग करें |
| `QUIRE_CACHE_DRIVER` | `memory` (डिफ़ॉल्ट), `postgres` या `valkey` | `memory` हर प्रक्रिया के लिए अलग है; containers के बीच rate limit लागू रखने के लिए `valkey` या `postgres` इस्तेमाल करें |
| `QUIRE_VIDEO_DRIVER` | `ffmpeg` (डिफ़ॉल्ट) या `progressive_mp4` | या hosted provider: Cloudflare Stream, Mux या Bunny, उनकी कुंजियों से |
| `QUIRE_IMAGE_DRIVER` | `noop` (डिफ़ॉल्ट), `imgproxy` या `cloudflare` | `noop` हर छवि मूल आकार में देता है। `imgproxy` को `images` प्रोफ़ाइल और नीचे की सेटिंगें चाहिए; `cloudflare` Cloudflare Images इस्तेमाल करता है |
| `QUIRE_MEETING_PROVIDER` | `bbb`, `zoom`, `teams`, `meet`, `jitsi` या `in_process` | live session के लिए platform डिफ़ॉल्ट। सेट न होने पर live session तब तक कॉन्फ़िगर नहीं दिखते जब तक संगठन Integrations, Live session provider में अपना खाता न जोड़े। संगठन का अपना खाता हमेशा इस मान को प्राथमिकता देता है। प्रत्येक provider की सेटिंग (`BBB_URL` और `BBB_SECRET`, `ZOOM_*`, `TEAMS_*`, `GOOGLE_MEET_*`, `JITSI_*` चर) केवल यहाँ नामित provider के लिए पढ़ी जाती हैं |
| `QUIRE_MEETING_REGIONS` | `eu`, `uk`, `us` की comma से अलग सूची | वे क्षेत्र जहाँ platform डिफ़ॉल्ट provider बैठकें संसाधित करता है। सेट न होने पर पहले की तरह संगठन के पिन किए गए क्षेत्र से इसकी तुलना नहीं होती। संगठन का अपना खाता अपने पृष्ठ पर अपने क्षेत्र बताता है |

इस रिलीज़ में उपलब्ध न होने वाला driver मान web tier शुरू होते समय अस्वीकार किया जाता है और सेटिंग का नाम बताया जाता है; उसे चुपचाप डिफ़ॉल्ट से नहीं बदला जाता।

### छवियाँ <!--quire:images-->

पृष्ठ चार तय आकारों में छवि माँगते हैं: `/api/files/{id}/image/{size}`। यह पहले उसी तरह पहुँच जाँचता है जैसे फ़ाइल के लिए, फिर image सेवा पर redirect करता है। हर संगठन हर घंटे अधिकतम `QUIRE_IMAGE_SPECS_PER_HOUR` (डिफ़ॉल्ट 2000) नए छवि-आकार युग्म माँग सकता है; उस घंटे पहले से बने आकार नहीं गिने जाते। एक से अधिक web container होने पर `valkey` या `postgres` इस्तेमाल करें, ताकि rate limit सब पर लागू हो; यह `QUIRE_CACHE_DRIVER` सेटिंग नियंत्रित करती है।

| सेटिंग | ड्राइवर | टिप्पणियाँ |
| --- | --- | --- |
| `IMGPROXY_URL` | `imgproxy` | वह पता जहाँ browser, imgproxy तक पहुँचते हैं; उदाहरणार्थ `https://images.example.org`। `images` प्रोफ़ाइल इसे `QUIRE_IMAGES_PORT` (8082) पर प्रकाशित करती है |
| `IMGPROXY_KEY`, `IMGPROXY_SALT` | `imgproxy` | hexadecimal string, वही मान जिनसे imgproxy शुरू होता है। प्रत्येक को `openssl rand -hex 32` से बनाएँ। Quire हर image address पर इनसे हस्ताक्षर करता है, इसलिए imgproxy केवल Quire द्वारा माँगी गई छवि रेंडर करता है |
| `QUIRE_IMAGE_SOURCE_ORIGIN` | स्थानीय storage के साथ `imgproxy` | जहाँ से imgproxy मूल छवियाँ प्राप्त करता है। Compose इसे `http://web:3000` सेट करता है। `s3` या `azure` storage में imgproxy bucket से लाता है और इसका उपयोग नहीं होता |
| `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_IMAGES_TOKEN`, `CLOUDFLARE_IMAGES_ACCOUNT_HASH` | `cloudflare` | Images edit अनुमति वाला API token और Images, Developer resources में मिलने वाला account hash। खाते के लिए flexible variants चालू करें |
| `CLOUDFLARE_IMAGES_SIGNING_KEY` | `cloudflare` | वैकल्पिक। सेट होने पर छवियाँ private होती हैं और हर address पर हस्ताक्षर होता है तथा उसकी समय-सीमा होती है। इसके बिना छवियाँ सार्वजनिक रहती हैं; उनके address `QUIRE_SECRET_KEY` से बनते हैं जिन्हें कोई अनुमान नहीं लगा सकता |

Cloudflare Images अपनी सेवा की हर मूल छवि की प्रति रखता है। फ़ाइल मिटाने पर worker मूल से पहले वह प्रति मिटाता है।

### कतार <!--quire:queue-->

पृष्ठभूमि कार्य उसी Postgres डेटाबेस में pg-boss इस्तेमाल करते हैं, इसलिए चलाने के लिए कोई queue सेवा नहीं और कॉन्फ़िगर करने के लिए कुछ नहीं। कार्य उस बदलाव के transaction में enqueue होते हैं जिसने उन्हें पैदा किया; इसलिए crash से कार्य खो या दो बार भेजा नहीं जा सकता। यहाँ `QUIRE_QUEUE_DRIVER` का डिफ़ॉल्ट `pgboss` है; `vercel` और `cloudflare` केवल हल्की notification तथा webhook delivery को platform की अपनी queue पर भेजते हैं। Vercel और Cloudflare गाइड उनका वर्णन करती हैं और बताती हैं कि उनके web tier कार्य कैसे enqueue करते हैं।

### ईमेल <!--quire:email-->

इनमें से एक सेट करें:

- `QUIRE_EMAIL_PROVIDER_CONFIG`: HTTP provider और उसके credentials वाला JSON object, जैसे `{"provider":"postmark","token":"..."}`। Postmark, Amazon SES, Mailgun, SendGrid और Resend समर्थित हैं।
- `QUIRE_SMTP_URL`: `smtp://user:password@host:587`। केवल यह लक्ष्य SMTP की अनुमति देता है; serverless लक्ष्य इसे रोकते हैं।

भेजने वाले का पता `QUIRE_MAIL_FROM` है। Quire आज़माने के लिए `devmail` प्रोफ़ाइल शुरू करें, `QUIRE_SMTP_URL=smtp://mailpit:1025` सेट करें और `http://localhost:8025` पर मेल पढ़ें।

### वैकल्पिक सेवाएँ <!--quire:optional-services-->

| सेटिंग | प्रोफ़ाइल के साथ |
| --- | --- |
| `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` | बाहरी खोज; अन्यथा Postgres full text |
| `QUIRE_BREACH_CHECK_PROVIDER=off`, `QUIRE_BREACH_CHECK_URL` | पासवर्ड उल्लंघन जाँच। डिफ़ॉल्ट रूप से `api.pwnedpasswords.com` पर चालू (केवल पाँच वर्ण का hash prefix भेजा जाता है); `off` इसे बंद करता है और URL आपके द्वारा होस्ट किए गए range API पर जा सकता है |

### प्रेक्षणीयता <!--quire:observability-->

`OTEL_EXPORTER_OTLP_ENDPOINT` उस collector का पता है जहाँ हर प्रक्रिया traces और metrics भेजती है; `observability` प्रोफ़ाइल के साथ यह `http://otelcol:4318` होता है, और अपने backend का exporter जोड़ने की जगह `docker/otel-collector.yaml` है। सेट होने पर web tier, worker, scheduler, content और collab प्रक्रियाएँ OTLP/HTTP से spans (web request, tenant database transaction, worker job और बाहर जाने वाली call) तथा हर मिनट उसी endpoint पर metrics भेजती हैं (`OTEL_METRICS_EXPORTER=none` से इन्हें बंद करें)। `OTEL_TRACES_SAMPLER_ARG` रखे जाने वाले traces का अनुपात सेट करता है। `LOG_LEVEL` पर logs standard output में जाते हैं और Compose उन्हें rotate करता है। Traces में व्यक्तिगत डेटा नहीं होता।

### क्षेत्रीय निर्गमन (EU डेटा निवास) <!--quire:regional-egress-eu-data-residency-->

`QUIRE_REGION=eu` बताता है कि स्टैक यूरोपीय संघ के संगठनों को सेवा देता है। फिर worker, EU में पिन किए गए संगठन की हर बाहर जाने वाली request को allowlist तक सीमित करता है (21-compliance.md अनुभाग 8.1)। Allowlist में उस क्षेत्र के लिए कॉन्फ़िगर की गई सेवाओं के घोषित host (storage endpoint, email provider, hosted video provider, संगठन के अपने storage target, AI provider और email account), सक्रिय छूट के अंतर्गत सेवाओं के host, और `QUIRE_EGRESS_ALLOW_HOSTS` में आपके द्वारा सूचीबद्ध host आते हैं। किसी अन्य सार्वजनिक host पर request भेजने से पहले अस्वीकार होती है; अस्वीकृति संगठन के audit trail में `privacy/egress_refused` के रूप में दर्ज होती है और Compliance, Data residency के अंतर्गत दिखाई जाती है।

| सेटिंग | मान | प्रभाव |
| --- | --- | --- |
| `QUIRE_EGRESS_ALLOW_HOSTS` | Hostname की comma से अलग सूची या हर subdomain के लिए `*.example.org` | अतिरिक्त host जहाँ EU संगठन पहुँच सकता है। Webhook, xAPI और SIEM endpoint, blog feed और Amazon SES host यहाँ होने चाहिए, क्योंकि संगठन इन्हें स्वयं चुनता है और कोई सेवा इन्हें घोषित नहीं करती। Loopback, निजी पते और `web` या `clamav` जैसे एक-लेबल नाम आपके अपने network हैं, इनकी जाँच कभी नहीं होती |

UK और US के संगठनों पर host सूची की यह पाबंदी नहीं है; उन पर सेवा के क्षेत्र की जाँच लागू रहती है। सूची worker पर सेट करें; admin पृष्ठ allowlist दिखाने के लिए web tier पर इसे पढ़ता है, इसलिए इसे `docker/.env` में रखें, जिसे हर सेवा पढ़ती है।

Application की जाँच स्पष्ट त्रुटि और audit प्रविष्टि देती है, पर वही गारंटी नहीं: code में गलती हो सकती है। गारंटी network से आती है। Compose इसे आपके लिए लागू नहीं करता। क्षेत्रीय स्टैक में `worker` और `web` सेवाओं को `internal: true` network पर रखें, जहाँ बाहर जाने का एकमात्र रास्ता ऐसा egress proxy हो (जैसे Squid या tinyproxy container) जो `QUIRE_EGRESS_ALLOW_HOSTS` के समान host और कॉन्फ़िगर सेवाओं के host स्वीकार करे; उन सेवाओं के लिए `HTTPS_PROXY` सेट करें। Residency पृष्ठ application की अनुमति वाले सटीक host दिखाता है, ताकि दोनों सूचियों की तुलना की जा सके।

## स्वास्थ्य <!--quire:health-->

| Endpoint | अर्थ |
| --- | --- |
| `/healthz` | जीवंतता: प्रक्रिया उत्तर देती है। Compose health check इसका उपयोग करते हैं |
| `/readyz` | तत्परता: निर्भरताएँ पहुँच योग्य हैं और हर वैकल्पिक सेवा कॉन्फ़िगर है या नहीं, इसकी स्थिति। Load balancer को यहाँ इंगित करें |

`docker compose -f docker/compose.yaml ps` हर सेवा का स्वास्थ्य दिखाता है।

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

`proxy` सेवा (Caddy, Apache-2.0, `docker/caddy/Caddyfile`) डिफ़ॉल्ट स्टैक का हिस्सा है। यह 80 और 443 पोर्ट पर उत्तर देती और अनुरोध यहाँ भेजती है:

| 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` प्रोफ़ाइल के साथ (`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` (डिफ़ॉल्ट): `localhost`, `*.localhost` और `lvh.me` के लिए Caddy की अपनी certificate authority। इसकी root पर एक बार भरोसा करें, फिर यह चलाएँ:

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

  `quire-local-ca.crt` को system या browser के trust store में जोड़ें। `curl` के साथ `--cacert` दें।
- E-mail पता: असली hostname के लिए automatic ACME certificate (पहले Let's Encrypt, फिर ZeroSSL)। दोनों origin और हर tenant host का DNS यहाँ इंगित करना चाहिए और internet से 80 तथा 443 पोर्ट पहुँच योग्य होने चाहिए।

Tenant host का certificate पहली बार आने पर माँगा जाता है, और केवल तब जब web पुष्टि करे कि नाम इस इंस्टॉल का है (`/tls-allowed`, Compose network पर पूछा जाता है)। Wildcard certificate या DNS provider plugin की ज़रूरत नहीं, और कोई अनजान व्यक्ति host की ओर नाम इंगित करके certificate जारी नहीं करवा सकता। Certificate तथा स्थानीय authority `caddy-data` वॉल्यूम में रहती हैं; `internal` उपयोग करने पर बाकी के साथ इसका बैकअप लें।

Web, `X-Forwarded-For` पर केवल proxy से आने पर भरोसा करता है: proxy का स्थिर पता (`QUIRE_PROXY_ADDRESS`, डिफ़ॉल्ट `172.29.64.10`) स्थिर subnet (`QUIRE_COMPOSE_SUBNET`) पर है और `QUIRE_TRUSTED_PROXY_CIDRS` उसी पते को बताता है। यदि subnet होस्ट के किसी 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 समाप्त करें। `QUIRE_APP_ORIGIN`, `QUIRE_CONTENT_ORIGIN` और `QUIRE_COLLAB_URL` (`wss://`) में सार्वजनिक पते सेट करें और `QUIRE_TRUSTED_PROXY_CIDRS` में अपने proxy की address range दें।

## समस्या निवारण <!--quire:troubleshooting-->

- `init` का "QUIRE_DATABASE_ID is not a UUID" संदेश के साथ बाहर निकलना: `uuidgen` से मान सेट करें।
- `web` का "did not start on compose" के साथ पुनः शुरू होना: log में हर ऐसी सेटिंग सूचीबद्ध है जिसका पालन नहीं हो सकता और उसके बदले क्या उपयोग करें।
- पहली शुरुआत के बाद `.env` में role पासवर्ड बदलने से कुछ नहीं होता: init script केवल एक बार चलती है। `ALTER ROLE` इस्तेमाल करें।
- `CLAMAV_URL` सेट होने पर scan error से upload विफल हों: पहली शुरुआत में ClamAV अपनी signatures डाउनलोड करता है, जिसमें कुछ मिनट लगते हैं।

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