New to Kestra?
Use blueprints to kickstart your first workflows.
Run a Python Apache Beam pipeline on a distributed Flink portable runner with Kestra. Event-driven, with pre-submit checks, retries, and Slack alerts.
Run a Python Apache Beam pipeline on a distributed Apache Flink cluster using the Beam portable runner, orchestrated end to end by Kestra. A JobServer bridges the serialized Python pipeline to the Flink JobManager and TaskManager, and the worker harness executes the Python code. The flow is chained off an upstream ingestion run with a Flow trigger and aggregates the data that ingestion produced (revenue per product category with a real GroupBy shuffle), so the distributed runner is justified by real work rather than a toy transform. It solves the gap that Beam and Flink themselves do not solve: event-driven submission, pre-submit reachability checks, retries, Slack alerting, and capture of output shards.
io.kestra.plugin.core.trigger.Flow trigger after_ingestion fires after the upstream flow ingest_raw_events in namespace company.team finishes with state SUCCESS.download_source (io.kestra.plugin.core.http.Download) pulls source_uri into Kestra internal storage as orders.csv with retry and a PT5M timeout.check_jobserver (io.kestra.plugin.core.http.Request) hits {{ inputs.flink_rest_url }}/overview to confirm the cluster is reachable before submitting, so an unreachable JobServer fails fast.write_pipeline (io.kestra.plugin.core.storage.Write) renders a Beam YAML pipeline that reads the CSV, sums revenue per product category with a Combine group-by (a real shuffle), and writes the result to CSV.run_on_flink (io.kestra.plugin.beam.RunPipeline) submits the pipeline with sdk: PYTHON and beamRunner: FLINK, targeting the JobServer at options.job_endpoint and pointing at the Flink REST API via runnerConfig.flinkRestUrl. Output is captured through outputFiles: ["revenue_by_category*"].log_result (io.kestra.plugin.core.log.Log) surfaces the Flink dashboard URL and the captured output shards.alert_failure (io.kestra.plugin.slack.notifications.SlackIncomingWebhook) posts a Slack alert with the execution id and Flink URL.revenue_by_category*) materialized as Kestra outputs.Beam's portable runner and Flink's REST API give you a distributed execution engine, not a scheduler. They have no concept of "run after upstream ingestion succeeds", no pre-submit health check, no retry-with-backoff on a flaky JobServer, no Slack alert on failure, and no native way to capture and pass output shards to a downstream flow. Kestra fills that gap: the Flow trigger reacts to upstream state, task-level retry and timeout handle transient JobServer or network issues, the errors block guarantees alerts are never silent, and the whole pipeline is declarative YAML with full execution lineage. You keep Beam and Flink for what they are good at (distributed compute) and let Kestra own scheduling, observability, and recovery.
A running Flink cluster and Beam JobServer reachable from the Kestra worker. This cannot be sandboxed, hence demo: false.
flink:1.18 images) with the REST API exposed (default :8082).apache/beam_flink1.18_job_server:2.71.0) exposing the job endpoint on :8099.environment_type: LOOPBACK, the lowest-privilege worker harness: no Docker socket, no privileged: true, no host networking. The DOCKER alternative requires mounting /var/run/docker.sock with privileged: true and host networking, which is container-escape-equivalent, so run it only on isolated, dedicated workers. EXTERNAL connects to a pre-provisioned worker pool and avoids granting any Docker access.job_endpoint, Container C (Flink TaskManager) executes, Container D (Worker) runs the Python SDK harness (the Kestra task process itself under LOOPBACK).SLACK_WEBHOOK: used only by the alert_failure error handler to post failure notifications. No cluster credentials are needed for this reference flow because the JobServer and Flink cluster are reached over the local network. If your cluster sits behind authentication, store the token as a secret and inject it with {{ secret('NAME') }} rather than hardcoding it.job_endpoint and flink_rest_url.SLACK_WEBHOOK secret for failure alerts.ingest_raw_events flow, or run it manually with the default public source_uri.ingest_raw_events. On success the after_ingestion trigger launches this pipeline.log_result) and the run_on_flink task logs.source_uri at the real sink your ingestion writes, and bring your own ingest_raw_events flow.environment_type to DOCKER or EXTERNAL for production worker pools (DOCKER needs a mounted Docker socket plus privileged: true and host networking, so run it only on isolated workers).parallelism to match your TaskManager slots, and version-match the SDK and JobServer images (for example apache/beam_flink1.18_job_server:2.71.0 with apache/beam_python3.11_sdk:2.71.0).Combine group-by with your own Beam YAML transforms, or write results to a warehouse instead of CSV.outputs.run_on_flink.outputFiles from a Subflow trigger.