Skip to content

Deployment topology

Factflow’s server is a single Python process (FastAPI + uvicorn) that talks to three external systems: a message broker, PostgreSQL, and an object store. This page is the physical architecture.

flowchart TB
subgraph Client[Clients]
  CLI[factflow CLI]
  WEB[Browser — React frontend]
  HK[Webhook consumers]
end
subgraph Ingress
  LB[Load balancer / ingress]
  AUTH[SSO auth layer]
end
subgraph Server[Factflow server process]
  API[FastAPI HTTP layer]
  CON[ServiceContainer DI]
  MGR[OrchestratorManager]
  CHAT[Chat service]
  WHK[Webhook dispatcher]
end
subgraph Infra[External systems]
  BR[Message broker<br/>Artemis / RabbitMQ / Pulsar]
  DB[(PostgreSQL 18<br/>+ pgvector)]
  ST[Object storage<br/>filesystem or MinIO/S3]
  LLM[LLM providers<br/>OpenAI / Anthropic / Bedrock]
end
CLI --> LB
WEB --> LB
LB --> AUTH
AUTH --> API
API --> CON
CON --> MGR
CON --> CHAT
CON --> WHK
MGR -.publish/subscribe.-> BR
MGR -.read/write.-> ST
MGR -.commit.-> DB
CHAT -.embed/complete.-> LLM
CHAT -.vector search.-> DB
WHK -.POST.-> HK
style Server fill:#f0fdf4,stroke:#16a34a,color:#111
style Infra fill:#e6f2ff,stroke:#2563eb,color:#111
Production topology. Server is a single process; external dependencies are independent services.
ComponentResponsibilities
Load balancerTLS termination, rate limiting, request routing
SSO auth layerDNB SSO headers → user identity (server trusts forwarded headers)
FastAPI HTTP layer91 endpoints, SSE streaming, OpenAPI spec
ServiceContainerDI registry — wires every service, owns lifecycle
OrchestratorManagerAll running executions in this process
Chat serviceRAG, stateful threads, streaming responses
Webhook dispatcherListens to storage/execution events, POSTs to subscribed URLs, retries
Message brokerDurable queues + topics across all executions
PostgreSQLPipeline configs, executions, lineage, embeddings, chat, webhook deliveries
Object storageEvery pipeline artefact — HTML, markdown, segments, binaries
LLM providersExternal SaaS or local inference endpoints

For local development, Factflow ships an embedded mode that auto-provisions infrastructure via Docker:

flowchart TB
subgraph Dev[Developer laptop]
  C[factflow CLI] --> E[Embedded server]
  E --> EPG[Postgres container]
  E --> EBR[Artemis container]
  E --> FS[./output/storage]
  E -.optional.-> LLM[Real LLM APIs via env vars]
end
style Dev fill:#fef3c7,stroke:#d97706,color:#111
Embedded mode. One command brings up the whole stack. Good for development, demos, integration tests.

Startup command:

Terminal window
cd backend
uv run python -m factflow_server serve --embedded

What happens on startup:

  1. Server process starts uvicorn on port 8000
  2. Container connects to a testcontainers-managed Postgres 18 instance
  3. Starts an in-process ActiveMQ Artemis broker (or spins up a container)
  4. Points storage at a local ./output/storage/ directory
  5. Reads LLM keys from environment if present
  6. Validates every shipped YAML under backend/config/pipelines/
  7. Runs orchestrator manager ready to accept executions

Factflow does not today support multiple server processes coordinating on executions. The OrchestratorManager is process-local; a second server process wouldn’t see or manage the first’s running executions.

Horizontal scaling happens at the request-handling layer — run multiple server instances behind a load balancer, each responsible for its own executions. Sticky execution-id routing via ingress is required so status queries hit the server that owns the execution.

flowchart TB
LB[Load balancer<br/>sticky on execution_id]
LB --> S1[Server 1<br/>owns execs A, B]
LB --> S2[Server 2<br/>owns execs C, D]
LB --> S3[Server 3<br/>owns execs E, F]
S1 -.shared.-> BR[(Broker)]
S2 -.shared.-> BR
S3 -.shared.-> BR
S1 -.shared.-> DB[(Postgres)]
S2 -.shared.-> DB
S3 -.shared.-> DB
S1 -.shared.-> ST[(Storage)]
S2 -.shared.-> ST
S3 -.shared.-> ST
Horizontal scaling: many servers, one shared broker / DB / storage. The ingress must route execution-id-aware.

Multi-process coordination (cross-process OrchestratorManager) is a possible future feature but not required today at DNB scale.

Service dependencies are decoupled:

Dependency failsEffect
BrokerExecutions stall (publish/subscribe fails). New executions fail to start. Running ones pause.
PostgreSQLEverything halts. Config lookups, lineage commits, execution CRUD all fail.
StorageAdapters that persist artefacts fail. Read-side replay / download fails.
One LLM providerAdapters using that provider fail. Others continue. Factory routes to configured alternatives if set.

Graceful degradation policies are adapter-specific. Circuit breakers open on repeated adapter failure; messages route to DLQ.

  • 8000 — server HTTP (unless overridden via --port)
  • 4321 — docsite dev server (separate — not a server port)
  • 5432 — PostgreSQL
  • 61613 — Artemis STOMP
  • 5672 — RabbitMQ AMQP
  • 6650 — Pulsar
  • 9000 / 9001 — MinIO API / console

All internal except the server port, which sits behind ingress.

Factflow deploys as a Docker container. Typical targets:

  • AWS — ECS/Fargate + RDS Postgres + S3 + managed message broker (or self-hosted Artemis on EC2)
  • Azure — Container Apps + Azure Database for PostgreSQL + Azure Blob Storage
  • On-prem — Kubernetes with self-managed Postgres + MinIO + Artemis

Chromium-dependent workflows (web_crawler adapter) require the unclecode/crawl4ai base image rather than the slim Python base.