FireFeed Docs

Deploy

Come è deployato FireFeed in produzione.

Production usa app.fire-feed.com (Feed-Manager), oms.fire-feed.com (OMS) e docs.fire-feed.com. Development usa domini separati. Entrambi gli stack vivono sullo stesso host Dokploy con immagini, container, volumi e cookie distinti.

Compose

services:
  migrator: # prisma migrate deploy → exit 0/1 (init-container)
  web: # Next.js 15 Feed-Manager,    porta 3000  → exp.fire-feed.com
  worker: # Fastify + BullMQ,           porta 4000  (interno)
  oms: # Next.js 15 OMS sorella,     porta 3010  → oms.fire-feed.com
  docs: # Fumadocs standalone,        porta 3001  → docs.fire-feed.com
networks:
  - firefeed-net # interno
  - dokploy-network # condiviso con Postgres e Redis managed
volumes:
  - firefeed-storage # condiviso web ↔ worker per file export scaricabili

Ogni ambiente deve valorizzare FIREFEED_DEPLOYMENT e FIREFEED_COOKIE_NAMESPACE con development o production. Il compose non imposta container_name: Docker Compose usa il project name Dokploy per isolare i container e i volumi. I tag immagine includono invece esplicitamente FIREFEED_DEPLOYMENT, evitando collisioni fra build sullo stesso host. FIREFEED_COOKIE_NAMESPACE e NEXT_PUBLIC_APP_URL sono passati anche come build arg alle immagini Next.js, così middleware e bundle client ricevono i valori corretti per ciascun ambiente.

Definito in docker-compose.prod.yml in repo root.

OMS — modulo sorella

oms.fire-feed.com è l'app sorella di Feed-Manager dedicata all'order management cross-marketplace (port da oms-module di ADIC). Condivide stessa session Keycloak via SSO trasparente — vedi auth-sso per il flow dettagliato.

Setup richiesto (una tantum, prima del primo deploy):

  1. Keycloak client firefeed-oms nel realm firefeed:
    • Redirect URI: https://oms.fire-feed.com/api/auth/callback/keycloak
    • Web origins: https://oms.fire-feed.com
    • Access type: confidential, Standard flow enabled
  2. DNS A record oms.fire-feed.com → stesso IP di app.fire-feed.com
  3. Env vars nel .env Dokploy (è unico per tutto il compose — vedi nota sotto):
    • AUTH_OMS_KEYCLOAK_ID=firefeed-oms
    • AUTH_OMS_KEYCLOAK_SECRET=<secret>
    • OMS_PUBLIC_URL=https://oms.fire-feed.com (link pubblico Feed-Manager → OMS, risolto a runtime)
    • OMS_URL=https://oms.fire-feed.com (per chiamate server-side cross-modulo)

Perché AUTH_OMS_* e non AUTH_KEYCLOAK_* — In Dokploy il file .env è singolo per l'intero compose: web/worker/oms/docs lo condividono. Non possiamo quindi avere AUTH_KEYCLOAK_ID=firefeed-web e AUTH_KEYCLOAK_ID=firefeed-oms al tempo stesso. Il service oms ha un blocco environment: esplicito nel docker-compose.prod.yml che mappa ${AUTH_OMS_KEYCLOAK_ID}AUTH_KEYCLOAK_ID solo dentro al suo container, ignorando il valore "web" presente nel .env. Web e worker continuano a leggere AUTH_KEYCLOAK_ID=firefeed-web come prima.

Le tabelle config (oms_channels, oms_sync_logs) vivono in public come per Feed-Manager. Le tabelle volume per company (orders, order_items, shipments, returns) vivono in oms_<companyId>, create on-demand da SchemaService.createOmsSchema() al primo accesso dell'utente nella sua company.

Variabili ambiente

Tutte in .env adiacente al compose (popolato da Dokploy UI o copiato da .env.prod.example):

DATABASE_URL=postgresql://...
REDIS_URL=redis://...
AUTH_SECRET=...
AUTH_KEYCLOAK_ID=...
AUTH_KEYCLOAK_SECRET=...
AUTH_KEYCLOAK_ISSUER=...
AUTH_TRUST_HOST=true
RESEND_API_KEY=...
APP_URL=https://app.fire-feed.com
DOCS_URL=https://docs.fire-feed.com

Build

docker compose -f docker-compose.prod.yml build

Quattro immagini multi-stage (Node 20-alpine + pnpm 10.17): firefeed-<ambiente>-web, firefeed-<ambiente>-worker, firefeed-<ambiente>-oms, firefeed-<ambiente>-docs. Il migrator riusa l'immagine worker dello stesso ambiente come init-container (vedi commento dedicato in docker-compose.prod.yml).

Migrazioni Prisma

Automatiche: il compose include un service migrator (init-container) che gira prisma migrate deploy ad ogni docker compose up. Web e worker dichiarano depends_on: migrator: condition: service_completed_successfully, quindi:

  • migration OK → web/worker partono normalmente
  • migration KO → l'intero deploy si blocca (fail-fast, nessuna app che gira contro DB con schema sbagliato)

Log del solo migrator (one-shot, esce a fine job):

docker compose -f docker-compose.prod.yml logs migrator

Lancio manuale (per debug o se vuoi applicare prima del up):

docker compose -f docker-compose.prod.yml run --rm migrator

Gli schemi company_<id> (Feed-Manager project tables) e oms_<id> (OMS volume tables) vengono creati on-demand dal SchemaService rispettivamente al primo project create e al primo accesso OMS della company (fuori dal flusso Prisma migrate).

Logging

docker compose -f docker-compose.prod.yml logs -f web worker docs

Log strutturati JSON dove possibile. Per la pipeline, il database table pipeline_logs è la source of truth (consultabile anche via UI Run Detail).

Reverse proxy

Una sola istanza Traefik/Caddy davanti che route:

  • exp.fire-feed.comweb:3000
  • docs.fire-feed.comdocs:3001

I labels Docker (dokploy.domain=...) abilitano l'autoconfigurazione.

Limiti attuali

Stato Maggio 2026, post chiusura del piano di consolidamento:

  • Storage artifact export: WS2B chiusa — gli artifact vivono su RustFS (S3-compatible) a exp.data.fire-feed.com, manifest persistito in export_artifacts. Volume Docker firefeed-storage resta come buffer local + fallback per export legacy. Retention via cron giornaliero (WS8): tiene gli ultimi EXPORT_ARTIFACT_RETENTION (default 10) per export, droppa il resto da S3 + manifest + filesystem. Vedi ADR 0001.
  • Replica DB: single-instance Postgres managed da Dokploy. Backup affidati alla policy Dokploy. Niente read replica.
  • Replica Redis: single-instance. Persistenza configurata via Dokploy.
  • Worker single-replica: una sola istanza firefeed-worker. Concurrency interna tunable via env (PIPELINE_CONCURRENCY etc, vedi Concurrency). Scaling a N istanze richiede deduplicazione delle responsibility (e.g. cron temp-cleanup attivo solo su instance 0).
  • Multi-region: no — single region, single host. Latency utenti EU/IT, OK; utenti US/APAC vedranno latenza.
  • OpenTelemetry distributed tracing: non attivo. Solo Prometheus metrics su worker (vedi GET /metrics worker, porta 4000 interna).

Per il dettaglio degli env disponibili: Variabili d'ambiente.

Observability

  • Healthcheck: web /api/health + worker /health pingano DB e Redis. Ritornano 503 se uno fallisce → Dokploy/Traefik instradano solo verso instance sane.
  • Metrics Prometheus: worker /metrics espone counter pipeline runs, histogram durata, gauge BullMQ jobs, histogram size artifact. Da scrapeare con Prometheus/vmagent/grafana-agent.
  • Logs strutturati: worker usa pino (Fastify built-in + createJobLogger(pipelineRunId) per job context). docker compose logs -f worker | jq per inspect.
  • Pipeline log persistenti: tabella pipeline_logs con pipelineRunId e step, sempre disponibile via UI Run Detail.

DB recovery

In caso di crash Postgres (could not find redo location), procedura:

  1. Stop dei container che scrivono (web, worker).
  2. Snapshot del volume (tar -cf snapshot.tar /var/lib/postgresql/data).
  3. docker run --rm -v <volume>:/var/lib/postgresql/data postgres:15 gosu postgres pg_resetwal -f /var/lib/postgresql/data
  4. Riavvio container DB, verifica SELECT pg_is_in_recovery().
  5. REINDEX SYSTEM, opzionale VACUUM ANALYZE.
  6. Riavvio app.

Importante: pg_resetwal -f lascia spesso il system catalog incoerente (orfani in pg_attrdef, pg_attribute, indici user-level con heap tid stale). Dopo qualsiasi pg_resetwal pianifica subito: REINDEX (CONCURRENTLY) DATABASE firefeedexp + TRUNCATE pg_statistic + ANALYZE. Se l'errore could not find tuple for attrdef <oid> torna, intervento manuale su pg_depend/pg_class con allow_system_table_mods=on. Documentato nei runbook interni.

In questa pagina