Skip to main content
This guide shows you how to build a CI/CD pipeline with GitHub Actions that automatically deploys your applications to Kubernetes when you push code. Your workflow builds container images and updates the GitOps repository. Ankra handles the rest.

Architecture Overview

The flow works like this:
  1. You push code to your GitHub application repository
  2. GitHub Actions builds and pushes a container image to your registry
  3. GitHub Actions updates the GitOps repo with the new image tag
  4. Ankra detects the change and triggers a deployment
  5. Ankra Agent deploys the updated manifest to your cluster

What You’ll Build

A complete CI/CD pipeline with:
ComponentPurpose
Application RepoYour app code with Dockerfile and .github/workflows/deploy.yml
GitOps RepoKubernetes manifests managed by Ankra
Container RegistryStores your built images (GHCR, GCR, ECR, Docker Hub, etc.)
GitHub ActionsBuilds images and updates the GitOps repo

Prerequisites

  • A cluster imported into Ankra with the agent connected
  • A container registry (GitHub Container Registry, Google Artifact Registry, AWS ECR, Docker Hub, etc.)
  • An application repository on GitHub with a Dockerfile

Step 1: Connect a GitHub Repository

First, connect a GitHub repository to your cluster. This enables GitOps and installs the necessary components on your cluster.
1

Navigate to Integration Settings

Go to your cluster → SettingsIntegration tab.
2

Add a GitHub Credential

If you haven’t connected GitHub yet, you’ll see a prompt to connect a repository.Select an existing GitHub credential from the dropdown, or click to add a new one. This authorizes Ankra to access your repositories.
3

Select a Repository

Choose the repository that will store your GitOps configuration. This can be an existing repo or a new one.
We recommend creating a dedicated repository (e.g., infrastructure-gitops) to keep your cluster configurations separate from application code.
4

Confirm Installation

When you connect for the first time, Ankra will install:
  • ArgoCD - GitOps continuous delivery
  • Ankra Stack Builder - Declarative infrastructure management
  • Ankra Resource Engine - Intelligent resource orchestration
  • GitOps Monitoring - Continuous deployment from your repository
Click Install ArgoCD & Connect to proceed.
Once connected, Ankra will create the repository structure and begin syncing your cluster configuration.

Step 2: Create a Stack for Your Application

In Ankra, manifests are organized into Stacks. A Stack is a collection of related Kubernetes resources that are deployed together.
1

Open the Stacks Page

Navigate to your cluster → Stacks.
2

Create a New Stack

Click Create to open the Stack Builder.
3

Name Your Stack

Give your stack a descriptive name, like backend-services or production-apps.
4

Add a Manifest Using AI

Press ⌘+J (or Ctrl+J) to open the AI Assistant and describe your deployment:
Create a deployment manifest for my backend service:
- Image: ghcr.io/my-org/backend:latest
- Namespace: production
- 2 replicas
- Port 8080
- Health check on /health
- 256Mi memory, 100m CPU requests
The AI will provide a manifest you can add to your stack.
5

Create the Stack

Review your configuration in the Builder tab, then click Create Stack.Ankra will commit the manifests to your GitOps repository and deploy them to your cluster.
You can view your stack’s manifests in the GitOps repository under clusters/{cluster-name}/manifests/.

Step 3: Set Up CI Deploy Key

Your GitHub Actions workflow needs write access to the GitOps repository to update image tags when new builds complete.
1

Generate an SSH Key

On your local machine, generate a deploy key:
ssh-keygen -t ed25519 -C "ci-deploy-key" -f deploy_key -N ""
This creates deploy_key (private) and deploy_key.pub (public).
2

Add Public Key to GitOps Repo

Go to your GitOps repository on GitHub → SettingsDeploy keysAdd deploy key.Paste the contents of deploy_key.pub and check Allow write access.
3

Add Private Key to App Repo

Go to your application repository → SettingsSecrets and variablesActions.Create a new repository secret called GITOPS_DEPLOY_KEY with the contents of deploy_key.

Step 4: Create the GitHub Actions Workflow

Add a GitHub Actions workflow to your application repository that builds your container and updates the GitOps repo.
1

Create the Workflow File

In your application repository, create .github/workflows/deploy.yml.
2

Use the AI to Generate the Workflow

Open the AI Assistant (⌘+J) and describe your pipeline:
Generate a GitHub Actions workflow that:
- Triggers on push to main branch
- Builds a Docker image from my Dockerfile
- Pushes to GitHub Container Registry
- Tags with the git SHA
- Updates my GitOps repo at github.com/my-org/infrastructure-gitops
- Updates the image tag in clusters/my-cluster/manifests/backend-deployment.yaml
The AI will generate a complete workflow tailored to your setup.
3

Add Registry Secrets

Add these secrets to your application repository under SettingsSecrets and variablesActions:
SecretDescription
GITOPS_DEPLOY_KEYThe SSH private key from Step 3
GitHub Container Registry (GHCR) is available by default in GitHub Actions via the GITHUB_TOKEN — no extra registry secrets needed. For external registries (GCR, ECR, Docker Hub), add the appropriate credentials as secrets.
Here’s a workflow using GitHub’s built-in container registry. Use the AI to customize it for your setup:
name: Build and Deploy

on:
  push:
    branches: [main]
    paths:
      - 'src/**'
      - 'Dockerfile'
  workflow_dispatch:

env:
  IMAGE_NAME: ghcr.io/${{ github.repository }}/backend
  GITOPS_REPO: git@github.com:my-org/infrastructure-gitops.git
  MANIFEST_PATH: clusters/my-cluster/manifests/backend-deployment.yaml

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-buildx-action@v3

      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ${{ env.IMAGE_NAME }}:${{ github.sha }}
            ${{ env.IMAGE_NAME }}:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

  update-gitops:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: ${{ secrets.GITOPS_DEPLOY_KEY }}

      - name: Update GitOps repo
        run: |
          git clone ${{ env.GITOPS_REPO }}
          cd infrastructure-gitops

          git config user.name "GitHub Actions"
          git config user.email "github-actions[bot]@users.noreply.github.com"

          sed -i "s|image: .*backend:.*|image: ${{ env.IMAGE_NAME }}:${{ github.sha }}|" "${{ env.MANIFEST_PATH }}"

          git add "${{ env.MANIFEST_PATH }}"
          git commit -m "Deploy backend: ${{ github.sha }}"
          git push origin main
Here’s a workflow pushing to Google Artifact Registry:
name: Build and Deploy

on:
  push:
    branches: [main]
    paths:
      - 'src/**'
      - 'Dockerfile'
  workflow_dispatch:

env:
  IMAGE_NAME: europe-west1-docker.pkg.dev/my-project/docker-images/backend
  GITOPS_REPO: git@github.com:my-org/infrastructure-gitops.git
  MANIFEST_PATH: clusters/my-cluster/manifests/backend-deployment.yaml

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-buildx-action@v3

      - uses: docker/login-action@v3
        with:
          registry: europe-west1-docker.pkg.dev
          username: _json_key
          password: ${{ secrets.GCP_SERVICE_ACCOUNT_KEY }}

      - uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ env.IMAGE_NAME }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  update-gitops:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: ${{ secrets.GITOPS_DEPLOY_KEY }}

      - name: Update GitOps repo
        run: |
          git clone ${{ env.GITOPS_REPO }}
          cd infrastructure-gitops

          git config user.name "GitHub Actions"
          git config user.email "github-actions[bot]@users.noreply.github.com"

          sed -i "s|image: .*backend:.*|image: ${{ env.IMAGE_NAME }}:${{ github.sha }}|" "${{ env.MANIFEST_PATH }}"

          git add "${{ env.MANIFEST_PATH }}"
          git commit -m "Deploy backend: ${{ github.sha }}"
          git push origin main
Here’s a workflow pushing to Amazon Elastic Container Registry:
name: Build and Deploy

on:
  push:
    branches: [main]
    paths:
      - 'src/**'
      - 'Dockerfile'
  workflow_dispatch:

env:
  IMAGE_NAME: 123456789012.dkr.ecr.us-east-1.amazonaws.com/backend
  GITOPS_REPO: git@github.com:my-org/infrastructure-gitops.git
  MANIFEST_PATH: clusters/my-cluster/manifests/backend-deployment.yaml

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - uses: aws-actions/amazon-ecr-login@v2

      - uses: docker/setup-buildx-action@v3

      - uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ env.IMAGE_NAME }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  update-gitops:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: ${{ secrets.GITOPS_DEPLOY_KEY }}

      - name: Update GitOps repo
        run: |
          git clone ${{ env.GITOPS_REPO }}
          cd infrastructure-gitops

          git config user.name "GitHub Actions"
          git config user.email "github-actions[bot]@users.noreply.github.com"

          sed -i "s|image: .*backend:.*|image: ${{ env.IMAGE_NAME }}:${{ github.sha }}|" "${{ env.MANIFEST_PATH }}"

          git add "${{ env.MANIFEST_PATH }}"
          git commit -m "Deploy backend: ${{ github.sha }}"
          git push origin main

Step 5: Configure Registry Access in Your Cluster

If your container registry is private, your cluster needs credentials to pull images.
1

Open the AI Assistant

Press ⌘+J to open the AI Assistant.
2

Ask for an Image Pull Secret

Create an image pull secret for my private registry:
- Registry: ghcr.io
- Namespace: production
- Name: ghcr-pull-secret
3

Add the Secret Value

The AI will provide a Secret manifest template. You’ll need to provide your registry credentials:
  • For GHCR: Use a Personal Access Token with read:packages scope
  • For GCP: Use a service account JSON key with Artifact Registry Reader role
  • For AWS ECR: Use an IAM access key
  • For Docker Hub: Use your username and access token
4

Link to Your Deployment

Ask the AI to update your deployment to use the pull secret:
Update my backend deployment to use the ghcr-pull-secret for pulling images
Use SOPS encryption to safely store registry credentials in your GitOps repository.

Step 6: Test the Pipeline

1

Push a Code Change

Make a change to your application code and push to main:
git add .
git commit -m "Add new feature"
git push origin main
2

Monitor CI Progress

Go to the Actions tab in your application repository to watch the workflow run.
3

Verify GitOps Update

After the workflow completes, check your GitOps repository. You should see a new commit updating the image tag.
4

Watch the Deployment

In Ankra, go to your cluster → Operations to see the deployment in progress. The new image will roll out automatically.

Step 7: Monitor GitOps Sync Status

Ankra provides visibility into your GitOps sync status.
1

View GitOps Status

Navigate to your cluster → GitOps to see:
  • Current sync status
  • Recent sync history
  • Any sync errors
2

Trigger Manual Sync

If needed, click Sync to manually trigger a sync from your repository.
3

View in Operations

Check the Operations page for detailed deployment history and job status.

Adding More Services

To add CI/CD for additional services, use the AI Assistant to scaffold everything:
1

Add to Existing Stack or Create New

Either edit your existing stack or create a new one for the service.
2

Generate the Deployment Manifest

Open the AI Assistant (⌘+J) and describe your service:
Add a deployment for my frontend service:
- Image: ghcr.io/my-org/frontend
- Namespace: production
- 3 replicas
- Port 3000
- Expose via a Service on port 80
3

Generate the CI Workflow

In your frontend app repo, create .github/workflows/deploy.yml or ask the AI:
Generate a GitHub Actions workflow to build and deploy my frontend:
- Build from ./frontend/Dockerfile
- Push to GitHub Container Registry
- Update clusters/my-cluster/manifests/frontend-deployment.yaml in my GitOps repo
4

Add the Secrets

Copy the same secrets (GITOPS_DEPLOY_KEY, registry credentials) to the new repository.

Common AI Prompts

Use these prompts with the AI Assistant (⌘+J) to set up your CI/CD:
Create a deployment manifest for my backend service:
- Image: ghcr.io/my-org/backend:latest
- Namespace: production
- 2 replicas with rolling update strategy
- Port 8080
- Health checks on /health and /ready
- Resource requests: 256Mi memory, 100m CPU
- Resource limits: 512Mi memory, 500m CPU
- Environment variables from a ConfigMap called backend-config
Set up a complete service stack for my API:
- Deployment with 3 replicas
- Service exposing port 80
- Ingress with TLS using cert-manager
- HorizontalPodAutoscaler scaling 2-10 replicas at 70% CPU
- PodDisruptionBudget allowing 1 unavailable
Create a docker registry secret for pulling images from:
- Registry: ghcr.io
- Namespace: production

Then update my backend deployment to use this secret.
Generate a GitHub Actions workflow that:
- Builds my Docker image on push to main
- Pushes to GitHub Container Registry
- Updates clusters/prod/manifests/app-deployment.yaml in my GitOps repo
- Only builds when files in src/ or Dockerfile change
Modify my deployment to support canary releases:
- Create a canary deployment with 1 replica
- Use labels to route 10% of traffic to canary
- Add a Service that selects both stable and canary pods
My backend deployment isn't updating after CI pushed a new image.
Help me troubleshoot why the pods aren't rolling out.
The AI Assistant has full context of your cluster. It can see your existing resources, logs, and events. Describe what you want to achieve and it will generate the right configuration.

Best Practices

Always use unique, immutable tags like ${{ github.sha }} or ${{ github.run_number }}. Avoid relying solely on latest.Ask the AI: “Ensure my deployment uses immutable image tags and imagePullPolicy IfNotPresent”
GitHub Actions provides GITHUB_TOKEN automatically — no need to create a Personal Access Token for pushing to GHCR. Just add packages: write to your job permissions.
Use cache-from: type=gha and cache-to: type=gha,mode=max with docker/build-push-action to speed up builds using GitHub’s built-in Actions cache.
Let the AI configure proper health checks for your deployments.Ask the AI: “Add appropriate liveness and readiness probes to my backend deployment for a Node.js app”
Prevent runaway resource usage with proper limits.Ask the AI: “Review my deployment and suggest appropriate resource requests and limits based on a typical web API”
Encrypt sensitive values in your GitOps repository.Ask the AI: “Help me encrypt my database password using SOPS”
For production deployments, set up GitHub Environments with required reviewers and branch restrictions to gate deployments.

Troubleshooting

Having issues? Open the AI Assistant (⌘+J) and describe your problem:
Ask the AI:
My GitHub Actions workflow is failing to push to the GitOps repo with a permission denied error.
Help me troubleshoot the deploy key setup.
Common causes:
  • Deploy key doesn’t have write access on the GitOps repo
  • GITOPS_DEPLOY_KEY secret is not set or has extra whitespace
  • The webfactory/ssh-agent action is missing from the workflow
Ask the AI:
I pushed a new image tag to my GitOps repo but the pods aren't updating.
What could be wrong?
Ask the AI:
My pods are stuck in ImagePullBackOff. Help me fix the registry authentication.
For GHCR, ensure your image pull secret uses a PAT with read:packages scope, or make the package public in your repository’s package settings.
If you see permission denied errors when pushing to GHCR, ensure your workflow has the correct permissions:
permissions:
  contents: read
  packages: write
Also check that SettingsActionsGeneralWorkflow permissions is set to Read and write permissions.
Ask the AI:
My deployment rollout is stuck. Show me what's blocking it and how to fix it.
Check the GitOps page in your cluster to see sync status. If sync isn’t triggering:
  • Verify the GitHub repository is still connected in SettingsIntegration
  • Check that your commit was pushed to the correct branch
  • Look for webhook configuration issues in the GitOps status
The AI has access to your pod logs, events, and deployment status. It can pinpoint exactly what’s going wrong and suggest fixes.

Next Steps

GitLab CI/CD Pipeline

See the equivalent guide for GitLab CI.

GitOps Reference

Learn more about GitOps file formats and include paths.

SOPS Encryption

Encrypt secrets in your GitOps repository.

Operations

Monitor deployment progress and history.