TL;DR: What I need isn’t another orchestration tool, but a Control Plane—a coordination layer that answers in one place: which workflow, which version, which run, which step failed, how many retries, when’s the next run. cron only knows how to “knock on time” and then walks away. After surveying 11 platforms, my pick: Windmill first choice, Kestra co-selected; n8n/Temporal/Argo explicitly rejected; Hermes demoted to “scheduled agent” instead of scheduler.

Background: What My Automation Stack Looks Like
My automation system is a classic “script dump”: 17 cron jobs, 13 systemd timers, 17 Docker containers, Python/Shell scripts scattered across six or seven directories, plus one AI content pipeline (RSS → fetch → LLM judgment → rewrite → publish → Telegram notification). It runs, but has a bunch of structural problems:
- Workflows are black boxes — cron fires the script, but nobody knows what happens inside
- Workflow scripts and monitoring scripts are two separate systems, each logging independently
- Once the pipeline broke and three days later I deduced it from “why hasn’t the WeChat account updated”
- The same workflow had naming like
workflow_final / workflow_final2 / workflow_new2— impossible to say which version was actually running in production - Agent workflows are especially hard to trace — steps happen inside LLM tokens
I categorize these pain points into four orthogonal problems: missing execution semantics (no concept of run/task/status), missing signals (no “fail → alert”), distorted world state (versions decoupled from deployments), and the Agent black box.
Layer First, Then Evaluate
For this survey I held one principle: don’t jam all these platforms into one ranking — they simply aren’t on the same layer.
| Layer | Definition | Platforms |
|---|---|---|
| Automation / Integration | Event-driven connector orchestration | n8n |
| Workflow Orchestrator (Control Plane) | Scheduling + orchestration + DAG + run status + versioning + audit | Kestra, Windmill, Prefect, Airflow |
| Data Orchestrator | Data-asset / lineage-centric | Dagster |
| Durable Execution | State persistence, precise recovery | Temporal, Hatchet, Inngest |
| Agent Runtime | LLM sessions / tool calls | Hermes, Claude Agent SDK |
I want the second layer — the control plane. Once layered, many “should I adopt it” questions resolve themselves: n8n isn’t bad, it’s just not on the layer I need.
Methodology: All Facts Verified Live on the Web
Conclusions need to survive复核, so this time I didn’t rely on impressions:
- One research agent per platform, checking each against the latest GitHub releases, official docs, and architecture docs. All version numbers locked to 2026-08-22
- Top 6 got adversarial review agents specifically tasked with finding errors. We actually found one: an error in Temporal’s latest release date — the kind of坑 that impressions can’t surface
- Verification is recursive: the first round flagged “Prefect stores secrets in plaintext in the database”; the second round, cross-referencing the official v3 docs, found this contradicts the official “encrypted at rest” position — I corrected this in the article and adjusted Prefect’s score from 7.9 to 8.0 (ranking unchanged). If you can catch others’ mistakes, you must also catch your own
- After the article went live, a community member asked about “multi-step tasks,” so I did a second round: multi-step semantics for all six platforms were verified from official first-party sources (schema pages / OpenFlow spec / official repo docs source code), cross-referenced by four review agents, and I spot-checked key syntax line-by-line against the originals — this round I actually caught my own mistake: the
{{ outputDir }}syntax in my earlier POC is now marked deprecated in current docs (replaced byoutputFiles), and the script task image field changed fromdocker:tocontainerImage - I also did a read-only inventory of my own machine; the migration plan is based on real assets, not imagination
Latest stable versions confirmed: Kestra 1.3.30 (Apache-2.0), Windmill v1.795.0 (AGPL-3.0), Prefect 3.8.3 (Apache-2.0), n8n 2.35.7 (Sustainable Use License), Dagster 1.13.19, Temporal v1.31.2, Airflow 2.10.3, Argo v4.1.2, Hatchet v0.101.27, Inngest 1.43.0.
Finalists: Weighted Scoring & Corrections
I set weights based on my own needs: script reuse 20%, orchestration 15%, observability 15%, versioning 10%, agent workflows 10%, with the rest distributed. Weighted results (out of 10):
| Platform | Weighted Score | One-liner |
|---|---|---|
| Windmill | 8.2 | Scripts as first-class citizens, native Git Sync → first choice |
| Kestra | 7.7 | One YAML declares all execution semantics, Apache-2.0 → co-selected |
| Prefect | 8.0 | Best Python DX, versioning is a hidden weakness → 3rd |
| n8n | 8.3 | Highest raw score, but license + JSON workflows make git review a reverse process → not as control plane |
| Dagster | 8.2 | Strong, but the asset paradigm mismatches my content pipeline |
| Temporal | 7.9 | Retry semantics champion, at the cost of rewriting all scripts as activities |
| Airflow / Argo | 7.6 / 7.5 | Ops paradigm too heavy (Argo requires K8s) |
| Inngest | 8.0 | Adjusted after survey (scripts must wrap in step.run, 42 LOC + worker) |
| Hatchet | 7.6 | Adjusted after survey (scripts must wrap in SDK task, 8-12 LOC/task + persistent worker) |
Score interpretation leaves room for judgment: in the first draft, Inngest/Hatchet both scored 8.3+, but that was “without counting SDK wrapping cost.” I then ran a second adversarial review + migration cost measurement for both — Hatchet requires wrapping every shell script as an SDK task (measured 8–12 lines of glue + a persistent worker container, plus cron lacks timezone support, pausing, and drops scheduling on downtime), Inngest requires wrapping in a step.run handler (42 lines of glue + worker) — post-measurement corrections brought them to 7.6 and 8.0. More dramatically, the review also overturned one of my own first-round verification conclusions: Prefect’s secrets, per the official v3 docs, are “encrypted at rest” — my earlier “plaintext in database” claim contradicted this, so I corrected it and readjusted Prefect to 8.0 (ranking unchanged). Costs that weight tables can’t measure must be supplemented by real measurements; even verification itself must be re-verified — which precisely reinforces the value of Windmill/Kestra treating scripts as first-class citizens.

Why I Explicitly Rejected Certain Platforms
- n8n: The strongest drag-and-drop integration platform with 500+ connectors. But my git versioning requirement collides with its JSON workflow definitions — diffs and reviews are painful; the Sustainable Use License also makes long-term dependency uncomfortable. It’s an Automation layer, not a control plane.
- Temporal: The de facto standard for Durable Execution, unmatched for precise resumption of hour-long tasks. But my pipeline doesn’t have this hard requirement, and the cost (full SDK-ification + multi-service ops) is certain; another often-overlooked deduction: its Web UI only shows the scheduling event stream, app logs don’t flow in, and per-step logs require self-built export — see the multi-step section below. Only adopt it when a hard requirement emerges, and wrap just that one sub-flow.
- Argo: A very decent K8s workflow tool, but I live on a WSL2 single-machine + docker-compose setup. Putting K8s in for it would be putting the cart before the horse.
Hermes’ Proper Place: Agent, Not Captain
My Hermes (multi-model TG gateway) has built-in cron and a Web UI, but no workflow DAG, no versioned workflows, no run semantics — it’s naturally an Agent Runtime. The right posture: the control plane calls it as an “agent task” (HTTP/CLI/MCP), and scheduling, retries, timeouts, and versioning all stay in the control plane. That way, today it’s Hermes, tomorrow it’s Claude Code headless — the control plane changes one line.
Community Follow-up: What Do Multi-Step Tasks Actually Look Like?
After the article went live, a community member’s one sentence hit my weak spot: your examples only show “scheduled trigger → run a script → retry rules → failure rules,” with nothing like GitHub Actions’ “one job, multiple steps.” The responsibility is mine — I picked the shortest snippets to prove “low onboarding cost,” which buried the control plane’s core value. This section fills that gap and directly addresses the mental model they proposed: tasks should be defined step by step, and from the moment of trigger, every step has run logs that answer “what step are we on now, where did it fail, how did each step run?”
I’ll lead with the conclusion: this model isn’t an “extra requirement” — it’s the definition of a control plane. Kestra, Windmill (and Prefect) all natively satisfy it; what’s truly counterintuitive is that if “per-step logs out of the box” is a hard metric, Temporal — the retry champion — is the first to fall off. Details follow.
Five Questions, Answered in One Run View
Breaking the community member’s mental model into five questions, compared against the two finalists’ run views:
| Community Member’s Question | Kestra (Execution / TaskRun two-level view) | Windmill (Run view) |
|---|---|---|
| Did this trigger happen? | Schedule + Execution list: each trigger generates an execution, recording trigger method and time | Schedules + Runs list: past runs and next scheduled time |
| Did the task complete? | Execution status: SUCCESS / WARNING / FAILED | run status and total duration |
| What step are we on now? | Each TaskRun has its own status, updating in real time | Run tree marks each module in real time |
| Where did it fail? | Failed TaskRun highlighted separately; upstream success visible on same page | Failed module in red; upstream green, downstream gray |
| How did each step run? | Each TaskRun has its own logs page + attempt count + inputs/outputs preview | Each module has its own logs + retry count + result values |
All capabilities in the table are available in both platforms’ community editions (I verified each line against official docs) — not Enterprise-only.
Paired with a full view of one run (what you see when step 2 fails):

Of these five questions, cron can only answer “when’s the next trigger” — the other four are all black boxes. That’s the full meaning of “control plane.”
Multi-Step in the Wild: Same Pipeline, Two Flavors
Kestra — one YAML declares four steps, each with built-in retry and timeout, steps pass artifacts via outputFiles, and on retry exhaustion the errors branch pings a specific step for alerting:
| |
Windmill — existing scripts registered as resources with zero changes; the flow handles wiring; each module’s retry/timeout/data chaining are all module-level fields:
| |
Step shapes can get richer: parallel branching (branchall), loop processing (forloop), and “pause at a step waiting for human approval” (suspend, resume on reply event) — all module-level config, without changing the scripts themselves. Kestra has equivalents: Parallel tasks, and arbitrary scripts/HTTP tasks inside the errors branch.
“Define Tasks by Their Final Artifacts”: Right Direction, But See the Boundaries
The community member’s second-layer claim is “define tasks by their final artifacts.” Taking that literally, the closest platform is actually Dagster: it explicitly models each artifact as an asset, connects assets with dependency lines, and records every output per run. This is precisely why I originally categorized Dagster under “data orchestration” and didn’t choose it as a control plane: the asset modeling sweet spot is data tables / datasets, while half of my stack is side-effect steps like “push a TG message, restart a container, call a publish API” — they have no “artifact,” and forcing asset semantics is square peg, round hole.
“Artifacts” in a control plane exist in lightweight form and are already sufficient: Kestra produces typed outputs per task (previewable in the UI, referenceable downstream); Windmill produces a result per module (auto-persisted, referenced downstream via results.<id>). So the artifact path is natural: large artifacts (generated PPTs, MP4s, images) are uploaded by the publish step itself to object storage; the control plane handles “did it reach that step, what did that step produce, where did it go” — exactly the role of GitHub Actions’ upload-artifact. No need to bring in an asset lineage engine for that.
Migration Principle: Onboard First, Don’t Touch Scripts
Right now, not a single line of my scripts gets rewritten. Four phases: the platform was deployed on a single-machine docker-compose on 2026-08-23 (Windmill, localhost:8760); this week, the first real pipeline gets onboarded and runs for 7 days; weeks 2–4, migrate high-frequency crons (vendor_watch, node health checks), and with each migration, delete the corresponding cron line in the same commit; month 2, all 17 crons onboarded, secrets migrated from .env into the platform, monitoring scripts become flows with alerts; month 3, integrate Langfuse to surface LLM token spend. The full report (including all scoring rationale and verification corrections) lives in a private repo.
Ten Conclusions
- What’s missing is a control plane, not a tool;
- Windmill is first choice, Kestra is co-selected — either one works; readers wanting “multi-step + per-step independent logs/retries” get that by default from both, not as a bonus;
- Prefect only overtakes when 90% of workflows are Python;
- Reserve n8n for “third-party SaaS integration” scenarios, don’t use it now;
- Only adopt Temporal when you have a hard requirement for hour-long precise resumption (remember: no built-in log aggregation, per-step logs require self-built export);
- Hermes is the scheduled agent, not the scheduler;
- Langfuse phase 2 is needed (token/cost are control plane blind spots);
- Don’t install Grafana/Loki initially — run the platform’s native UI + Telegram alerts first;
- Keep cron only for system maintenance level, add zero new business crons;
- The platform was deployed on 2026-08-23 (single-machine compose, localhost:8760); this week there’s only one thing left: onboard the first real pipeline and wire up failure alerts.
Survey Limitations: Each platform’s score comes from a single research agent (eight passed adversarial review; Hatchet/Inngest had separate packaging cost measurements; Airflow/Argo did not pass, scores read with wider confidence intervals); multi-step semantics were supplemented in the 2026-08-23 second round (six-platform official docs verified + adversarial review, key syntax spot-checked line-by-line against official schema pages / repo originals); all versions and licenses are based on 2026-08-22 official GitHub/docs sources. References: Kestra Official Docs, Windmill Official Docs, Prefect v3 Docs, Temporal Releases, n8n Docs, Dagster Releases.
