New to Kestra?
Use blueprints to kickstart your first workflows.
Treat stale data as bad data with Soda and Kestra. A SodaCL freshness check warns at 6 hours, fails at 24, and alerts Slack when a table ages out.
A table full of last Tuesday's rows passes every row_count and missing_count check while being wrong for every decision made on it. This blueprint treats staleness as a first-class quality failure: io.kestra.plugin.soda.Scan runs a SodaCL freshness check on the newest created_at value with two thresholds. Past 6 hours the check warns and the task ends in a WARNING state, a visible early signal that data is aging; past 24 hours the check fails, the task fails, and the errors block tells Slack to stop trusting the table.
load_events (io.kestra.plugin.jdbc.duckdb.Queries) refreshes a small events table with current timestamps in the DuckDB file at warehouse_path. It exists so the blueprint runs anywhere and passes on first run; in production your streaming sink or batch load keeps the table fresh.freshness_scan (io.kestra.plugin.soda.Scan) receives the connection through the configuration map, which must define a data source named kestra, and the tiered check through the checks map: freshness(created_at) with warn: when > 6h and fail: when > 24h. The requirements list adds soda-core-duckdb.errors block. Staleness escalates the same way any other quality failure does.notify posts the verdict with the scalar hasWarnings and hasFailures flags from {{ outputs.freshness_scan.result }}, so the channel sees the warn tier while there is still time to fix the upstream load.Schedule trigger probes every 6 hours, matching the warn threshold so aging data is caught within one warn window.A freshness check is a measurement plus a policy: how often to probe, what a warning means, who hears about a breach. Kestra supplies the policy half. The Schedule trigger sets the probe cadence, warn and fail tiers map onto WARNING and FAILED execution states visible in every run, the errors block makes the breach loud, and the execution history becomes a freshness log for the table. The task ships the Soda container image, so nothing is installed on workers.
warehouse_path input) visible to the task containers. For production warehouses, swap the configuration map to Snowflake, BigQuery, or Postgres and add the matching soda-core-* package to requirements.created_at.SLACK_WEBHOOK_URL: Slack incoming webhook URL.SLACK_WEBHOOK_URL secret to your Kestra namespace.when > 0m and rerun to watch the staleness path exercise the errors block.freshness_probe trigger.configuration map at Snowflake, BigQuery, or Postgres with credentials from {{ secret('...') }}, and add soda-core-snowflake, soda-core-bigquery, or soda-core-postgres to requirements.checks for <table> blocks with per-table thresholds.