Deployment Recipes¶
This guide captures reference deployment flows for running Orcheo locally during development and hosting the service for teams. Each recipe lists the required environment variables, supporting services, and common verification steps.
Local Development (PostgreSQL)¶
This setup mirrors the default configuration that the tests exercise. It is ideal when you want to iterate on nodes, run the FastAPI server, and execute LangGraph workflows from the command line.
- Install dependencies
- Configure environment variables
- Multi-workspace is always on. Users must already belong to a workspace or create one through the self-service API after login.
- Keep
ORCHEO_WORKSPACE_BACKEND=postgresandORCHEO_POSTGRES_DSNpointed at a durable database so memberships and workspace metadata survive backend restarts. - Start the API server
- Run an example workflow
- Send a websocket message to
ws://localhost:2025/ws/workflow/<workflow_id>with a payload matching the schema intests/test_main.py.
Verification: Run uv run pytest to validate the environment. The test suite uses the same backend factories as the server.
Vault note: Set ORCHEO_VAULT_BACKEND=postgres and ORCHEO_VAULT_ENCRYPTION_KEY before starting the backend so credential encryption is configured from the first run.
Repository note: Local development uses the PostgreSQL workflow repository. Set ORCHEO_REPOSITORY_BACKEND=postgres and ORCHEO_POSTGRES_DSN so runs, triggers, and workflow state persist durably.
Workspace Bring-up¶
Workspace scoping is always on. Before exposing the API:
- Postgres workspace store
- Set
ORCHEO_WORKSPACE_BACKEND=postgresand provideORCHEO_POSTGRES_DSN. - Memberships
- Confirm every user has at least one workspace membership. Service tokens
and dev logins must carry
workspace_idsin their claims. - Verification
- Hit
/api/workspaces/meand confirm the Studio workspace badge to ensure the resolved workspace matches expectations.
Docker Compose (PostgreSQL, multi-container)¶
Use this recipe when you want an isolated environment that mimics production with a dedicated PostgreSQL database.
- Create
docker-compose.ymlservices: orcheo: build: . command: uvicorn orcheo_backend.app:app --host 0.0.0.0 --port 2025 environment: ORCHEO_HOST: 0.0.0.0 ORCHEO_PORT: "2025" ORCHEO_CHECKPOINT_BACKEND: postgres ORCHEO_GRAPH_STORE_BACKEND: postgres ORCHEO_REPOSITORY_BACKEND: postgres ORCHEO_WORKSPACE_BACKEND: postgres ORCHEO_CHATKIT_BACKEND: postgres ORCHEO_VAULT_BACKEND: postgres ORCHEO_VAULT_ENCRYPTION_KEY: change-me ORCHEO_POSTGRES_DSN: postgresql://orcheo:orcheo@postgres:5432/orcheo ports: - "2025:2025" depends_on: - postgres postgres: image: postgres:16 environment: POSTGRES_USER: orcheo POSTGRES_PASSWORD: orcheo POSTGRES_DB: orcheo ports: - "5432:5432" volumes: - postgres-data:/var/lib/postgresql/data volumes: postgres-data: - Build and start
- Connect
Access the API via
http://localhost:2025. The Postgres database is stored inside the named volume so runs persist across container restarts.
Verification: docker compose exec orcheo uv run pytest tests/test_main.py confirms the container is healthy.
Vault note: Rotate ORCHEO_VAULT_ENCRYPTION_KEY regularly and back up the Postgres volume alongside the database.
Reachable Self-Hosted Host (Bundled Caddy)¶
This is the standard public self-hosted recipe for Orcheo on a reachable Linux host. The bundled stack keeps backend, Studio, Postgres, Redis, worker, and beat on the Docker network while Caddy is the only service that needs public 80/443.
- Prepare the host
- Point your DNS hostname at the machine that will run Docker.
- Open inbound
80and443. - Install Docker and the Orcheo SDK.
- Install the stack with public ingress
- Understand the routing contract
https://orcheo.example.com/-> Studiohttps://orcheo.example.com/api/...-> backend HTTP routeswss://orcheo.example.com/ws/...-> backend WebSocket routes- Inspect the generated stack config when needed
COMPOSE_PROFILES=public-ingressenables Caddy TLS ingress. Backend and Studio remain accessible on their direct localhost ports (2025and2026by default).ORCHEO_CADDY_BACKEND_UPSTREAMScontrols the backend upstream pool for/api/*and/ws/*.- Verify the public origin
Replica Topology¶
The initial supported load-balancing topology is one logical deployment with multiple backend replicas that all share the same Postgres and Redis services. Caddy load-balances only replicas of that same deployment.
Set explicit backend upstreams in ~/.orcheo/stack/.env when you add more backend replicas:
Use this pattern only when the replicas share the same repository, checkpoint, ChatKit, and vault state through shared Postgres and Redis. Do not use one hostname and one path to multiplex isolated customer-specific stacks.
When To Put Something In Front Of Caddy¶
Bundled Caddy is appropriate for standard self-hosted installs and moderate scale. Prefer a cloud-managed load balancer, ingress controller, CDN, or WAF in front of Caddy, or instead of Caddy, when you need:
- higher-volume internet edge traffic
- managed certificates outside the host
- WAF, bot management, or DDoS shielding
- platform-native ingress on Kubernetes or managed container platforms
Source-Built Staging Host¶
Use this recipe when the staging machine deploys from a full git checkout and should stay close to the production stack without waiting for package or image releases.
- Pull the latest code on the staging host
- Configure stack environment values
make staging-*reuses~/.orcheo/stack/.envwhen it already exists.- On first run it creates
~/.orcheo/stack/.envfromdeploy/stack/.env.example, then preserves later edits while backfilling newly introduced keys. - Build the staging images from source
- Start the production-style staging stack
- Inspect or stop the stack when needed
The staging targets combine deploy/stack/docker-compose.yml with deploy/stack/docker-compose.staging.yml. This keeps the same runtime topology as production while swapping published images for local source builds:
- backend, worker, and beat are built from the monorepo checkout
- Studio is built from local
apps/studiosource and served by nginx - there are no source bind mounts, Vite dev server processes, or backend
--reloadflags
Cloudflare Tunnel Or Similar Split-Origin Tunnel¶
Use this recipe when the host is not directly reachable or when you intentionally keep Studio and backend on separate public hostnames behind a tunnel. In this topology, bundled Caddy stays off and the tunnel forwards to the direct localhost ports published by backend and Studio.
- Install the stack without bundled public ingress
- Point your tunnel routes at the direct localhost ports
https://orcheo.example.com->http://localhost:2025https://orcheo-studio.example.com->http://localhost:2026- Set the generated stack env to the split-origin contract
ORCHEO_PUBLIC_INGRESS_ENABLED=false ORCHEO_API_URL=https://orcheo.example.com VITE_ORCHEO_BACKEND_URL=https://orcheo.example.com ORCHEO_CORS_ALLOW_ORIGINS=https://orcheo-studio.example.com ORCHEO_CHATKIT_PUBLIC_BASE_URL=https://orcheo-studio.example.com VITE_ORCHEO_ALLOWED_HOSTS=localhost,127.0.0.1,orcheo-studio.example.com - Restart the stack after editing
~/.orcheo/stack/.env - Verify the public origins
The important distinction is that backend-facing values use the backend hostname, while browser-origin values use the Studio hostname. If these are collapsed back to localhost values, browsers will fail preflight requests and the backend will log OPTIONS ... 400.
Managed Hosting (PostgreSQL, async pool)¶
This deployment targets platforms such as Fly.io, Railway, or Kubernetes where Postgres is available as a managed service.
- Provision PostgreSQL
- Create a database and note the DSN, e.g.
postgresql://user:pass@host:5432/orcheo. - Ensure the
psycopg[binary,pool]andlanggraph[postgres]extras are installed (already defined inpyproject.toml). - Configure environment variables
export ORCHEO_CHECKPOINT_BACKEND=postgres export ORCHEO_POSTGRES_DSN=postgresql://user:pass@host:5432/orcheo export ORCHEO_REPOSITORY_BACKEND=postgres export ORCHEO_CHATKIT_BACKEND=postgres export ORCHEO_HOST=0.0.0.0 export ORCHEO_PORT=2025 export ORCHEO_VAULT_BACKEND=postgres export ORCHEO_VAULT_ENCRYPTION_KEY=change-me export ORCHEO_VAULT_TOKEN_TTL_SECONDS=900 - Deploy the application
- Docker image: Build with
docker build -t orcheo-app .and push to your registry. - Fly.io example:
- Ensure the container command starts uvicorn:
uvicorn orcheo_backend.app:app --host 0.0.0.0 --port ${PORT}. - Health checks
- Expose
/docsand/openapi.jsonfor HTTP checks. - Use
/ws/workflow/{workflow_id}for synthetic workflow runs during smoke tests.
Verification: Run uv run pytest tests/test_persistence.py locally with the ORCHEO_CHECKPOINT_BACKEND=postgres environment variable set and a reachable Postgres DSN to mirror production behavior.
Vault note: Managed environments should prefer KMS-integrated vaults. Configure IAM policies so only the Orcheo runtime can decrypt with the specified key.
Operational Tips¶
- Secrets: Prefer platform-specific secret managers (Fly Secrets, Railway variables, AWS Parameter Store) and never bake DSNs or vault encryption keys into images.
- Observability: Route application logs to structured logging (e.g., stdout + centralized collector) and enable tracing once Milestone 6 instrumentation lands.
- Scaling: The FastAPI app is stateless. Scale horizontally by adding replicas while pointing them at the same checkpoint database. With bundled Caddy, keep replica pools limited to one logical deployment that shares Postgres and Redis.
- Backups: Schedule database backups (pg_dump or managed snapshots) to protect workflow history and run states.
Use Cloudflare Tunnel when the host is not directly reachable from the internet, or when you intentionally want tunnel-managed public hostnames in front of the direct localhost ports. For reachable hosts with direct inbound ports and one shared origin, bundled Caddy is the simpler default.
These recipes will evolve as additional milestones introduce credential vaulting, trigger services, and observability pipelines.