---
title: "Instalar Quire con Docker Compose"
description: "Instala Quire na túa propia infraestrutura con Docker Compose."
image: "https://docs.quirelms.com/og.png"
---

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

# Instalar Quire con Docker Compose

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

Este é o produto completo nun único host: o LMS, as tarefas en segundo plano, os servizos de tempo real e edición colaborativa, e todos os servizos opcionais detrás dun perfil. O deseño descríbese na sección 2 de `docs/architecture/23-ops.md`.

Outras opcións: [Vercel](/gl/ops/vercel/) e [Cloudflare Workers](/gl/ops/cloudflare/) executan só a capa web. As actualizacións explícanse en [upgrade.md](/gl/ops/upgrade/), e as copias de seguridade e a proba de restauración en [backup-restore.md](/gl/ops/backup-restore/).

## Requisitos <!--quire:what-you-need-->

- Docker Engine 27 ou posterior co complemento Compose 2.30 ou posterior.
- 4 núcleos de CPU e 8 GB de memoria para a configuración predeterminada; 8 núcleos e 16 GB con `--profile full` (ClamAV ocupa aproximadamente 1,5 GB só coas sinaturas).
- Un nome DNS para a capa web e outro para o contido non fiable. Deben ser hosts distintos: os paquetes SCORM e o HTML cargado execútanse na orixe de contido para que nunca poidan ler as cookies do LMS.
- Para unha proba local, `lvh.me` e `*.localhost` resolven a 127.0.0.1, como configura `docker/.env.example`. O servizo `proxy` da propia pila serve ambos con https mediante unha autoridade de certificación local, polo que non hai que instalar nada máis (consulta «TLS»).
- Os portos 80 e 443 deben estar libres no host (`QUIRE_PROXY_HTTP_PORT` e `QUIRE_PROXY_HTTPS_PORT` permiten cambialos).

## Primeira execución <!--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` crea `docker/.env` a partir de `docker/.env.example` e xera todos os segredos (contrasinais da base de datos, claves de sinatura e mestra, par de claves para iniciar contido) e a clave de sinatura dos puntos de control de auditoría en `docker/secrets/audit-signing-key.pem`, que Compose monta nos traballadores como segredo. Só necesita `sh`, `awk` e `openssl`, e non permite sobrescribir un `docker/.env` existente. Garda ambos ficheiros fóra do host: sen `QUIRE_MASTER_KEY`, a base de datos restaurada non poderá descifrar as credenciais almacenadas. Para cubrir o ficheiro manualmente, utiliza `cp docker/.env.example docker/.env`; o propio ficheiro explica como xerar cada segredo.

Ambas as orixes deben usar `https`: o servizo de contido rexeita http sen cifrar en produción e as dúas non poden compartir o mesmo dominio rexistrable. O servizo `proxy` termina TLS para ambas (consulta «TLS»); `init-env.sh` rexeita as orixes `http://`.

A pila arranca nunha orde fixa e cada paso agarda a que remate o anterior:

1. `postgres` pasa a estado saudable. No primeiro inicio, o seu script (`docker/postgres/init/90-passwords.sh`) establece os catro contrasinais dos roles.
2. `migrate` aplica todas as migracións e inicializa a cola de tarefas na base de datos de control e en cada base de datos dedicada de inquilino; comproba que todas coincidan e, a continuación, remata (consulta docs/ops/upgrade.md).
   As migracións execútanse en cada inicio e son idempotentes, polo que para actualizar abonda con usar unha imaxe nova e reiniciar.
3. `init` (`apps/web/src/first-run.ts`) rexistra a base de datos da aplicación en `QUIRE_DATABASE_ID` e, se se definiu `QUIRE_SETUP_ADMIN_EMAIL`, crea a primeira organización e a súa persoa administradora. O enderezo de inicio de sesión e un contrasinal xerado aparecen unha soa vez en `docker compose logs init`.
4. Inícianse `web`, `content`, `worker`, `scheduler`, `collab` e `centrifugo`.
5. `proxy` arranca cando `web` e `content` están saudables.

Abre `https://demo.` seguido do dominio da aplicación (o rexistro `init` amosa o enderezo exacto para iniciar sesión) e accede. Nunha instalación local, confía primeiro na autoridade de certificación do proxy (consulta «TLS»). Cambia o contrasinal xerado en `/account/security`.

Se se inicia un proceso sen un segredo obrigatorio, este négase a arrancar e indica no rexistro que axuste falta. A pila non queda nunca configurada a medias.

## Servizos e perfís <!--quire:services-and-profiles-->

| Servizo | Perfil | Función |
| --- | --- | --- |
| postgres | sempre | A base de datos (PostgreSQL 18 con pgvector, construída desde `docker/postgres.Dockerfile`), con arquivo WAL desde o primeiro inicio |
| migrate, init | sempre | Tarefas dunha soa execución: migracións e, despois, primeira execución |
| web | sempre | O LMS, no porto `QUIRE_HTTP_PORT` (8080) |
| content | sempre | A orixe de contido non fiable, no porto `QUIRE_CONTENT_PORT` (8081) |
| worker | sempre | Tarefas en segundo plano: correo, informes, procesamento de ficheiros e webhooks |
| scheduler | sempre | Tarefas recorrentes: rexistra as 64 programacións de execución e entrégallas ao traballador; só hai un líder á vez |
| collab | sempre | Servizo websocket de edición colaborativa, no porto `QUIRE_COLLAB_HTTP_PORT` (1234) |
| centrifugo | sempre | Distribución en tempo real, no porto `QUIRE_REALTIME_PORT` (8000) |
| proxy | sempre | Caddy, o punto de entrada TLS nos portos 80 e 443 (consulta «TLS») |
| valkey | `cache` | Caché e límites de solicitudes |
| clamav | `scan` | Análise de malware dos ficheiros cargados |
| gotenberg | `preview` | Previsualización de documentos de Office en PDF e creación de certificados |
| imgproxy | `images` | Redimensionamento e conversión de imaxes |
| transcoder | `video` | Imaxe do traballador con ffmpeg só baixo LGPL para crear versións de vídeo |
| seaweedfs | `storage` | Almacenamento de obxectos compatible con S3 neste host |
| otelcol | `observability` | Colector de OpenTelemetry |
| mailpit | `devmail` | Captura todo o correo saínte para probar Quire |
| backup | `backup` | Copia de seguridade base dunha soa execución; consulta backup-restore.md |
| backup-scheduler, backup-offsite | `backup` | Copia base cada `QUIRE_BACKUP_INTERVAL_HOURS` e copias externas cifradas cunha proba de verificación semanal |
| h5p | `h5p` | Imaxe da ferramenta H5P LTI 1.3 que proporcionas en `QUIRE_H5P_IMAGE`, no porto `QUIRE_H5P_PORT` (8090); consulta «Conectar un provedor H5P» |

`--profile full` inicia todos os servizos opcionais agás `backup` e `h5p`.
Inicia un deles con `docker compose -f docker/compose.yaml --profile scan up -d`.
Quire segue funcionando se falta un servizo opcional e indica o que falta: sen
analizador, os ficheiros cárganse sen analizar e avísase á persoa administradora;
sen Gotenberg, os ficheiros pódense descargar pero non previsualizar; sen
transcodificador, o vídeo reprodúcese como ficheiro orixinal.

`docker/third-party-containers.yaml` enumera todas as imaxes de terceiros e as obrigas asociadas ás súas licenzas.

### Conectar un provedor H5P <!--quire:connecting-an-h5p-provider-->

Quire non incorpora nin distribúe un entorno de execución H5P nin un compoñente auxiliar (ADR 0019). Se utilizas H5P, proporciona unha subscrición aloxada propia ou xestiona unha instancia propia de H5P, autoaloxada e separada de Quire. Rexistra ese provedor como ferramenta externa LTI 1.3 e engade o seu contido aos cursos como actividades da ferramenta. Quire intercambia cualificacións e progreso das actividades mediante Assignment and Grade Services (AGS) de LTI.
Se o provedor tamén envía enunciados xAPI, configura por separado o almacén de enunciados xAPI de Quire; o intercambio de cualificacións e progreso con AGS non envía enunciados xAPI. As importacións de Moodle indican que as actividades H5P requiren unha conexión cunha ferramenta LTI. O provedor segue sendo responsable do seu entorno de execución H5P, da creación de contido, do banco de contidos e do historial de intentos.

Para executar a túa propia instancia autoaloxada neste host, establece `QUIRE_H5P_IMAGE` coa súa imaxe e inicia o perfil `h5p`. Compose publícaa no porto `QUIRE_H5P_PORT` (8090) e conserva os datos no volume `h5p-data`; a imaxe e as obrigas que leva asociadas son responsabilidade túa.

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

Todos os procesos len `docker/.env`. O modelo `docker/.env.example` enumera cada axuste co seu valor predeterminado. Os grupos son:

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

| Axuste | Significado |
| --- | --- |
| `QUIRE_APP_ORIGIN` | Enderezo público do LMS, como `https://learn.example.com` |
| `QUIRE_CONTENT_ORIGIN` | Orixe do contido, nun host diferente |
| `QUIRE_PLATFORM_DOMAINS` | Dominios onde residen as organizacións, separados por comas |
| `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` | Aquí é `compose`. Consulta as outras guías para `vercel` e `cloudflare` |
| `QUIRE_TRUSTED_PROXY_CIDRS` | Os proxies cuxo `X-Forwarded-For` se considera fiable |

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

| Axuste | Significado |
| --- | --- |
| `QUIRE_SECRET_KEY` | Asina sesións e tokens. 64 caracteres hexadecimais |
| `QUIRE_MASTER_KEY` | Cifra por capas as credenciais almacenadas, como segredos de SSO e webhook. 32 bytes en base64. A capa web e o traballador necesitan o mesmo valor. Rotación: [key-rotation.md](/gl/ops/key-rotation/) |
| `QUIRE_MASTER_KEY_VERSION` | Etiqueta da versión da clave mestra; `v1` se non se define. Aumenta o valor ao rotala |
| `QUIRE_MASTER_KEY_RETIRED` | Claves mestras anteriores que aínda fan falta para ler o contido cifrado, co formato `v1=<base64>`. Elimínaas cando remate a rotación e non quede nada sen resolver |
| `QUIRE_COLLAB_SIGNING_KEY` | Compartida entre web e collab para asinar tokens de edición |
| `QUIRE_BACKUP_SIGNING_KEY` | Asina copias de seguridade de cursos (opcional) |

Garda unha copia de `QUIRE_MASTER_KEY` noutro lugar, fóra deste host. Sen ela, a base de datos restaurada non poderá descifrar as credenciais almacenadas.

### Base de datos <!--quire:database-->

| Axuste | Significado |
| --- | --- |
| `POSTGRES_PASSWORD` | Contrasinal do superusuario, utilizado polo contedor e polas copias de seguridade |
| `QUIRE_DB_APP_PASSWORD`, `QUIRE_DB_MIGRATOR_PASSWORD`, `QUIRE_DB_REPORT_PASSWORD`, `QUIRE_DB_AUDIT_PASSWORD` | Contrasinais dos roles, establecidos no primeiro inicio |
| `DATABASE_URL` | Rol da aplicación. A seguridade a nivel de fila aplícase a todas as consultas que realiza |
| `DATABASE_MIGRATOR_URL`, `QUIRE_MIGRATION_URL` | Rol de migracións, para `migrate` e `init` |
| `QUIRE_SUPERUSER_URL` | Utilizado só na primeira execución |
| `QUIRE_REPORT_DATABASE_URL` | Rol de informes de só lectura, para os informes e o xerador de informes |
| `QUIRE_AUDIT_DATABASE_URL` | Rol de auditoría, para a consola de auditoría e a exportación SIEM |
| `QUIRE_DATABASE_ID` | Calquera UUID, fixo durante toda a vida da instalación |

Os contrasinais dos roles aplícanse só cando se crea o volume da base de datos. Para cambialos máis adiante, utiliza `ALTER ROLE` e actualiza o URL correspondente.

`QUIRE_REPORT_DATABASE_URL` úsase para a base de datos física configurada por `DATABASE_URL`. Para calquera outra base de datos física rexistrada, define o seu propio URL de conexión `quire_report` nos entornos da web e do traballador e introduce o nome da variable no campo **Variable de entorno de informes** desa base de datos como `env:NAME`. A referencia debe apuntar á mesma base de datos que a conexión da aplicación, idealmente á súa réplica de lectura. Todas as superficies de informes seguen o inquilino ata a conexión de informes propia da súa base de datos: xerador e informes gardados, envíos programados, exportacións, analítica, rexistro de auditoría, recursos de auditoría da API REST e busca de auditoría do asistente. Ningunha delas utiliza nunca o URL de informes doutra base de datos. Se unha base de datos non ten conexión de informes, os informes normais execútanse coa conexión propia da aplicación nesa base de datos, mentres que a analítica e todas as lecturas de auditoría se rexeitan e o indican, xa que o rol da aplicación non pode ler o rexistro de auditoría.

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

| Axuste | Opcións desta versión | Notas |
| --- | --- | --- |
| `QUIRE_STORAGE_DRIVER` | `local` (predeterminado), `s3` ou `azure` | `local` garda os ficheiros no volume `files`. `s3` abarca AWS S3, a interoperabilidade con R2 e GCS, e outros almacéns compatibles con S3, con cargas multipartes reanudables |
| `QUIRE_REALTIME_DRIVER` | `inprocess` (predeterminado), `sse`, `centrifugo` ou `durable_objects` | `inprocess` é axeitado para un único contedor web; utiliza `centrifugo` ou `sse` cando haxa varios |
| `QUIRE_CACHE_DRIVER` | `memory` (predeterminado), `postgres` ou `valkey` | `memory` é independente para cada proceso; utiliza `valkey` ou `postgres` para que os límites de solicitudes sexan compartidos polos contedores |
| `QUIRE_VIDEO_DRIVER` | `ffmpeg` (predeterminado) ou `progressive_mp4` | Ou un provedor aloxado: Cloudflare Stream, Mux ou Bunny, mediante as súas claves |
| `QUIRE_IMAGE_DRIVER` | `noop` (predeterminado), `imgproxy` ou `cloudflare` | `noop` serve cada imaxe co tamaño orixinal. `imgproxy` require o perfil `images` e os axustes indicados máis abaixo; `cloudflare` utiliza Cloudflare Images |
| `QUIRE_MEETING_PROVIDER` | `bbb`, `zoom`, `teams`, `meet`, `jitsi` ou `in_process` | Provedor predeterminado da plataforma para sesións en directo. Se non se define, as sesións en directo indican que non están configuradas ata que unha organización conecte a súa conta en Integracións, Provedor de sesións en directo. A conta propia da organización sempre prevalece sobre este valor. Só se len os axustes propios de cada provedor (`BBB_URL` e `BBB_SECRET`, as variables `ZOOM_*`, `TEAMS_*`, `GOOGLE_MEET_*` e `JITSI_*`) se aquí se escolle ese provedor |
| `QUIRE_MEETING_REGIONS` | Lista separada por comas con `eu`, `uk`, `us` | Lugares onde procesa as reunións o provedor predeterminado da plataforma. Se non se define, como antes, non se comproba coas organizacións vinculadas a unha rexión. A páxina da conta propia da organización indica as súas rexións |

Se nesta versión se indica un valor de controlador que non está incluído, a capa web rexeita o inicio e indica o axuste en vez de substituílo silenciosamente polo valor predeterminado.

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

As páxinas solicitan imaxes en catro tamaños fixos mediante
`/api/files/{id}/image/{size}`; esta ruta comproba o mesmo acceso que o propio ficheiro e, a continuación, redirixe ao servizo de imaxes. Cada organización pode solicitar `QUIRE_IMAGE_SPECS_PER_HOUR` imaxes e combinacións de tamaño novas por hora (2000 de xeito predeterminado); non contan os tamaños xa xerados durante esa hora. Se hai máis dun contedor web, utiliza `valkey` ou `postgres` para `QUIRE_CACHE_DRIVER`, de xeito que o límite se aplique a todos.

| Axuste | Controlador | Notas |
| --- | --- | --- |
| `IMGPROXY_URL` | `imgproxy` | Enderezo de imgproxy ao que acceden os navegadores, por exemplo `https://images.example.org`. O perfil `images` publícao no porto `QUIRE_IMAGES_PORT` (8082) |
| `IMGPROXY_KEY`, `IMGPROXY_SALT` | `imgproxy` | Cadeas hexadecimais, iguais aos valores cos que se inicia imgproxy. Xera cada unha con `openssl rand -hex 32`. Quire asina todos os enderezos das imaxes con elas, polo que imgproxy non representa ningunha imaxe que Quire non solicitase |
| `QUIRE_IMAGE_SOURCE_ORIGIN` | `imgproxy` con almacenamento local | Orixe desde a que imgproxy obtén os ficheiros orixinais. Compose establece `http://web:3000`. Con almacenamento `s3` ou `azure`, imgproxy obtén as imaxes do bucket e non se utiliza este axuste |
| `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_IMAGES_TOKEN`, `CLOUDFLARE_IMAGES_ACCOUNT_HASH` | `cloudflare` | Token da API con permisos de edición de Images e o hash da conta que aparece en Images, Developer resources. Activa as variantes flexibles para a conta |
| `CLOUDFLARE_IMAGES_SIGNING_KEY` | `cloudflare` | Opcional. Se se define, as imaxes son privadas e todos os enderezos asínanse e caducan. Sen ela, as imaxes son públicas e os enderezos derívanse de `QUIRE_SECRET_KEY`, polo que ninguén os pode adiviñar |

Cloudflare Images conserva a súa propia copia de cada orixinal que serve. Cando se elimina un ficheiro, o traballador elimina esa copia antes que o orixinal.

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

As tarefas en segundo plano utilizan pg-boss na mesma base de datos Postgres, polo que non hai que executar nin configurar un servizo de cola. As tarefas engádense na mesma transacción que o cambio que as provocou, polo que un fallo non pode perder unha tarefa nin executala dúas veces. `QUIRE_QUEUE_DRIVER` ten aquí o valor predeterminado `pgboss`; `vercel` e `cloudflare` trasladan só as notificacións lixeiras e as entregas de webhook á cola propia da plataforma; as guías de Vercel e Cloudflare explican as colas e como as súas capas web engaden tarefas.

### Correo electrónico <!--quire:email-->

Configura unha destas opcións:

- `QUIRE_EMAIL_PROVIDER_CONFIG`: obxecto JSON que identifica un provedor HTTP e as súas credenciais, como `{"provider":"postmark","token":"..."}`. Admítense Postmark, Amazon SES, Mailgun, SendGrid e Resend.
- `QUIRE_SMTP_URL`: `smtp://user:password@host:587`. Só para este destino; os destinos sen servidor bloquean SMTP.

`QUIRE_MAIL_FROM` indica o remitente. Para probar Quire, inicia o perfil `devmail`, define `QUIRE_SMTP_URL=smtp://mailpit:1025` e consulta o correo en `http://localhost:8025`.

### Servizos opcionais <!--quire:optional-services-->

| Axuste | Co perfil |
| --- | --- |
| `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` ou `QUIRE_MEILISEARCH_URL` | Busca externa; se non, utilízase a busca de texto completo de Postgres |
| `QUIRE_BREACH_CHECK_PROVIDER=off`, `QUIRE_BREACH_CHECK_URL` | Comprobación de contrasinais expostos. Activada de xeito predeterminado contra `api.pwnedpasswords.com` (só se envía un prefixo de cinco caracteres do hash); `off` desactívaa e o URL permite indicar unha API de intervalos propia |

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

`OTEL_EXPORTER_OTLP_ENDPOINT` indica o colector ao que todos os procesos envían trazas e métricas; co perfil `observability` é `http://otelcol:4318` e `docker/otel-collector.yaml` é o ficheiro onde engadir o exportador do teu sistema. A capa web, o traballador, o planificador, o servizo de contido e os procesos collab envían intervalos por OTLP/HTTP (peticións web, transaccións de bases de datos de inquilino, tarefas e chamadas saíntes) cando se define; tamén envían métricas ao mesmo enderezo cada minuto (`OTEL_METRICS_EXPORTER=none` desactívaas). `OTEL_TRACES_SAMPLER_ARG` establece a proporción de trazas que se conserva. Os rexistros envíanse á saída estándar co nivel `LOG_LEVEL` e Compose fai a súa rotación. As trazas nunca conteñen datos persoais.

### Saída rexional (residencia de datos da UE) <!--quire:regional-egress-eu-data-residency-->

`QUIRE_REGION=eu` indica que a pila presta servizo a organizacións da Unión Europea. O traballador limita entón todas as peticións saíntes dunha organización vinculada á UE a unha lista de permitidos (sección 8.1 de 21-compliance.md). A lista inclúe os hosts que declaran para a rexión os servizos configurados (endpoint de almacenamento, provedor de correo, provedor de vídeo aloxado, destinos de almacenamento propios da organización, provedores de IA e conta de correo), os hosts dos servizos cunha excepción vixente e os que se enumeran en `QUIRE_EGRESS_ALLOW_HOSTS`. Rexéitase antes do envío calquera petición a outro host público; o rexeitamento anótase no rexistro de auditoría da organización como `privacy/egress_refused` e aparece en Cumprimento, Residencia de datos.

| Axuste | Valores | Efecto |
| --- | --- | --- |
| `QUIRE_EGRESS_ALLOW_HOSTS` | Lista de nomes de host separados por comas, ou `*.example.org` para todos os subdominios | Hosts adicionais aos que pode acceder unha organización da UE. Os endpoints de webhook, xAPI e SIEM, as fontes de blog e os hosts de Amazon SES deben incluírse aquí, xa que os escolle a propia organización e ningún servizo os declara. Non se comproban os enderezos de bucle local, privados e de nome único, como `web` ou `clamav`, porque pertencen á túa propia rede |

As organizacións do Reino Unido e dos Estados Unidos non están limitadas por unha lista de hosts; seguen aplicándose as comprobacións da rexión do servizo. Configura a lista no traballador; a páxina de administración léa na capa web para mostrar os hosts permitidos, así que inclúea en `docker/.env`, que len todos os servizos.

A comprobación da aplicación proporciona un erro claro e unha entrada de auditoría, pero non é a garantía: o código pode conter erros. A garantía reside na rede. Compose non a impón. Para unha pila rexional, conecta os servizos `worker` e `web` a unha rede `internal: true`, cuxa única saída pase por un proxy de saída (por exemplo, un contedor Squid ou tinyproxy) que permita os mesmos hosts que `QUIRE_EGRESS_ALLOW_HOSTS` e os dos servizos configurados; establece `HTTPS_PROXY` neses servizos. A páxina de residencia enumera os hosts exactos que permite a aplicación para poder comparar ambas as listas.

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

| Endpoint | Significado |
| --- | --- |
| `/healthz` | Actividade: o proceso responde. Compose utiliza este endpoint nas comprobacións de estado |
| `/readyz` | Dispoñibilidade: pódese acceder ás dependencias e cada servizo opcional indica se está configurado. Indica este endpoint ao equilibrador de carga |

`docker compose -f docker/compose.yaml ps` amosa o estado de cada servizo.

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

O servizo `proxy` (Caddy, Apache-2.0, `docker/caddy/Caddyfile`) forma parte da configuración predeterminada. Responde nos portos 80 e 443 e enruta:

| Host ou ruta | Destino |
| --- | --- |
| `QUIRE_PROXY_CONTENT_HOST` | `content` |
| `QUIRE_PROXY_APP_HOST`, cada subdominio de inquilino e dominio personalizado | `web` |
| `/_collab/` neses hosts | `collab` (websocket, `QUIRE_COLLAB_URL`) |
| `/_realtime/connection/` neses hosts | websocket de cliente de `centrifugo`; a súa API de servidor nunca se expón |
| `/_images/` neses hosts | `imgproxy`, co perfil `images` (`IMGPROXY_URL`) |

`init-env.sh` deriva `QUIRE_PROXY_APP_HOST`, `QUIRE_PROXY_CONTENT_HOST`,
`QUIRE_PROXY_HTTPS_PORT`, `QUIRE_COLLAB_URL` e `IMGPROXY_URL` das dúas
orixes, para que sempre estean sincronizados. Se cambias unha orixe manualmente,
edítaos xuntos.

Os certificados dependen de `QUIRE_PROXY_TLS`:

- `internal` (predeterminado): autoridade de certificación propia de Caddy para
  `localhost`, `*.localhost` e `lvh.me`. Confía nela unha vez e, a continuación,
  abre:

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

  Engade `quire-local-ca.crt` ao almacén de confianza do sistema ou navegador. `curl`
  permite indicala con `--cacert`.
- Un enderezo de correo electrónico: certificados ACME automáticos (Let's Encrypt
  e, despois, ZeroSSL) para nomes de host reais. Os DNS de ambas as orixes e de
  todos os hosts de inquilino deben apuntar aquí e os portos 80 e 443 deben ser
  accesibles desde Internet.

Os certificados dos hosts dos inquilinos emítense baixo demanda, na primeira visita e só cando a web confirma que o nome pertence a esta instalación (consulta `/tls-allowed` na rede de Compose). Non se necesita un certificado comodín nin un complemento de provedor DNS; ademais, unha persoa allea non pode facer que se soliciten certificados simplemente apuntando un nome ao host. Os certificados e a autoridade local gárdanse no volume `caddy-data`; se utilizas `internal`, inclúe ese volume nas copias de seguridade.

A web confía só no `X-Forwarded-For` do proxy: o proxy ten un enderezo fixo (`QUIRE_PROXY_ADDRESS`, predeterminado `172.29.64.10`) nunha subrede fixa (`QUIRE_COMPOSE_SUBNET`) e `QUIRE_TRUSTED_PROXY_CIDRS` identifica ese enderezo. Se a subrede entra en conflito cunha rede do host, cambia ambos os axustes e executa `docker compose down` antes de volver iniciar con `up`.

## Detrás do teu propio proxy inverso <!--quire:behind-your-own-reverse-proxy-->

Para utilizar no seu lugar un equilibrador de carga ou proxy xa existente, deixa fóra `proxy` (`docker compose up -d --scale proxy=0`) e termina TLS diante de `web` (8080), `content` (8081), `collab` (1234, websocket) e `centrifugo` (8000, websocket). Establece os enderezos públicos en `QUIRE_APP_ORIGIN`, `QUIRE_CONTENT_ORIGIN` e `QUIRE_COLLAB_URL` (`wss://`) e o intervalo de enderezos do proxy en `QUIRE_TRUSTED_PROXY_CIDRS`.

## Resolución de problemas <!--quire:troubleshooting-->

- `init` remata cun erro «QUIRE_DATABASE_ID is not a UUID»: establece o valor con `uuidgen`.
- `web` reiníciase cun erro «did not start on compose»: o rexistro enumera cada axuste que non pode utilizar e a alternativa correspondente.
- Cambiar o contrasinal dun rol en `.env` despois do primeiro inicio non ten efecto: o script de inicialización só se executa unha vez. Utiliza `ALTER ROLE`.
- As cargas fallan cun erro de análise cando se define `CLAMAV_URL`: ClamAV descarga as sinaturas no primeiro inicio, o que tarda uns minutos.

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