Argo Workflows for Kubernetes Job Orchestration
Argo Workflows is a Kubernetes-native workflow engine for orchestrating parallel jobs, data pipelines, and CI/CD using directed acyclic graphs (DAGs). This guide covers installing Argo Workflows, creating workflow templates, managing artifacts, scheduling cron workflows, and building CI/CD pipelines.
Prerequisites
- Kubernetes cluster 1.24+
kubectlconfigured with cluster access- Helm 3.x
- S3-compatible storage (MinIO, AWS S3) for artifact management
- Argo CLI (installed in this guide)
Install Argo Workflows
# Create the namespace
kubectl create namespace argo
# Install Argo Workflows (quick start manifest)
ARGO_VERSION=v3.5.4
kubectl apply -n argo \
-f https://github.com/argoproj/argo-workflows/releases/download/${ARGO_VERSION}/install.yaml
# Wait for the pods to be ready
kubectl wait --for=condition=Ready pods --all -n argo --timeout=120s
# Verify the installation
kubectl get pods -n argo
# Install the Argo CLI
curl -sLO https://github.com/argoproj/argo-workflows/releases/download/${ARGO_VERSION}/argo-linux-amd64.gz
gunzip argo-linux-amd64.gz
chmod +x argo-linux-amd64
sudo mv argo-linux-amd64 /usr/local/bin/argo
argo version
Access the Argo UI
# Port-forward to the Argo server
kubectl -n argo port-forward svc/argo-server 2746:2746
# The UI is available at https://localhost:2746
# Note: uses self-signed TLS by default
# For a local development cluster (no auth):
kubectl patch deployment argo-server -n argo \
--patch '{"spec":{"template":{"spec":{"containers":[{"name":"argo-server","args":["server","--auth-mode=server"]}]}}}}'
# Configure Argo CLI to connect
argo auth token # Get the token for UI login
export ARGO_SERVER='localhost:2746'
export ARGO_SECURE=false # For dev with no TLS
Your First Workflow
# hello-workflow.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: hello-
namespace: argo
spec:
entrypoint: hello-world
templates:
- name: hello-world
container:
image: alpine:3.18
command: [echo]
args: ["Hello, Argo Workflows!"]
resources:
requests:
memory: 32Mi
cpu: 100m
# Submit the workflow
argo submit -n argo hello-workflow.yaml --watch
# List workflows
argo list -n argo
# Get workflow logs
argo logs -n argo @latest
# Delete a workflow
argo delete -n argo hello-XXXXX
Parameterized workflow:
# parameterized-workflow.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: param-workflow-
spec:
entrypoint: process-data
arguments:
parameters:
- name: input-file
value: "data.csv"
- name: output-bucket
value: "my-results"
templates:
- name: process-data
inputs:
parameters:
- name: input-file
- name: output-bucket
container:
image: my-org/data-processor:latest
command: [python, /app/process.py]
args:
- --input={{inputs.parameters.input-file}}
- --output={{inputs.parameters.output-bucket}}
# Override parameters at submit time
argo submit -n argo parameterized-workflow.yaml \
-p input-file=large-dataset.csv \
-p output-bucket=results-2024
DAG Workflows
DAGs define dependencies between tasks explicitly:
# dag-pipeline.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: data-pipeline-
spec:
entrypoint: data-pipeline
templates:
- name: data-pipeline
dag:
tasks:
# Task A: no dependencies (runs first)
- name: extract
template: run-extract
# Task B: depends on A
- name: transform
template: run-transform
dependencies: [extract]
arguments:
parameters:
- name: input
value: "{{tasks.extract.outputs.parameters.output-path}}"
# Task C: depends on A (runs in parallel with B)
- name: validate
template: run-validate
dependencies: [extract]
# Task D: depends on both B and C
- name: load
template: run-load
dependencies: [transform, validate]
- name: run-extract
outputs:
parameters:
- name: output-path
valueFrom:
path: /tmp/output-path.txt
container:
image: my-org/etl:latest
command: [python, extract.py]
- name: run-transform
inputs:
parameters:
- name: input
container:
image: my-org/etl:latest
command: [python, transform.py]
args: ["--input={{inputs.parameters.input}}"]
- name: run-validate
container:
image: my-org/etl:latest
command: [python, validate.py]
- name: run-load
container:
image: my-org/etl:latest
command: [python, load.py]
Fan-out / fan-in with items:
templates:
- name: process-all-files
dag:
tasks:
- name: process-file
template: process-single-file
arguments:
parameters:
- name: filename
value: "{{item}}"
withItems:
- report-jan.csv
- report-feb.csv
- report-mar.csv
- name: process-single-file
inputs:
parameters:
- name: filename
container:
image: my-org/processor:latest
command: [python, process.py, "{{inputs.parameters.filename}}"]
Workflow Templates and Reuse
WorkflowTemplate resources are reusable workflow definitions stored in the cluster:
# workflow-template.yaml
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: docker-build-push
namespace: argo
spec:
templates:
- name: build-and-push
inputs:
parameters:
- name: image
- name: tag
- name: context
value: "."
container:
image: gcr.io/kaniko-project/executor:latest
command:
- /kaniko/executor
- --dockerfile=Dockerfile
- --context={{inputs.parameters.context}}
- --destination={{inputs.parameters.image}}:{{inputs.parameters.tag}}
volumeMounts:
- name: docker-config
mountPath: /kaniko/.docker/
volumes:
- name: docker-config
secret:
secretName: docker-registry-credentials
# Reference the template from another workflow
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: ci-build-
spec:
entrypoint: build
templates:
- name: build
steps:
- - name: build-image
templateRef:
name: docker-build-push
template: build-and-push
arguments:
parameters:
- name: image
value: my-org/my-app
- name: tag
value: "{{workflow.parameters.git-sha}}"
Artifact Management
Configure artifact storage to pass files between steps:
# Configure MinIO as artifact store
# Apply to argo namespace
kubectl create secret generic minio-secret \
-n argo \
--from-literal=accesskey=minioadmin \
--from-literal=secretkey=minioadmin
kubectl patch configmap workflow-controller-configmap -n argo \
--patch '{"data":{"artifactRepository":"{\"s3\":{\"bucket\":\"argo-artifacts\",\"endpoint\":\"minio.minio.svc:9000\",\"insecure\":true,\"accessKeySecret\":{\"name\":\"minio-secret\",\"key\":\"accesskey\"},\"secretKeySecret\":{\"name\":\"minio-secret\",\"key\":\"secretkey\"}}}"}}'
# Workflow using artifacts
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: artifact-pipeline-
spec:
entrypoint: pipeline
templates:
- name: pipeline
steps:
- - name: generate
template: generate-report
- - name: process
template: process-report
arguments:
artifacts:
- name: report
from: "{{steps.generate.outputs.artifacts.report}}"
- name: generate-report
outputs:
artifacts:
- name: report
path: /tmp/report.json
container:
image: alpine:3.18
command: [sh, -c]
args: ['echo "{\"status\":\"ok\",\"count\":42}" > /tmp/report.json']
- name: process-report
inputs:
artifacts:
- name: report
path: /tmp/input-report.json
container:
image: python:3.11-alpine
command: [python3, -c]
args: ['import json; d=json.load(open("/tmp/input-report.json")); print(d)']
Cron Workflows
# cron-workflow.yaml
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
name: nightly-report
namespace: argo
spec:
schedule: "0 2 * * *" # Daily at 2am UTC
timezone: "UTC"
concurrencyPolicy: Forbid # Don't run if previous is still running
successfulJobsHistoryLimit: 5
failedJobsHistoryLimit: 3
workflowSpec:
entrypoint: generate-report
templates:
- name: generate-report
container:
image: my-org/reporter:latest
command: [python, generate.py]
env:
- name: REPORT_DATE
value: "{{workflow.scheduledTime}}"
# Manage cron workflows
argo cron list -n argo
argo cron suspend nightly-report -n argo
argo cron resume nightly-report -n argo
# Manually trigger a cron workflow
argo cron get nightly-report -n argo
argo submit -n argo --from=cronwf/nightly-report
CI/CD Pipeline Patterns
# Complete CI pipeline as a WorkflowTemplate
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: ci-pipeline
namespace: argo
spec:
entrypoint: ci
arguments:
parameters:
- name: git-repo
- name: git-branch
value: main
- name: image
templates:
- name: ci
dag:
tasks:
- name: lint
template: run-lint
- name: test
template: run-tests
dependencies: [lint]
- name: build
template: build-image
dependencies: [test]
- name: scan
template: security-scan
dependencies: [build]
- name: deploy-staging
template: deploy
dependencies: [scan]
arguments:
parameters:
- name: environment
value: staging
Troubleshooting
Workflow stuck in Pending state:
# Check pod events
kubectl describe pod -n argo -l workflows.argoproj.io/workflow=hello-XXXXX
# Common cause: resource requests too high for available nodes
kubectl top nodes
Workflow fails with "permission denied":
# Argo needs a service account with proper RBAC
kubectl create rolebinding argo-default-binding \
--clusterrole=argo-aggregate-to-admin \
--serviceaccount=argo:default \
-n argo
Artifacts not being saved:
# Verify artifact repository config
kubectl get configmap workflow-controller-configmap -n argo -o yaml
# Check MinIO/S3 connectivity from a pod
kubectl run -n argo debug --rm -it --image=alpine -- \
wget http://minio.minio.svc:9000/minio/health/live
DAG task not running:
# View detailed workflow status
argo get -n argo WORKFLOW_NAME -o yaml | grep -A 10 "nodes:"
# A task won't run if its dependencies failed
# Check dependency task logs:
argo logs -n argo WORKFLOW_NAME -c extract
Conclusion
Argo Workflows provides a powerful, Kubernetes-native platform for orchestrating complex job pipelines, CI/CD workflows, and data processing tasks. Its DAG execution model, artifact management, and template reuse make it suitable for everything from simple cron jobs to multi-stage ML training pipelines. Combined with the Argo Events and Argo CD ecosystem, it forms a complete GitOps-native automation platform.


