Skip to content

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.

  1. Install dependencies
    uv sync --all-groups
    
  2. Configure environment variables
    cp .env.example .env
    
  3. Multi-workspace is always on. Users must already belong to a workspace or create one through the self-service API after login.
  4. Keep ORCHEO_WORKSPACE_BACKEND=postgres and ORCHEO_POSTGRES_DSN pointed at a durable database so memberships and workspace metadata survive backend restarts.
  5. Start the API server
    make dev-server
    
  6. Run an example workflow
  7. Send a websocket message to ws://localhost:2025/ws/workflow/<workflow_id> with a payload matching the schema in tests/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:

  1. Postgres workspace store
  2. Set ORCHEO_WORKSPACE_BACKEND=postgres and provide ORCHEO_POSTGRES_DSN.
  3. Memberships
  4. Confirm every user has at least one workspace membership. Service tokens and dev logins must carry workspace_ids in their claims.
  5. Verification
  6. Hit /api/workspaces/me and 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.

  1. Create docker-compose.yml
    services:
      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:
    
  2. Build and start
    docker compose up --build
    
  3. 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.

  1. Prepare the host
  2. Point your DNS hostname at the machine that will run Docker.
  3. Open inbound 80 and 443.
  4. Install Docker and the Orcheo SDK.
  5. Install the stack with public ingress
    orcheo install --public-ingress --public-host orcheo.example.com --start-stack
    
  6. Understand the routing contract
  7. https://orcheo.example.com/ -> Studio
  8. https://orcheo.example.com/api/... -> backend HTTP routes
  9. wss://orcheo.example.com/ws/... -> backend WebSocket routes
  10. Inspect the generated stack config when needed
  11. COMPOSE_PROFILES=public-ingress enables Caddy TLS ingress. Backend and Studio remain accessible on their direct localhost ports (2025 and 2026 by default).
  12. ORCHEO_CADDY_BACKEND_UPSTREAMS controls the backend upstream pool for /api/* and /ws/*.
  13. Verify the public origin
    curl -I https://orcheo.example.com/
    curl https://orcheo.example.com/api/system/info
    

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:

ORCHEO_CADDY_BACKEND_UPSTREAMS=backend:2025 backend-2:2025 backend-3:2025

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.

  1. Pull the latest code on the staging host
    git pull
    
  2. Configure stack environment values
  3. make staging-* reuses ~/.orcheo/stack/.env when it already exists.
  4. On first run it creates ~/.orcheo/stack/.env from deploy/stack/.env.example, then preserves later edits while backfilling newly introduced keys.
  5. Build the staging images from source
    make staging-build
    
  6. Start the production-style staging stack
    make staging-up
    
  7. Inspect or stop the stack when needed
    make staging-config
    make staging-logs
    make staging-down
    

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/studio source and served by nginx
  • there are no source bind mounts, Vite dev server processes, or backend --reload flags

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.

  1. Install the stack without bundled public ingress
    orcheo install --start-stack
    
  2. Point your tunnel routes at the direct localhost ports
  3. https://orcheo.example.com -> http://localhost:2025
  4. https://orcheo-studio.example.com -> http://localhost:2026
  5. 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
    
  6. Restart the stack after editing ~/.orcheo/stack/.env
    orcheo stack --stop
    orcheo stack --start
    
  7. Verify the public origins
    curl -I https://orcheo-studio.example.com/
    curl https://orcheo.example.com/api/system/info
    

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.

  1. Provision PostgreSQL
  2. Create a database and note the DSN, e.g. postgresql://user:pass@host:5432/orcheo.
  3. Ensure the psycopg[binary,pool] and langgraph[postgres] extras are installed (already defined in pyproject.toml).
  4. 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
    
  5. Deploy the application
  6. Docker image: Build with docker build -t orcheo-app . and push to your registry.
  7. Fly.io example:
    fly launch --no-deploy
    fly secrets set ORCHEO_POSTGRES_DSN=...
    fly deploy
    
  8. Ensure the container command starts uvicorn: uvicorn orcheo_backend.app:app --host 0.0.0.0 --port ${PORT}.
  9. Health checks
  10. Expose /docs and /openapi.json for HTTP checks.
  11. 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.