Skip to content

Dagster+ Hybrid Architecture (evaluated, not adopted)

What Would Have Changed: Self-Hosted β†’ Dagster+ Hybrid

Section titled β€œWhat Would Have Changed: Self-Hosted β†’ Dagster+ Hybrid”
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Your Infrastructure β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ dagster-web │───────▢│ dagster-code-server β”‚ β”‚
β”‚ β”‚ (Port 3000) β”‚ gRPC β”‚ (Port 4000) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ Runs CONTINUOUSLY β”‚ β”‚
β”‚ β”‚ Serves code via β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ gRPC β”‚ β”‚
β”‚ β”‚dagster-daemonβ”‚β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β”‚ (Scheduler) β”‚ β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ apps/dagster/ β”‚ β”‚
β”‚ β”‚ (Your code) β”‚ β”‚
β”‚ β”‚ Loaded by code β”‚ β”‚
β”‚ β”‚ server β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Components:

  • dagster-code-server: Runs 24/7, serves your code via gRPC
  • dagster-web: Runs 24/7, connects to code server, serves UI
  • dagster-daemon: Runs 24/7, connects to code server, runs schedules
  • apps/dagster/: Your pipeline code, loaded by code server

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Dagster+ Cloud β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Webserver β”‚ β”‚ Daemon β”‚ β”‚ Metadata β”‚ β”‚
β”‚ β”‚ (UI) β”‚ β”‚ (Scheduler) β”‚ β”‚ Storage β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β–² β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ gRPC/HTTPS
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Your Infrastructure β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ dagster-agent β”‚ β”‚
β”‚ β”‚ Runs CONTINUOUSLY β”‚ β”‚
β”‚ β”‚ Connects to Dagster+ β”‚ β”‚
β”‚ β”‚ Waits for job requests β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚
β”‚ β”‚ When job runs: β”‚
β”‚ β–Ό β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ User Code Container (ON-DEMAND) β”‚ β”‚
β”‚ β”‚ - Pulls image: ghcr.io/.../dagster:tag β”‚ β”‚
β”‚ β”‚ - Contains: apps/dagster/ (built in) β”‚ β”‚
β”‚ β”‚ - Runs job β”‚ β”‚
β”‚ β”‚ - Stops when done β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Another Job Container (if concurrent) β”‚ β”‚
β”‚ β”‚ (Same image, separate container) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Components:

  • dagster-agent: Runs 24/7, connects to Dagster+ cloud, launches containers
  • User Code Containers: Launched ON-DEMAND when jobs run, stop when done
  • apps/dagster/: Still your code, but now built into Docker image

AspectSelf-HostedDagster+ Hybrid
Code Serverβœ… Runs 24/7 (port 4000)❌ Not needed
Webserverβœ… Runs 24/7 (port 3000)❌ Dagster+ manages
Daemonβœ… Runs 24/7❌ Dagster+ manages
Agent❌ Not neededβœ… Runs 24/7
Code LocationLoaded by code serverBuilt into Docker image
Job ExecutionDaemon spawns processesAgent launches containers
Metadata StorageYour PostgreSQLDagster+ manages

apps/dagster/ is still critical! It contains:

  1. Pipeline Definitions (erp_pipeline/definitions.py)

    • Your jobs, assets, sensors, schedules
    • What gets executed
  2. DLT Pipelines (erp_pipeline/dlt/)

    • Data extraction logic
    • Transformers
  3. dbt Models (dbt/)

    • SQL transformations
  4. Configuration (dagster_cloud.yaml)

    • Tells Dagster+ where to find your code

The difference: Instead of being loaded by a code server, it’s now:

  1. Built into a Docker image during CI/CD
  2. Pushed to GHCR (ghcr.io/ERP-Unlocked/ordermatic/dagster:tag)
  3. Pulled by agent when jobs run
  4. Executed in containers that start/stop per job

Terminal window
# You edit apps/dagster/erp_pipeline/definitions.py
git push
Terminal window
# GitHub Actions builds Docker image
docker build -t ghcr.io/.../dagster:v1.2.3 apps/dagster/
docker push ghcr.io/.../dagster:v1.2.3
Terminal window
# GitHub Actions tells Dagster+ about new image
dagster-cloud serverless deploy-python-executable \
--image ghcr.io/.../dagster:v1.2.3
1. Dagster+ schedules a job (via daemon)
2. Dagster+ tells agent: "Run job X"
3. Agent pulls image: ghcr.io/.../dagster:v1.2.3
4. Agent launches container with your code
5. Container runs job (executes apps/dagster/ code)
6. Container stops when done

  • apps/dagster/ - Your pipeline code (still critical!)
  • Dockerfile - Builds your code into image
  • CI/CD - Builds and pushes images
  • Environment variables - Passed to containers
  • dagster-code-server - Agent launches containers instead
  • dagster-web - Dagster+ provides UI
  • dagster-daemon - Dagster+ manages scheduling
  • Dagster PostgreSQL - Dagster+ manages metadata
  • workspace.yaml - Not used (code is in image)

  1. Simpler Infrastructure: One agent vs three services
  2. Better Isolation: Each job runs in its own container
  3. Automatic Scaling: Containers start/stop as needed
  4. No Code Server Crashes: Code issues don’t affect agent
  5. Managed UI/Daemon: Dagster+ handles reliability

apps/dagster/ is still your code - it just gets packaged differently:

  • Before: Code server loads it from filesystem
  • After: Built into Docker image, launched in containers

No code server needed because:

  • Agent pulls pre-built images
  • Launches containers on-demand
  • Containers contain your code already

You’re essentially moving from β€œload code at runtime” to β€œpackage code at build time” - much more reliable!