Argo Workflows
Container-native workflow engine for orchestrating parallel jobs on Kubernetes
Argo Workflows is an open-source container-native workflow engine for orchestrating parallel jobs on Kubernetes. It's commonly used for CI/CD pipelines, data processing, machine learning workflows, and infrastructure automation.
What you'll learn
- Argo Workflows architecture and components
- Workflow templates and patterns
- CI/CD pipeline implementation
- Event-driven workflow triggers
- Parallel execution strategies
- Artifact and parameter passing
Related tools
- Argo Events for event-driven automation
- ArgoCD for GitOps deployments
- Argo Rollouts for progressive delivery
- Kaniko for container builds
Why Argo Workflows?#
Argo Workflows brings powerful orchestration capabilities to Kubernetes:
| Feature | Benefit |
|---|---|
| Container-native | Each step runs in its own container |
| DAG Support | Complex workflow dependencies |
| Parallel Execution | Fan-out/fan-in patterns |
| Artifact Management | S3, GCS, Minio integration |
| Event-driven | Trigger workflows from webhooks, Git, schedules |
| Kubernetes-native | CRD-based, kubectl compatible |
Architecture#
1┌─────────────────────────────────────────────────────────────────┐2│ Argo Workflows Components │3│ │4│ ┌─────────────────┐ ┌─────────────────┐ │5│ │ Workflow │ │ Argo Server │ │6│ │ Controller │ │ (UI + API) │ │7│ └────────┬────────┘ └─────────────────┘ │8│ │ │9│ ┌────────▼────────────────────────────────┐ │10│ │ Workflow Executor │ │11│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │12│ │ │ Pod 1 │ │ Pod 2 │ │ Pod 3 │ │ │13│ │ │ (Step) │ │ (Step) │ │ (Step) │ │ │14│ │ └─────────┘ └─────────┘ └─────────┘ │ │15│ └──────────────────────────────────────────┘ │16│ │17│ ┌─────────────────┐ ┌─────────────────┐ │18│ │ Artifact Repo │ │ Event Source │ │19│ │ (S3/GCS/Minio) │ │ (Webhook/Git) │ │20│ └─────────────────┘ └─────────────────┘ │21└─────────────────────────────────────────────────────────────────┘Components#
Workflow Controller Watches for Workflow resources and creates pods to execute workflow steps. Manages workflow lifecycle and status.
Argo Server Provides REST API and Web UI for workflow management. Supports SSO and RBAC.
Workflow Executor Runs inside each step pod to manage artifacts, parameters, and container lifecycle.
Artifact Repository Stores workflow artifacts (S3, GCS, Minio, Azure Blob).
Installation#
1#!/bin/bash2# Argo Workflows installation34# Create namespace5kubectl create namespace argo67# Install Argo Workflows8kubectl apply -n argo -f https://github.com/argoproj/argo-workflows/releases/download/v3.5.2/install.yaml910# Install Argo Events for triggering workflows11kubectl create namespace argo-events12kubectl apply -n argo-events -f https://raw.githubusercontent.com/argoproj/argo-events/stable/manifests/install.yaml1314# Configure artifact repository15kubectl apply -f - <<EOF16apiVersion: v117kind: ConfigMap18metadata:19 name: artifact-repositories20 namespace: argo21data:22 default-v1: |23 archiveLogs: true24 s3:25 bucket: argo-artifacts26 endpoint: s3.amazonaws.com27 region: us-east-128 accessKeySecret:29 name: argo-artifacts-s330 key: accessKey31 secretKeySecret:32 name: argo-artifacts-s333 key: secretKey34EOF3536# Create service account with proper RBAC37kubectl apply -f - <<EOF38apiVersion: v139kind: ServiceAccount40metadata:41 name: argo-workflow42 namespace: argo43---44apiVersion: rbac.authorization.k8s.io/v145kind: ClusterRoleBinding46metadata:47 name: argo-workflow-binding48roleRef:49 apiGroup: rbac.authorization.k8s.io50 kind: ClusterRole51 name: argo-workflow-role52subjects:53 - kind: ServiceAccount54 name: argo-workflow55 namespace: argo56EOF5758echo "Argo Workflows installed successfully"CI/CD Pipeline#
Complete CI/CD Workflow Template#
1apiVersion: argoproj.io/v1alpha12kind: WorkflowTemplate3metadata:4 name: ci-cd-pipeline5 namespace: argo6spec:7 entrypoint: ci-cd89 serviceAccountName: argo-workflow1011 # Workflow-level parameters12 arguments:13 parameters:14 - name: repo-url15 value: "https://github.com/company/application.git"16 - name: branch17 value: "main"18 - name: image-registry19 value: "ghcr.io/company"20 - name: app-name21 value: "payment-service"22 - name: environment23 value: "staging"2425 # Volume claim template for workspace26 volumeClaimTemplates:27 - metadata:28 name: workspace29 spec:30 accessModes: ["ReadWriteOnce"]31 resources:32 requests:33 storage: 5Gi3435 templates:36 # Main DAG template37 - name: ci-cd38 dag:39 tasks:40 - name: checkout41 template: git-checkout42 arguments:43 parameters:44 - name: repo-url45 value: "{{workflow.parameters.repo-url}}"46 - name: branch47 value: "{{workflow.parameters.branch}}"4849 - name: run-tests50 template: test51 dependencies: [checkout]5253 - name: security-scan54 template: security-scan55 dependencies: [checkout]5657 - name: build-image58 template: build-and-push59 dependencies: [run-tests, security-scan]60 arguments:61 parameters:62 - name: image-tag63 value: "{{workflow.parameters.image-registry}}/{{workflow.parameters.app-name}}:{{workflow.parameters.branch}}-{{workflow.uid}}"6465 - name: update-manifests66 template: update-gitops-repo67 dependencies: [build-image]68 arguments:69 parameters:70 - name: image-tag71 value: "{{workflow.parameters.image-registry}}/{{workflow.parameters.app-name}}:{{workflow.parameters.branch}}-{{workflow.uid}}"72 - name: environment73 value: "{{workflow.parameters.environment}}"7475 - name: notify76 template: slack-notify77 dependencies: [update-manifests]7879 # Git checkout template80 - name: git-checkout81 inputs:82 parameters:83 - name: repo-url84 - name: branch85 container:86 image: alpine/git:latest87 command: [sh, -c]88 args:89 - |90 git clone --depth 1 --branch {{inputs.parameters.branch}} \91 {{inputs.parameters.repo-url}} /workspace/source92 cd /workspace/source93 echo "Checked out commit: $(git rev-parse HEAD)"94 echo "$(git rev-parse HEAD)" > /workspace/commit-sha95 volumeMounts:96 - name: workspace97 mountPath: /workspace9899 # Test template100 - name: test101 container:102 image: node:18-alpine103 command: [sh, -c]104 args:105 - |106 cd /workspace/source107 npm ci108 npm run lint109 npm run test:unit110 npm run test:integration111 volumeMounts:112 - name: workspace113 mountPath: /workspace114 resources:115 requests:116 cpu: 500m117 memory: 1Gi118 limits:119 cpu: 2000m120 memory: 4Gi121122 # Security scan template123 - name: security-scan124 container:125 image: aquasec/trivy:latest126 command: [sh, -c]127 args:128 - |129 cd /workspace/source130131 # Scan for vulnerabilities in dependencies132 trivy fs --security-checks vuln,secret,config \133 --severity HIGH,CRITICAL \134 --exit-code 1 \135 --format table \136 .137138 # Generate SBOM139 trivy fs --format spdx-json \140 --output /workspace/sbom.json \141 .142 volumeMounts:143 - name: workspace144 mountPath: /workspace145 outputs:146 artifacts:147 - name: sbom148 path: /workspace/sbom.json149 s3:150 key: "{{workflow.uid}}/sbom.json"151152 # Build and push template with Kaniko153 - name: build-and-push154 inputs:155 parameters:156 - name: image-tag157 container:158 image: gcr.io/kaniko-project/executor:latest159 args:160 - --dockerfile=/workspace/source/Dockerfile161 - --context=/workspace/source162 - --destination={{inputs.parameters.image-tag}}163 - --cache=true164 - --cache-repo={{workflow.parameters.image-registry}}/cache165 - --snapshot-mode=redo166 - --use-new-run167 volumeMounts:168 - name: workspace169 mountPath: /workspace170 - name: docker-config171 mountPath: /kaniko/.docker172 resources:173 requests:174 cpu: 1000m175 memory: 2Gi176 limits:177 cpu: 4000m178 memory: 8Gi179 volumes:180 - name: docker-config181 secret:182 secretName: docker-registry-creds183184 # Update GitOps repository185 - name: update-gitops-repo186 inputs:187 parameters:188 - name: image-tag189 - name: environment190 container:191 image: alpine/git:latest192 command: [sh, -c]193 args:194 - |195 set -e196197 # Clone GitOps repo198 git clone https://github.com/company/gitops-repo.git /tmp/gitops199 cd /tmp/gitops200201 # Update image tag in Kustomization202 cd apps/{{workflow.parameters.app-name}}/overlays/{{inputs.parameters.environment}}203204 # Update image tag205 kustomize edit set image \206 {{workflow.parameters.app-name}}={{inputs.parameters.image-tag}}207208 # Commit and push209 git config user.email "[email protected]"210 git config user.name "Argo Workflows"211 git add .212 git commit -m "chore: update {{workflow.parameters.app-name}} to {{inputs.parameters.image-tag}}"213 git push origin main214215 echo "GitOps repository updated successfully"216 env:217 - name: GIT_TOKEN218 valueFrom:219 secretKeyRef:220 name: git-credentials221 key: token222 volumeMounts:223 - name: workspace224 mountPath: /workspace225226 # Slack notification template227 - name: slack-notify228 container:229 image: curlimages/curl:latest230 command: [sh, -c]231 args:232 - |233 curl -X POST $SLACK_WEBHOOK_URL \234 -H 'Content-type: application/json' \235 --data '{236 "text": "CI/CD Pipeline Completed",237 "blocks": [238 {239 "type": "section",240 "text": {241 "type": "mrkdwn",242 "text": "*Pipeline Completed Successfully*\n* App: {{workflow.parameters.app-name}}\n* Environment: {{workflow.parameters.environment}}\n* Branch: {{workflow.parameters.branch}}"243 }244 }245 ]246 }'247 env:248 - name: SLACK_WEBHOOK_URL249 valueFrom:250 secretKeyRef:251 name: slack-webhook252 key: urlEvent-Driven Workflows#
GitHub Webhook Event Source#
1apiVersion: argoproj.io/v1alpha12kind: EventSource3metadata:4 name: github-webhook5 namespace: argo-events6spec:7 github:8 app-push:9 owner: company10 repository: application11 webhook:12 endpoint: /push13 port: "12000"14 method: POST15 url: https://webhooks.example.com16 events:17 - push18 apiToken:19 name: github-token20 key: token21 webhookSecret:22 name: github-webhook-secret23 key: secret24 contentType: json25 insecure: false26 active: true2728 app-pr:29 owner: company30 repository: application31 webhook:32 endpoint: /pr33 port: "12000"34 method: POST35 url: https://webhooks.example.com36 events:37 - pull_request38 apiToken:39 name: github-token40 key: token41 webhookSecret:42 name: github-webhook-secret43 key: secretSensor to Trigger Workflows#
1apiVersion: argoproj.io/v1alpha12kind: Sensor3metadata:4 name: github-sensor5 namespace: argo-events6spec:7 dependencies:8 - name: push-dep9 eventSourceName: github-webhook10 eventName: app-push11 filters:12 data:13 - path: body.ref14 type: string15 value:16 - refs/heads/main17 - refs/heads/develop1819 - name: pr-dep20 eventSourceName: github-webhook21 eventName: app-pr22 filters:23 data:24 - path: body.action25 type: string26 value:27 - opened28 - synchronize2930 triggers:31 # Trigger CI/CD on push to main/develop32 - template:33 name: trigger-ci-cd34 argoWorkflow:35 group: argoproj.io36 version: v1alpha137 resource: workflows38 operation: submit39 source:40 resource:41 apiVersion: argoproj.io/v1alpha142 kind: Workflow43 metadata:44 generateName: ci-cd-pipeline-45 spec:46 workflowTemplateRef:47 name: ci-cd-pipeline48 arguments:49 parameters:50 - name: branch51 value: ""52 - name: repo-url53 value: ""54 parameters:55 - src:56 dependencyName: push-dep57 dataKey: body.ref58 dataTemplate: "{{ trimPrefix .Input \"refs/heads/\" }}"59 dest: spec.arguments.parameters.0.value60 - src:61 dependencyName: push-dep62 dataKey: body.repository.clone_url63 dest: spec.arguments.parameters.1.value64 retryStrategy:65 steps: 366 duration: "10s"6768 # Trigger PR preview on pull request69 - template:70 name: trigger-pr-preview71 argoWorkflow:72 group: argoproj.io73 version: v1alpha174 resource: workflows75 operation: submit76 source:77 resource:78 apiVersion: argoproj.io/v1alpha179 kind: Workflow80 metadata:81 generateName: pr-preview-82 spec:83 workflowTemplateRef:84 name: pr-preview-workflow85 arguments:86 parameters:87 - name: pr-number88 value: ""89 - name: head-sha90 value: ""91 parameters:92 - src:93 dependencyName: pr-dep94 dataKey: body.number95 dest: spec.arguments.parameters.0.value96 - src:97 dependencyName: pr-dep98 dataKey: body.pull_request.head.sha99 dest: spec.arguments.parameters.1.valueCron Scheduled Workflows#
1apiVersion: argoproj.io/v1alpha12kind: CronWorkflow3metadata:4 name: nightly-tests5 namespace: argo6spec:7 schedule: "0 2 * * *" # Every day at 2 AM8 timezone: "America/New_York"9 concurrencyPolicy: Replace10 startingDeadlineSeconds: 011 successfulJobsHistoryLimit: 312 failedJobsHistoryLimit: 31314 workflowSpec:15 entrypoint: run-nightly-tests16 serviceAccountName: argo-workflow1718 templates:19 - name: run-nightly-tests20 dag:21 tasks:22 - name: integration-tests23 template: integration-tests24 - name: e2e-tests25 template: e2e-tests26 - name: performance-tests27 template: performance-tests28 dependencies: [integration-tests, e2e-tests]29 - name: notify30 template: notify-results31 dependencies: [performance-tests]3233 - name: integration-tests34 container:35 image: company/test-runner:latest36 command: [npm, run, test:integration]3738 - name: e2e-tests39 container:40 image: company/test-runner:latest41 command: [npm, run, test:e2e]4243 - name: performance-tests44 container:45 image: company/test-runner:latest46 command: [npm, run, test:performance]4748 - name: notify-results49 container:50 image: curlimages/curl:latest51 command: [sh, -c]52 args:53 - |54 curl -X POST $SLACK_WEBHOOK_URL \55 -H 'Content-type: application/json' \56 --data '{"text": "Nightly tests completed"}'Workflow Patterns#
Parallel Execution with Fan-out/Fan-in#
1apiVersion: argoproj.io/v1alpha12kind: WorkflowTemplate3metadata:4 name: parallel-test-suite5 namespace: argo6spec:7 entrypoint: run-tests89 templates:10 - name: run-tests11 dag:12 tasks:13 - name: setup14 template: setup-environment1516 # Fan-out: Run test suites in parallel17 - name: unit-tests18 template: run-test-suite19 dependencies: [setup]20 arguments:21 parameters:22 - name: suite23 value: "unit"2425 - name: integration-tests26 template: run-test-suite27 dependencies: [setup]28 arguments:29 parameters:30 - name: suite31 value: "integration"3233 - name: e2e-tests34 template: run-test-suite35 dependencies: [setup]36 arguments:37 parameters:38 - name: suite39 value: "e2e"4041 - name: performance-tests42 template: run-test-suite43 dependencies: [setup]44 arguments:45 parameters:46 - name: suite47 value: "performance"4849 # Fan-in: Aggregate results50 - name: aggregate-results51 template: aggregate-test-results52 dependencies: [unit-tests, integration-tests, e2e-tests, performance-tests]5354 - name: setup-environment55 container:56 image: node:18-alpine57 command: [sh, -c]58 args:59 - |60 npm ci --cache /tmp/.npm6162 - name: run-test-suite63 inputs:64 parameters:65 - name: suite66 outputs:67 artifacts:68 - name: test-results69 path: /workspace/test-results70 container:71 image: node:18-alpine72 command: [sh, -c]73 args:74 - |75 npm run test:{{inputs.parameters.suite}} -- \76 --reporter junit \77 --output /workspace/test-results/{{inputs.parameters.suite}}.xml7879 - name: aggregate-test-results80 container:81 image: alpine:latest82 command: [sh, -c]83 args:84 - |85 echo "All test suites completed successfully"Dynamic Parallelism with withItems#
1apiVersion: argoproj.io/v1alpha12kind: WorkflowTemplate3metadata:4 name: matrix-build5 namespace: argo6spec:7 entrypoint: build-matrix89 templates:10 - name: build-matrix11 dag:12 tasks:13 - name: build14 template: build-for-platform15 arguments:16 parameters:17 - name: platform18 value: "{{item.platform}}"19 - name: arch20 value: "{{item.arch}}"21 withItems:22 - { platform: "linux", arch: "amd64" }23 - { platform: "linux", arch: "arm64" }24 - { platform: "darwin", arch: "amd64" }25 - { platform: "darwin", arch: "arm64" }26 - { platform: "windows", arch: "amd64" }2728 - name: build-for-platform29 inputs:30 parameters:31 - name: platform32 - name: arch33 container:34 image: golang:1.2135 command: [sh, -c]36 args:37 - |38 GOOS={{inputs.parameters.platform}} \39 GOARCH={{inputs.parameters.arch}} \40 go build -o /output/app-{{inputs.parameters.platform}}-{{inputs.parameters.arch}} .41 env:42 - name: CGO_ENABLED43 value: "0"Dynamic Parallelism with withParam#
1apiVersion: argoproj.io/v1alpha12kind: WorkflowTemplate3metadata:4 name: dynamic-parallel5 namespace: argo6spec:7 entrypoint: main89 templates:10 - name: main11 steps:12 - - name: get-services13 template: list-services14 - - name: deploy-service15 template: deploy16 arguments:17 parameters:18 - name: service-name19 value: "{{item}}"20 withParam: "{{steps.get-services.outputs.result}}"2122 - name: list-services23 script:24 image: python:3.9-alpine25 command: [python]26 source: |27 import json28 services = ["api", "worker", "scheduler", "web"]29 print(json.dumps(services))3031 - name: deploy32 inputs:33 parameters:34 - name: service-name35 container:36 image: kubectl:latest37 command: [kubectl, apply, -f]38 args:39 - /manifests/{{inputs.parameters.service-name}}/Retry and Error Handling#
1apiVersion: argoproj.io/v1alpha12kind: WorkflowTemplate3metadata:4 name: retry-pattern5 namespace: argo6spec:7 entrypoint: main89 templates:10 - name: main11 dag:12 tasks:13 - name: deploy14 template: deploy-with-retry15 continueOn:16 failed: true17 - name: notify-failure18 template: notify19 dependencies: [deploy]20 when: "{{tasks.deploy.status}} == Failed"21 - name: notify-success22 template: notify-success23 dependencies: [deploy]24 when: "{{tasks.deploy.status}} == Succeeded"2526 - name: deploy-with-retry27 retryStrategy:28 limit: 329 backoff:30 duration: "30s"31 factor: 232 maxDuration: "5m"33 retryPolicy: "Always"34 container:35 image: kubectl:latest36 command: [kubectl, apply, -f, /manifests/]3738 - name: notify39 container:40 image: curlimages/curl:latest41 command: [sh, -c]42 args:43 - |44 curl -X POST $SLACK_WEBHOOK_URL \45 -d '{"text": "Deployment failed after retries"}'4647 - name: notify-success48 container:49 image: curlimages/curl:latest50 command: [sh, -c]51 args:52 - |53 curl -X POST $SLACK_WEBHOOK_URL \54 -d '{"text": "Deployment succeeded"}'Artifacts and Parameters#
Passing Artifacts Between Steps#
1apiVersion: argoproj.io/v1alpha12kind: Workflow3metadata:4 name: artifact-passing5spec:6 entrypoint: main78 templates:9 - name: main10 dag:11 tasks:12 - name: generate13 template: generate-artifact14 - name: consume15 template: consume-artifact16 dependencies: [generate]17 arguments:18 artifacts:19 - name: data20 from: "{{tasks.generate.outputs.artifacts.data}}"2122 - name: generate-artifact23 container:24 image: alpine:latest25 command: [sh, -c]26 args:27 - |28 echo '{"key": "value", "count": 42}' > /tmp/data.json29 outputs:30 artifacts:31 - name: data32 path: /tmp/data.json3334 - name: consume-artifact35 inputs:36 artifacts:37 - name: data38 path: /tmp/data.json39 container:40 image: alpine:latest41 command: [sh, -c]42 args:43 - |44 cat /tmp/data.json45 echo "Processing data..."Output Parameters#
1apiVersion: argoproj.io/v1alpha12kind: Workflow3metadata:4 name: parameter-passing5spec:6 entrypoint: main78 templates:9 - name: main10 dag:11 tasks:12 - name: get-version13 template: determine-version14 - name: build15 template: build-app16 dependencies: [get-version]17 arguments:18 parameters:19 - name: version20 value: "{{tasks.get-version.outputs.parameters.version}}"2122 - name: determine-version23 container:24 image: alpine:latest25 command: [sh, -c]26 args:27 - |28 VERSION=$(date +%Y%m%d)-$(git rev-parse --short HEAD)29 echo $VERSION > /tmp/version.txt30 outputs:31 parameters:32 - name: version33 valueFrom:34 path: /tmp/version.txt3536 - name: build-app37 inputs:38 parameters:39 - name: version40 container:41 image: docker:latest42 command: [sh, -c]43 args:44 - |45 echo "Building version: {{inputs.parameters.version}}"46 docker build -t app:{{inputs.parameters.version}} .Best Practices#
Workflow Organization#
- Use WorkflowTemplates - Reusable workflow definitions
- Parameterize workflows - Make workflows configurable
- Use DAGs for dependencies - Clear step relationships
- Limit workflow history - Set retention limits
Resource Management#
- Set resource limits - Prevent runaway pods
- Use node selectors - Run on appropriate nodes
- Configure timeouts - Prevent hanging workflows
- Clean up artifacts - Set artifact retention policies
Security#
- Use service accounts - Limit Kubernetes permissions
- Secure secrets - Use Kubernetes secrets or external stores
- Audit workflow runs - Enable audit logging
- Network policies - Restrict workflow pod network access