New to Kestra?
Use blueprints to kickstart your first workflows.
Orchestrate a GCS to BigQuery pipeline with Kestra. Download an object, clean it with Python and pandas, load the curated result, and notify Discord.
This blueprint implements the classic download, transform, load pattern with Google Cloud Storage as the source and BigQuery as the destination. Raw files land in a GCS bucket, but they rarely arrive analytics ready: column names are inconsistent, rows are empty, and duplicates creep in. This flow closes that gap on a schedule by pulling a raw object from GCS, cleaning it in a Python container with pandas, and loading the curated output into a clean BigQuery table, with a Discord notification to confirm each run. It is a reusable GCS to BigQuery ETL pipeline you can point at any CSV prefix.
daily io.kestra.plugin.core.trigger.Schedule trigger fires on a cron cadence (0 6 * * *, shipped disabled) so the curation runs unattended.download task (io.kestra.plugin.gcp.gcs.Download) pulls the object at gs://<bucket>/{{ inputs.source_object }} into Kestra internal storage.transform task (io.kestra.plugin.scripts.python.Script) runs in a Docker container (ghcr.io/kestra-io/pydata:latest), reads the file with pandas, normalizes column names, drops empty rows, de-duplicates, and emits curated.csv.load_curated task (io.kestra.plugin.gcp.bigquery.Load) loads the curated CSV into analytics.customers_clean with autodetect, createDisposition: CREATE_IF_NEEDED, and writeDisposition: WRITE_TRUNCATE.notify task (io.kestra.plugin.discord.DiscordIncomingWebhook) confirms the load, and an alert_on_failure error handler posts to Discord if any step fails.GCS and BigQuery each have their own scheduling primitives, but neither coordinates a download, a Python transform, and a load as one observable unit. Kestra ties them together with event or schedule triggers, automatic retries, execution level lineage across tasks, and declarative YAML you can version control. The container based transform keeps business logic in Python while the pipeline stays reproducible, and the error handler guarantees you hear about failures, something a storage lifecycle rule or a scheduled query alone cannot deliver.
A Google Cloud project with a GCS bucket and a BigQuery analytics dataset, plus a Discord incoming webhook. A raw object must exist at the source_object path (default raw/customers.csv).
GCP_PROJECT_ID: GCP project hosting the bucket and dataset.GCP_SERVICE_ACCOUNT: service account key JSON with GCS read and BigQuery job access.GCS_BUCKET: bucket holding the raw source files.DISCORD_WEBHOOK_URL: Discord incoming webhook URL.source_object and GCS_BUCKET at your file and confirm the analytics dataset exists.daily schedule (or change the cron) to run it unattended.Download for Downloads to bulk-pull a whole prefix, then loop the transform over each file.writeDisposition to WRITE_APPEND for incremental accumulation instead of full refresh.