DevOps

Tekton + ArgoCD — full Kubernetes CI/CD on minikube (from scratch)

Build the complete flow: git push → Tekton builds + pushes image → ArgoCD auto-deploys to cluster. No shortcuts, real webhooks.

Most CI/CD tutorials skip the real complexity. This is the full production pattern deployed on minikube — Tekton for building, ArgoCD for deploying, real GitHub webhooks tying it together.

Why two tools

Tekton = CI (build, test, push image) ArgoCD = CD (deploy to cluster from Git)

Some teams do both with Jenkins or GitLab CI. In Kubernetes-native world, Tekton + ArgoCD is cleaner because:

  • Both run IN your cluster (no external servers)
  • Both use K8s CRDs (Tasks, Pipelines, Applications)
  • Separation of concerns: Tekton knows nothing about deployment; ArgoCD knows nothing about building

The full flow (what you’re building)

Developer pushes code to Git
       ↓
GitHub sends webhook to Tekton EventListener
       ↓
Tekton Pipeline runs:
   1. Clone repo
   2. Run tests
   3. Build container image with Kaniko
   4. Push image to registry (Docker Hub)
   5. Update GitOps repo with new image tag
       ↓
ArgoCD detects Git change
       ↓
ArgoCD applies updated manifest → cluster runs new image

Two Git repos:

  • App repo → source code
  • GitOps repo → K8s manifests (Tekton updates this, ArgoCD watches it)

Install both

# Tekton
kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml
kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/latest/interceptors.yaml

# ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml --server-side

The --server-side flag on ArgoCD is critical. Without it you’ll hit “metadata.annotations: Too long” errors.

Set up a Tekton Task

Tasks = reusable steps. Example: clone a repo.

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: git-clone
spec:
  params:
  - name: url
  - name: revision
  workspaces:
  - name: output
  steps:
  - name: clone
    image: alpine/git
    script: |
      cd $(workspaces.output.path)
      git clone --depth 1 -b $(params.revision) $(params.url) .

Pipeline = chain of Tasks

apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  name: build-and-deploy
spec:
  params:
  - name: repo-url
  - name: image-name
  workspaces:
  - name: shared-data
  tasks:
  - name: clone
    taskRef: { name: git-clone }
    params:
    - name: url
      value: $(params.repo-url)
    workspaces:
    - name: output
      workspace: shared-data
  - name: build-and-push
    runAfter: [clone]
    taskRef: { name: kaniko }
    params:
    - name: image
      value: $(params.image-name)
    workspaces:
    - name: source
      workspace: shared-data

Trigger the pipeline on git push

EventListener + TriggerBinding + TriggerTemplate:

apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: github-listener
spec:
  serviceAccountName: tekton-triggers-sa
  triggers:
  - name: github-push
    bindings:
    - ref: github-push-binding
    template:
      ref: build-and-deploy-template
    interceptors:
    - ref: { name: github }
      params:
      - name: secretRef
        value:
          secretName: github-webhook-secret
          secretKey: token
      - name: eventTypes
        value: ["push"]

Expose the EventListener with ngrok / cloudflared:

kubectl port-forward -n tekton-pipelines svc/el-github-listener 8080:8080
cloudflared tunnel --url http://localhost:8080

You get a public HTTPS URL. Add it as GitHub webhook. Every push = pipeline runs.

ArgoCD — watch the GitOps repo

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/<user>/gitops-repo
    targetRevision: HEAD
    path: apps/myapp
  destination:
    server: https://kubernetes.default.svc
    namespace: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Apply. ArgoCD now polls the repo. Any change → auto-syncs to cluster.

The complete loop

  1. Push code
  2. Tekton builds myapp:v1.2.3 → pushes to Docker Hub
  3. Last Tekton step commits image: myapp:v1.2.3 to GitOps repo
  4. ArgoCD sees the change (within 3 min default poll)
  5. ArgoCD applies new manifest → cluster pulls new image → runs it

Zero manual kubectl. Zero SSH. Zero deploy scripts.

The gotcha nobody mentions

Tekton EventListener needs a public URL for GitHub webhooks. On local minikube, use:

  • cloudflared tunnel (free, no signup) — best
  • ngrok (free tier requires signup)

Or skip webhooks initially — trigger pipelines manually with:

kubectl create -f pipelinerun.yaml

Why this is worth the complexity

  • Zero-downtime deploys — K8s rolling updates + ArgoCD sync = smooth
  • Git as source of truth — cluster state = whatever’s in your GitOps repo
  • Instant rollback — revert the GitOps commit, ArgoCD reverts the cluster
  • Multi-cluster — one ArgoCD instance can deploy to 50 clusters
  • Audit trail — every deploy = git commit = who + when

Common failures

  • EventListener not receiving webhooks — check tunnel is still up, verify GitHub webhook shows “delivered”
  • ArgoCD stuck in “OutOfSync” — auto-sync not enabled, or RBAC blocking
  • Pipeline creates image but doesn’t update GitOps repo — SSH key missing for git push step
  • imagePullSecrets missing — pod can’t pull private image after Tekton pushes it

Skip Tekton if…

You’re on a single team. GitHub Actions + ArgoCD is simpler. Tekton shines when:

  • You want CI in-cluster (no external SaaS)
  • You need custom pipeline logic
  • You’re at scale (hundreds of pipelines per day)

Full setup with all YAMLs

For 50+ production-ready YAML templates covering Kubernetes, Tekton, ArgoCD, Terraform and more, see the DevOps YAML Pack.

Skip the boilerplate

DevOps YAML Pack

40+ production-ready configs — Kubernetes, Docker, Terraform, Ansible, Helm, GitHub Actions. Every file commented. Copy, edit two lines, ship. MIT license, no watermarks.

Get the pack — ₹499 →