Tekton and Argo CD are powerful tools for achieving GitOps, but integrating them can feel like trying to get two highly opinionated chefs to collaborate on a single dish.

Let’s see what a typical GitOps flow looks like when these two are working together. Imagine you’ve just pushed a change to your application’s Kubernetes manifest repository.

apiVersion: v1
kind: Pod
metadata:
  name: my-app-pod
  labels:
    app: my-app
spec:
  containers:
  - name: my-app-container
    image: my-registry/my-app:v1.2.0 # <-- This tag just changed

Argo CD, constantly watching this repository, detects the change. Its job is to reconcile the desired state in Git with the actual state in your cluster.

# Argo CD's sync status in the UI might show:
# Application: my-app-sync
# Sync Status: OutOfSync
# Health: Healthy

But Argo CD isn’t just blindly applying changes. It’s configured to trigger a Tekton pipeline for the deployment. This Tekton pipeline is where the actual build, test, and potentially advanced deployment strategies (like canary or blue/green) happen.

# Your Argo CD Application manifest might look like this:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app-sync
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your-org/your-app-manifests.git
    targetRevision: HEAD
    path: path/to/your/app/config
  destination:
    server: https://kubernetes.default.svc
    namespace: my-app-namespace
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true
    # This is the key part: telling Argo CD to use Tekton
    hooks:
    - name: trigger-tekton-pipeline
      events:
      - SyncFail
      - Synced
      # This Argo CD hook will trigger a Kubernetes Job that in turn starts a Tekton PipelineRun
      # You'll typically define this Job in your application's manifest repository
      hookType: PostSync
      sync:
        resource: |
          apiVersion: batch/v1
          kind: Job
          metadata:
            name: trigger-tekton-deployment
          spec:
            template:
              spec:
                containers:
                - name: trigger
                  image: bitnami/kubectl:latest # Or any image with kubectl
                  command: ["/bin/sh", "-c"]
                  args:
                  - |
                    set -e
                    echo "Triggering Tekton PipelineRun for deployment..."
                    # This kubectl command creates a PipelineRun that Tekton will pick up
                    kubectl apply -f - <<EOF
                    apiVersion: tekton.dev/v1beta1
                    kind: PipelineRun
                    metadata:
                      name: my-app-deploy-$(date +%Y%m%d%H%M%S)
                      namespace: tekton-pipelines # Your Tekton namespace
                      labels:
                        app: my-app
                        git-commit: $(GIT_COMMIT_SHA) # Assuming you can get this from Argo CD context
                    spec:
                      pipelineRef:
                        name: build-and-deploy-pipeline # The name of your Tekton pipeline
                      params:
                      - name: image-tag
                        value: "v1.2.0" # This would ideally be dynamic
                      workspaces:
                      - name: shared-data
                        volumeClaimTemplate:
                          spec:
                            accessModes:
                            - ReadWriteOnce
                            resources:
                              requests:
                                storage: 1Gi
                    EOF
                restartPolicy: Never
            backoffLimit: 4

The PostSync hook in Argo CD is crucial. When Argo CD successfully syncs your application’s manifests (or even if it fails, depending on your events configuration), it can trigger a Kubernetes Job. This Job then uses kubectl to create a PipelineRun in your Tekton namespace.

The Tekton PipelineRun is what kicks off your actual CI/CD process. It references a Pipeline that you’ve defined in Tekton. This Pipeline is a directed acyclic graph (DAG) of Tasks. Each Task performs a specific action, like checking out code, building a Docker image, running tests, or deploying to Kubernetes.

# Example Tekton Pipeline
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: build-and-deploy-pipeline
spec:
  params:
  - name: image-tag
    description: The tag for the Docker image
    type: string
  tasks:
  - name: clone-repo
    taskRef:
      name: git-clone
    params:
    - name: url
      value: "https://github.com/your-org/your-app.git"
    - name: revision
      value: "main" # Or a specific commit SHA passed from Argo CD
    workspaces:
    - name: output
      workspace: shared-data

  - name: build-image
    runAfter:
    - clone-repo
    taskRef:
      name: buildah-build
    params:
    - name: IMAGE
      value: "my-registry/my-app:$(params.image-tag)"
    workspaces:
    - name: source
      workspace: shared-data

  - name: deploy-app
    runAfter:
    - build-image
    taskRef:
      name: kubernetes-deploy
    params:
    - name: manifest-path
      value: "path/to/your/app/config/deployment.yaml"
    - name: image-tag
      value: "$(params.image-tag)"
    workspaces:
    - name: manifest
      workspace: shared-data

Here, the build-and-deploy-pipeline has three tasks: clone-repo (using a standard git-clone task), build-image (using a buildah-build task), and deploy-app (using a kubernetes-deploy task). Notice how runAfter ensures tasks execute in sequence, and workspaces (shared-data) allow data to be passed between them.

The deploy-app task would typically use kubectl apply or a similar tool to update your Kubernetes deployment with the new image tag. This is where Tekton directly interacts with your cluster to perform the deployment actions that Argo CD orchestrated.

The magic is in the interplay: Argo CD ensures your desired state in Git is reflected in the cluster, and it uses Tekton to perform the complex actions needed to get there. Tekton handles the how of the deployment (build, test, deploy), while Argo CD handles the what and when (syncing Git to cluster state).

When you configure Tekton tasks, you’re defining the exact commands and container images that execute your build and deploy logic. For instance, a buildah-build task might use buildah bud --tag $(params.IMAGE) $(workspaces.source.path) to build your container image. Similarly, a kubernetes-deploy task could use kubectl set image deployment/<your-deployment-name> <your-container-name>=$(params.IMAGE) -n <your-namespace> to update your deployment.

The key challenge and a common point of confusion is how to pass dynamic information, like the specific Git commit SHA or the image tag that Argo CD just synced, into your Tekton PipelineRun. The Job triggered by the Argo CD PostSync hook needs to inject this context. You might parse Argo CD’s internal state or use environment variables exposed to the Job to populate the PipelineRun’s params. For example, if your Application manifest has a way to expose the targetRevision to the Job’s environment, you could use that to set the revision parameter in the Tekton PipelineRun.

The next hurdle you’ll likely encounter is managing complex deployment strategies like blue/green or canary releases within your Tekton pipelines, which requires careful orchestration of Kubernetes resources and traffic management.

Want structured learning?

Take the full Tekton course →