Deploy Kestra — Self-Hosted Workflow Orchestration & Airflow Alternative
Self-host Kestra — YAML data pipelines & workflow orchestration
Just deployed
/var/lib/postgresql/data
Kestra
Just deployed
/app/storage
Deploy and Host Kestra on Railway
Kestra is an open-source workflow orchestration platform — think Airflow, but with declarative YAML instead of Python DAGs. Build and schedule data pipelines, ETL jobs, infrastructure automation, and event-driven workflows, running scripts, SQL, HTTP calls, and Python from a clean web UI with full execution history. This template deploys Kestra in standalone mode against a managed PostgreSQL database with the configuration and storage volume set up — so you have a production orchestrator ready to run your first flow in minutes.
What This Template Deploys
| Service | Purpose |
|---|---|
| Kestra | The orchestration engine, scheduler, worker, and web UI on port 8080 |
| PostgreSQL | Flow definitions, executions, logs, KV store, and the scheduler queue |
Both connect over Railway's private network. Kestra runs in standalone mode (executor, scheduler, worker, and indexer in one JVM), backed by PostgreSQL, with a persistent volume at /app/storage for execution artifacts.
About Hosting
Kestra is a powerful orchestrator, and its configuration model is unusual — understanding it is the key to a clean deploy, and this template sets it up for you.
Kestra's whole configuration is one YAML blob. Unlike most apps with flat environment variables, Kestra takes its entire configuration as a single YAML string in KESTRA_CONFIGURATION — the Postgres datasource, the queue type, the storage path, and basic auth all live inside that one variable as nested YAML. This is the thing that confuses first-time deployers. This template provides a correct KESTRA_CONFIGURATION wired to the managed Postgres, so the engine starts configured rather than erroring on a malformed config.
PostgreSQL is required — H2 loses everything. Kestra stores flow definitions, executions, logs, KV pairs, and the scheduler queue in PostgreSQL. It can run with an embedded H2 database for local dev, but H2 is in-memory and loses everything on container restart, which is useless on a platform like Railway. This template wires a managed Postgres as the backend, so your flows and history persist.
Standalone mode runs everything in one service. The template runs Kestra in standalone mode — executor, scheduler, worker, and indexer in a single JVM — the right shape for small-to-medium workloads. The JVM defaults to ~3.8 GB max heap on a 4 GB container; for hundreds of concurrent executions, raise the memory to 8 GB or split into separate webserver and worker services. Flows are defined in declarative YAML, so pipelines are version-controllable and readable.
Set basic auth, and artifacts persist. Basic authentication is configured in the KESTRA_CONFIGURATION YAML (a username that must be a valid email, and a password), so your orchestrator isn't open on the public internet — on first visit you set up your account. Files produced by task runs are stored on the volume at /app/storage, and flows, executions, and logs live in PostgreSQL — both survive redeploys, with Postgres the primary backup target. For object storage at scale, Kestra supports S3-compatible backends via configuration.
Typical cost: ~$10–20/month on Railway for Kestra and PostgreSQL, scaling with memory and execution volume. Kestra's open-source edition is Apache-2.0 and free.
How It Compares
| Kestra (self-hosted) | Airflow | Temporal | n8n | |
|---|---|---|---|---|
| Workflow definition | Declarative YAML | Python DAGs | Code (SDK) | Visual nodes |
| Setup complexity | Moderate | High | High | Low |
| Languages in tasks | Any (scripts, SQL, HTTP) | Python-centric | SDK languages | JS + nodes |
| UI + observability | Full | Full | Full | Full |
| Data ownership | Full — your infra | Full | Full | Full |
| Self-hostable | Yes | Yes | Yes | Yes |
Airflow is powerful but heavy and Python-DAG-centric. Temporal excels at durable microservice orchestration but needs code and a steeper setup. n8n is great for visual integration but less suited to serious data pipelines. Kestra's edge is declarative YAML orchestration — readable, version-controllable pipelines running any language's tasks — with a simpler deployment than Airflow or Temporal, self-hosted at flat cost with your workflows and data on infrastructure you own.
Deploy in Under 5 Minutes
- Click Deploy on Railway — Kestra and PostgreSQL build and wire together (~4 minutes)
- Confirm
KESTRA_CONFIGURATIONpoints at the managed Postgres and the volume is mounted - Open your Railway URL on port
8080and set up your account - Create a flow in YAML — a schedule, a script, an HTTP call, or a multi-step pipeline
- Run it and watch the execution, logs, and outputs in the web UI
For heavy concurrency, raise the memory or split into webserver and worker services.
Common Use Cases
- Data pipelines / ETL — schedule and orchestrate data movement across sources in YAML
- Infrastructure automation — provisioning, scaling, and maintenance workflows on a schedule
- Scheduled jobs — a robust, observable replacement for scattered cron jobs
- AI/ML orchestration — sequence model training, LLM calls, and review steps with observability
Configuration
| Variable | Required | Description |
|---|---|---|
KESTRA_CONFIGURATION | Required | The full Kestra config as YAML — datasource, queue, storage, auth |
DATABASE_URL / Postgres | Auto-injected | Managed PostgreSQL backend via Railway reference |
| Storage volume | Pre-set | Persistent volume at /app/storage for execution artifacts |
| JVM heap | Configurable | Raise plan memory (e.g. 8 GB) for high concurrency |
The whole config is one YAML blob.
KESTRA_CONFIGURATIONholds the Postgres datasource, queue type, storage path, and basic auth as nested YAML — this template provides a correct one wired to the managed Postgres, so Kestra starts configured.
Postgres is required and the JVM needs headroom. H2 loses data on restart, so Postgres is the backend. The standalone JVM defaults to ~3.8 GB heap on a 4 GB container — raise memory for heavy concurrency.
Dependencies for Kestra Hosting
- Railway account — ~$10–20/month for Kestra and PostgreSQL, scaling with memory
- PostgreSQL (included and wired as the backend)
- A persistent volume at
/app/storagefor execution artifacts (included) - Optional: an S3-compatible bucket for object storage at scale
Deployment Dependencies
- Kestra GitHub Repository
- Kestra Documentation
- Kestra Docker Compose Guide
- Railway Private Networking
Implementation Details
The template runs the official kestra/kestra:latest image with the server standalone command on port 8080, backed by a Railway-managed PostgreSQL database, with a persistent volume mounted at /app/storage for execution artifacts. Standalone mode runs the executor, scheduler, worker, and indexer together in one JVM, which suits small-to-medium workloads and keeps the deployment to a single service plus the database.
Kestra's configuration is supplied through the KESTRA_CONFIGURATION environment variable as a single YAML string — an unusual model where the Postgres datasource (jdbc:postgresql://...), the repository and queue types (both postgres), the storage path, and basic authentication all live as nested YAML inside one variable. The template provides a correct configuration wired to the managed database. PostgreSQL is required because it stores flow definitions, executions, logs, KV pairs, and the scheduler queue; the embedded H2 alternative is in-memory and loses all state on restart, making it unsuitable on a PaaS.
The standalone JVM defaults to roughly 3.8 GB max heap on a 4 GB container, so high-concurrency workloads benefit from more memory or from splitting into separate webserver and worker services. Basic auth is enabled in the configuration YAML. Execution artifacts persist on the volume and orchestration state in Postgres, the primary backup target; S3-compatible object storage is supported via configuration for larger deployments.
Frequently Asked Questions
Why is the config one big YAML variable? Kestra takes its entire configuration — datasource, queue, storage, and auth — as nested YAML inside the KESTRA_CONFIGURATION variable, rather than separate flat variables. This template provides a correct one wired to Postgres, so it starts configured out of the box.
Why does Kestra need PostgreSQL? It stores flows, executions, logs, KV pairs, and the scheduler queue in Postgres. The embedded H2 option is in-memory and loses everything on restart, so Postgres is required for a persistent deployment on Railway.
How is Kestra different from Airflow? Kestra uses declarative YAML instead of Python DAGs, so pipelines are readable and version-controllable without orchestration code, and its deployment is simpler. Tasks can run any language — scripts, SQL, HTTP, Python.
Can I run Python and scripts? Yes — Kestra tasks can run scripts, SQL queries, HTTP requests, and Python (and more via plugins), all orchestrated from declarative YAML flows with full logging.
Why Deploy Kestra on Railway?
Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.
By deploying Kestra on Railway you get a workflow orchestrator with the hard parts solved — a correct configuration wired to managed PostgreSQL, a persistent storage volume, standalone mode, and automatic HTTPS. Build data pipelines and automations in declarative YAML, self-hosted on infrastructure you own.
Template Content
Kestra
Shinyduo/kestra-railwayKESTRA_ADMIN_PASSWORD
Admin password — 8+ chars, uppercase + number required (regex: ^(?=.[A-Z])(?=.[0-9]).{8,}$)
KESTRA_ADMIN_USERNAME
Admin email — used to log in to Kestra UI