logzly. Pipeline Pulse

Automated Canary Deploys with GitHub Actions & Argo Rollouts

Read this article in clean Markdown format for LLMs and AI context.

Push a change, watch a canary fail, and spend hours fixing a broken release? Stop the firefighting – this guide shows you how to set up a fully automated canary deployment pipeline that builds, tests, and rolls out safely with zero manual steps.

In the next few minutes you’ll get a copy‑paste GitHub Actions workflow, a ready‑to‑use Argo Rollouts manifest, and the exact tweaks needed to keep your SaaS service healthy. By the end, pushing to main will automatically spin up a canary, validate it, and promote it — or roll back without you lifting a finger.

Why Manual Canary Releases Break Everything

Most teams still tweak a YAML file, run kubectl apply, and stare at dashboards hoping nothing goes wrong. Miss a selector, forget to update the image tag, or omit a health check, and the new pods never receive traffic. The result? Users see errors, alerts fire, and you spend the whole day rolling back.

An automated canary deployment pipeline eliminates these human slips. It programmatically:

  1. Builds a Docker image with a unique SHA tag.
  2. Updates the Argo Rollout resource.
  3. Runs health checks and, if they pass, gradually shifts traffic.

If any check fails, Argo automatically rolls back to the previous stable version.

Setup: GitHub Actions Workflow

Create .github/workflows/canary.yml in your repo. This workflow builds the image, pushes it to your registry, and triggers Argo Rollouts.

name: Canary Deploy

on:
  push:
    branches: [ main ]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Log in to registry
        uses: docker/login-action@v2
        with:
          registry: ${{ secrets.REGISTRY }}
          username: ${{ secrets.REGISTRY_USER }}
          password: ${{ secrets.REGISTRY_PASS }}
      - name: Build and push image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ${{ secrets.REGISTRY }}/${{ github.repository }}:${{ github.sha }}

  rollout:
    needs: build-and-push
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up kubectl
        uses: azure/setup-kubectl@v3
        with:
          version: 'v1.28.0'
      - name: Install Argo Rollouts
        run: |
          kubectl argo rollouts install --manifests https://raw.githubusercontent.com/argoproj/argo-rollouts/stable/manifests/install.yaml
      - name: Patch Rollout with new image
        run: |
          kubectl set image rollout/my-app my-app=${{ secrets.REGISTRY }}/${{ github.repository }}:${{ github.sha }} -n production
      - name: Promote if healthy
        run: |
          kubectl argo rollouts promote my-app -n production

What this does:

  • Checks out the code.
  • Builds a Docker image tagged with the commit SHA (critical for Argo to detect changes).
  • Pushes the image to your private registry.
  • Updates the Argo Rollout and tells it to promote the canary only when health checks succeed.

Configure the Argo Rollouts Manifest

Save the following as rollout.yaml in the same repository. Adjust the replicas, namespace, and containerPort to match your service.

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: my-app
  namespace: production
spec:
  replicas: 5
  strategy:
    canary:
      steps:
        - setWeight: 20
        - pause: {duration: 2m}
        - setWeight: 50
        - pause: {duration: 2m}
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: placeholder:latest   # overridden by GitHub Actions
        ports:
        - containerPort: 80

Key points to remember

  • Image placeholder – Never leave latest; the SHA tag from the workflow forces a new rollout.
  • Health checks – Define an AnalysisTemplate that hits a /healthz endpoint and expects a 200 response. Without it the rollout will pause indefinitely.
  • Namespace permissions – The GitHub runner needs a kubeconfig secret with get, update, and promote rights in the target namespace.

Common Gotchas & Pro Tips

  1. SHA‑based image tags – Using the commit SHA guarantees a fresh image each run. If you revert to latest, Argo may think nothing changed and skip the rollout.
  2. Missing analysis – Argo Rollouts will only promote after the analysis step succeeds. Add a simple SuccessMetric that queries your service’s health endpoint.
  3. Kubeconfig security – Store the kubeconfig as a GitHub secret, then inject it in the workflow with kubectl config set-credentials. Keep the secret scoped to the minimum required namespace.
  4. Observability – Enable Argo’s dashboard or integrate with Prometheus to visualize canary traffic percentages and automatic rollbacks in real time.

Wrap‑Up: From Manual to Zero‑Touch Deploys

Setting up this automated canary deployment pipeline takes a couple of afternoons, but the payoff is immediate: pushes to main trigger a safe, monitored rollout, and any failure triggers an instant rollback. No more midnight alerts, no more manual kubectl commands.

If this guide saved you time, consider subscribing to the [Blog Name] newsletter for more DevOps shortcuts. Share the post with teammates still doing manual canaries, and start shipping features with confidence.

Reactions
Do you have any feedback or ideas on how we can improve this page?