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 scaricabiliOgni 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):
- Keycloak client
firefeed-omsnel realmfirefeed:- Redirect URI:
https://oms.fire-feed.com/api/auth/callback/keycloak - Web origins:
https://oms.fire-feed.com - Access type: confidential, Standard flow enabled
- Redirect URI:
- DNS A record
oms.fire-feed.com→ stesso IP diapp.fire-feed.com - Env vars nel
.envDokploy (è unico per tutto il compose — vedi nota sotto):AUTH_OMS_KEYCLOAK_ID=firefeed-omsAUTH_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 nonAUTH_KEYCLOAK_*— In Dokploy il file.envè singolo per l'intero compose: web/worker/oms/docs lo condividono. Non possiamo quindi avereAUTH_KEYCLOAK_ID=firefeed-webeAUTH_KEYCLOAK_ID=firefeed-omsal tempo stesso. Il serviceomsha un bloccoenvironment:esplicito neldocker-compose.prod.ymlche mappa${AUTH_OMS_KEYCLOAK_ID}→AUTH_KEYCLOAK_IDsolo dentro al suo container, ignorando il valore "web" presente nel.env. Web e worker continuano a leggereAUTH_KEYCLOAK_ID=firefeed-webcome 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.comBuild
docker compose -f docker-compose.prod.yml buildQuattro 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 migratorLancio manuale (per debug o se vuoi applicare prima del up):
docker compose -f docker-compose.prod.yml run --rm migratorGli 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.com→web:3000docs.fire-feed.com→docs: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 inexport_artifacts. Volume Dockerfirefeed-storageresta come buffer local + fallback per export legacy. Retention via cron giornaliero (WS8): tiene gli ultimiEXPORT_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_CONCURRENCYetc, 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 /metricsworker, porta 4000 interna).
Per il dettaglio degli env disponibili: Variabili d'ambiente.
Observability
- Healthcheck:
web /api/health+worker /healthpingano DB e Redis. Ritornano 503 se uno fallisce → Dokploy/Traefik instradano solo verso instance sane. - Metrics Prometheus:
worker /metricsespone 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 | jqper inspect. - Pipeline log persistenti: tabella
pipeline_logsconpipelineRunIdestep, sempre disponibile via UI Run Detail.
DB recovery
In caso di crash Postgres (could not find redo location), procedura:
- Stop dei container che scrivono (
web,worker). - Snapshot del volume (
tar -cf snapshot.tar /var/lib/postgresql/data). docker run --rm -v <volume>:/var/lib/postgresql/data postgres:15 gosu postgres pg_resetwal -f /var/lib/postgresql/data- Riavvio container DB, verifica
SELECT pg_is_in_recovery(). REINDEX SYSTEM, opzionaleVACUUM ANALYZE.- Riavvio app.
Importante:
pg_resetwal -flascia spesso il system catalog incoerente (orfani inpg_attrdef,pg_attribute, indici user-level conheap tidstale). Dopo qualsiasipg_resetwalpianifica subito:REINDEX (CONCURRENTLY) DATABASE firefeedexp+TRUNCATE pg_statistic+ANALYZE. Se l'errorecould not find tuple for attrdef <oid>torna, intervento manuale supg_depend/pg_classconallow_system_table_mods=on. Documentato nei runbook interni.