Argo Workflows for Kubernetes Batch Processing
Argo Workflows Kubernetes batch processing provides a cloud-native pipeline orchestration engine that runs directly on your Kubernetes cluster. Unlike external orchestration tools such as Airflow, which manage Kubernetes jobs remotely, Argo Workflows is a Kubernetes-native CRD (Custom Resource Definition) that defines workflows as first-class Kubernetes resources. As a result, you inherit the full power of Kubernetes scheduling, resource management, and observability without bolting on a separate execution layer.
This guide covers building production batch processing pipelines, from simple sequential steps to complex DAG-based workflows with conditional execution, parameter passing, and artifact management. Moreover, you will learn retry strategies, resource optimization, and integration patterns with data warehouses and ML training pipelines. Throughout, the emphasis stays on patterns that hold up in production rather than toy examples.
Why Argo Workflows Over Airflow
Apache Airflow is the industry standard for workflow orchestration, but it was designed before Kubernetes became the dominant deployment platform. Consequently, Airflow requires its own infrastructure — a web server, scheduler, metadata database, and a pool of workers. Additionally, Airflow DAGs are defined in Python and stored on a shared filesystem, which creates real deployment and versioning friction as teams scale.
Argo Workflows, by contrast, runs as a lightweight controller on Kubernetes. Workflows are YAML manifests versioned in Git alongside your application code. Each step runs in its own container with its own resource limits, and Kubernetes handles scheduling, scaling, and failure recovery. Furthermore, Argo integrates natively with Kubernetes RBAC, service accounts, and secrets, so security and access control follow the same rules as the rest of your cluster.
Installing Argo Workflows
# Install Argo Workflows
kubectl create namespace argo
kubectl apply -n argo -f https://github.com/argoproj/argo-workflows/releases/download/v3.6.0/install.yaml
# Install Argo CLI
curl -sLO https://github.com/argoproj/argo-workflows/releases/download/v3.6.0/argo-linux-amd64.gz
gunzip argo-linux-amd64.gz
chmod +x argo-linux-amd64
sudo mv argo-linux-amd64 /usr/local/bin/argo
# Verify installation
argo version
Argo Workflows Kubernetes: Building a Data Pipeline
# data-pipeline.yaml — ETL workflow with DAG
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: data-pipeline-
namespace: data-processing
spec:
entrypoint: etl-pipeline
arguments:
parameters:
- name: date
value: "2026-03-23"
- name: source-bucket
value: "s3://raw-data"
- name: dest-bucket
value: "s3://processed-data"
# Artifact repository configuration
artifactRepositoryRef:
configMap: artifact-repositories
key: default-v1
# Resource limits for the entire workflow
podGC:
strategy: OnWorkflowCompletion
deleteDelayDuration: 600s
templates:
- name: etl-pipeline
dag:
tasks:
# Extract from multiple sources in parallel
- name: extract-orders
template: extract
arguments:
parameters:
- name: source
value: "orders"
- name: extract-customers
template: extract
arguments:
parameters:
- name: source
value: "customers"
- name: extract-products
template: extract
arguments:
parameters:
- name: source
value: "products"
# Transform — depends on all extractions completing
- name: transform
template: transform-data
dependencies: [extract-orders, extract-customers, extract-products]
arguments:
artifacts:
- name: orders-data
from: "{{tasks.extract-orders.outputs.artifacts.extracted-data}}"
- name: customers-data
from: "{{tasks.extract-customers.outputs.artifacts.extracted-data}}"
- name: products-data
from: "{{tasks.extract-products.outputs.artifacts.extracted-data}}"
# Validate transformed data
- name: validate
template: validate-data
dependencies: [transform]
# Load — only if validation passes
- name: load
template: load-data
dependencies: [validate]
when: "{{tasks.validate.outputs.parameters.validation-status}} == passed"
# Notify on completion or failure
- name: notify-success
template: notify
dependencies: [load]
arguments:
parameters:
- name: status
value: "Pipeline completed successfully"
- name: notify-failure
template: notify
dependencies: [validate]
when: "{{tasks.validate.outputs.parameters.validation-status}} != passed"
arguments:
parameters:
- name: status
value: "Pipeline failed: validation errors"
# Extract template — reusable for each source
- name: extract
inputs:
parameters:
- name: source
outputs:
artifacts:
- name: extracted-data
path: /tmp/output/
container:
image: myregistry/data-extractor:latest
command: [python, extract.py]
args: ["--source", "{{inputs.parameters.source}}",
"--date", "{{workflow.parameters.date}}",
"--output", "/tmp/output/"]
resources:
requests:
memory: "2Gi"
cpu: "1"
limits:
memory: "4Gi"
cpu: "2"
retryStrategy:
limit: 3
retryPolicy: Always
backoff:
duration: "30s"
factor: 2
maxDuration: "5m"
# Transform template
- name: transform-data
inputs:
artifacts:
- name: orders-data
path: /tmp/input/orders/
- name: customers-data
path: /tmp/input/customers/
- name: products-data
path: /tmp/input/products/
outputs:
artifacts:
- name: transformed-data
path: /tmp/output/
container:
image: myregistry/data-transformer:latest
command: [python, transform.py]
args: ["--input", "/tmp/input/", "--output", "/tmp/output/"]
resources:
requests:
memory: "8Gi"
cpu: "4"
limits:
memory: "16Gi"
cpu: "8"
# Validation template
- name: validate-data
outputs:
parameters:
- name: validation-status
valueFrom:
path: /tmp/validation-result.txt
container:
image: myregistry/data-validator:latest
command: [python, validate.py]
# Load template
- name: load-data
container:
image: myregistry/data-loader:latest
command: [python, load.py]
args: ["--dest", "{{workflow.parameters.dest-bucket}}"]
# Notification template
- name: notify
inputs:
parameters:
- name: status
container:
image: curlimages/curl:latest
command: [sh, -c]
args:
- |
curl -X POST https://hooks.slack.com/services/T.../B.../xxx \
-H 'Content-Type: application/json' \
-d '{"text": "Data Pipeline: {{inputs.parameters.status}}"}'
Understanding the DAG: Parallelism, Dependencies, and Conditionals
The power of the example above lies in how the DAG expresses execution order declaratively. The three extract tasks list no dependencies, so Argo schedules them concurrently — Kubernetes spins up three pods at once, bounded only by cluster capacity. The transform task, however, declares dependencies: [extract-orders, extract-customers, extract-products], so it waits until all three artifacts exist before starting. This fan-out then fan-in shape is the bread and butter of batch ETL.
Just as importantly, the when expressions turn the DAG into a branching pipeline. The load step runs only when validation reports passed, while notify-failure fires on the opposite condition. Because both notification branches depend on earlier steps, exactly one of them executes per run. In production teams typically extend this with a dedicated cleanup or alerting branch so that no failure passes silently.
Artifacts and Parameters: Passing Data Between Steps
Argo distinguishes two ways to move information between steps, and choosing correctly matters for performance. Parameters are small strings — a status flag, a row count, a date — passed inline through the workflow’s variable system. Artifacts are files or directories — extracted datasets, model checkpoints, reports — stored in an external repository such as S3, GCS, or MinIO, and then mounted into the next pod. The extract template above outputs the /tmp/output/ directory as the extracted-data artifact; the transform template mounts those same artifacts at predictable input paths.
A common pitfall is trying to shuttle large datasets through parameters. Parameters are capped at a modest size and are not meant for bulk data, so anything beyond a few kilobytes belongs in an artifact. Configuring the artifact repository once via artifactRepositoryRef, as shown, keeps every template free of repetitive bucket boilerplate.
CronWorkflows for Scheduled Batch Jobs
Therefore, recurring batch jobs use CronWorkflows, the Kubernetes-native equivalent of cron jobs but with Argo’s full workflow capabilities including retries, DAGs, and artifact management. Crucially, a CronWorkflow wraps the exact same workflowSpec you already tested manually, so promoting a one-off pipeline to a scheduled one requires no rewrite.
# cron-workflow.yaml — Daily data pipeline
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
name: daily-data-pipeline
namespace: data-processing
spec:
schedule: "0 2 * * *" # 2 AM daily
timezone: "UTC"
concurrencyPolicy: Replace
startingDeadlineSeconds: 300
successfulJobsHistoryLimit: 7
failedJobsHistoryLimit: 3
workflowSpec:
entrypoint: etl-pipeline
arguments:
parameters:
- name: date
value: "{{workflow.scheduledTime.Format "2006-01-02"}}"
# ... same templates as above
Two fields deserve attention. The concurrencyPolicy: Replace setting prevents overlapping runs — if a pipeline is still going when the next schedule fires, Argo cancels the old run rather than stacking two heavy jobs onto the cluster. Meanwhile, startingDeadlineSeconds guards against missed schedules during controller downtime, so a brief outage does not silently skip a day’s batch. The history limits keep the cluster tidy by garbage-collecting old workflow records.
Retry Strategies and Resource Tuning
Transient failures are inevitable in distributed batch processing — a flaky network, a throttled API, a preempted spot node. The extract template demonstrates the recommended response: an exponential backoff that retries up to three times, doubling the wait each attempt up to a five-minute ceiling. This protects downstream steps from a single hiccup without hammering an upstream service. For idempotent steps, retryPolicy: Always is safe; for steps with side effects, the docs recommend OnError or OnTransientError so you never re-run a partial write blindly.
Resource requests and limits are equally consequential. The transform step requests 8Gi and 4 CPUs but may burst to 16Gi and 8 CPUs, which lets the Kubernetes scheduler bin-pack efficiently while still capping a runaway job. A common pattern is to right-size requests from observed historical usage and keep limits generous enough to absorb seasonal data spikes. Pairing Argo with a node autoscaler means the cluster can grow to meet a large nightly batch and shrink afterward, controlling cost.
When NOT to Use Argo Workflows
Argo Workflows requires genuine Kubernetes expertise. If your team does not already run Kubernetes in production, the learning curve is steep, and a managed Airflow service or even plain cron jobs may deliver value faster. Consequently, the YAML-based definition can also become verbose for branching logic that would read more naturally as a few lines of Python; large workflows sometimes benefit from generating the manifests programmatically rather than hand-editing them.
For real-time stream processing, Argo is simply the wrong tool — reach for Apache Flink, Kafka Streams, or a comparable streaming framework instead. Argo is built for finite batch jobs and pipeline orchestration, not continuous, unbounded data. Likewise, for sub-second, latency-sensitive request handling, a long-running service is the right design, not a per-event workflow. Knowing these boundaries prevents teams from stretching Argo into roles it was never meant to fill.
Key Takeaways
- Model pipelines as DAGs so independent steps run in parallel and dependent steps wait automatically
- Pass small values as parameters and large datasets as artifacts backed by object storage
- Use exponential backoff retries, matching
retryPolicyto whether a step is idempotent - Promote tested workflows to CronWorkflows with
concurrencyPolicy: Replaceto avoid overlap - Reach for streaming engines, not Argo, when data is continuous and latency-sensitive
Argo Workflows Kubernetes batch processing brings pipeline orchestration natively into your cluster, eliminating the need for external orchestration infrastructure. Start by migrating your simplest cron job to an Argo Workflow and gradually tackle more complex pipelines. For comprehensive documentation, see the Argo Workflows docs and the Argo Workflows examples repository. Our guides on Karpenter for node autoscaling and GitHub Actions self-hosted runners complement your Kubernetes operations toolkit.
In conclusion, Argo Workflows Kubernetes batch processing is an essential capability for modern data and ML teams. By applying the DAG, artifact, retry, and scheduling patterns covered in this guide — and by respecting the cases where it is the wrong fit — you can build robust, scalable, and observable pipelines that live and breathe alongside the rest of your Kubernetes platform.