Start typing to search.

Core Concepts

Architecture

View Markdown

Understand Envoy's control plane, executors, subprocess task runner, streaming pipeline engine, and state stores.

Architecture

What Envoy is made of

Envoy is a single compiled binary that plays three roles depending on how it is started:

  • Control plane (envoy server) — serves the Vue web UI and the REST API, stores all state, schedules triggered runs, and dispatches task runs.
  • Executor (envoy executor) — registers with a control plane over HTTP and runs task subprocesses on its behalf. Optional: a control plane configured with executor.type: local runs tasks itself.
  • CLI (envoy <task>, envoy job run, …) — runs tasks and jobs directly from the terminal with no server at all.

All three paths execute tasks the same way, which is what makes tasks portable across environments.

flowchart LR UI[Web UI / REST API] --> CP[Control plane] CP -->|resolved task YAML| EX[Executor] EX -->|spawn per run| SP[Task subprocess] SP --> SRC[(Source systems)] SP --> DST[(Destination systems)] CP --> DB[(State store)]
Control plane, executor, and task subprocesses

One run, one subprocess

Every task run executes in its own spawned subprocess. Before dispatch, the control plane resolves everything the run needs into a single self-contained YAML: the task definition (snapshotted at run time), the connector configurations decrypted from the store, and any argument values. The subprocess receives that resolved YAML and nothing else — it has no access to the server's database or encryption key, and it does not know whether its configs came from the UI, a job binding, or a CLI file.

This isolation has practical consequences:

  • A crashing or leaking task cannot take the control plane down.
  • Runs are killable: stopping a run terminates its process.
  • Concurrency is bounded by configuration (runs.max_concurrent on the server, --max-concurrent on an executor).

The subprocess reports progress back through its stdout/stderr, which the server captures into the run's log store.

The streaming pipeline engine

Inside the subprocess, a task's steps become a Node.js Stream pipeline. Records flow through the steps one at a time with backpressure: a slow destination naturally throttles the source, so datasets never need to fit in memory. Steps can declare per-step concurrency for parallel processing, and batch/spool options for steps that need groups of records rather than single ones. See Run lifecycle for what happens around the pipeline, and Task anatomy for the YAML that defines it.

Control plane responsibilities

Beyond dispatching runs, the control plane owns:

  • State — tasks and their versions, job templates, jobs, runs, connectors, triggers, users, and sessions, in a store that is SQLite by default and can be MSSQL or PostgreSQL (store.type in envoy-server.yaml).
  • Secrets — connector configurations are encrypted at rest with the server's encryption_key; deployments can also reference secrets from Azure Key Vault. See RBAC, secrets, and hardening.
  • Scheduling — cron Triggers start job runs without a user in the loop.
  • Run logs and metrics — captured per run, stored in a configurable log store (file, database, or VictoriaLogs), and streamed live to the UI.

Executors and topology

The smallest deployment is one container running the control plane with executor.type: local. The default Docker Compose stack splits the roles: a control-plane container and an executor container that authenticate to each other with a shared executor key. Larger deployments add executors for capacity. The trade-offs and configuration are covered in Control plane and executors.

Where the layers live

The three-layer model (Tasks, jobs, and templates) maps onto this architecture cleanly: Tasks and Job Templates are portable YAML (from bundled files or the store), Jobs and their Connector bindings exist only in the control plane's state store, and every execution — however triggered — ends up as the same resolved YAML in a subprocess.