DEV Community

Cover image for From GitHub Actions to ArgoCD: A Complete GitOps Migration Guide
varun varde
varun varde

Posted on

From GitHub Actions to ArgoCD: A Complete GitOps Migration Guide

I've done this migration three times now. Each time, I learn something new about what NOT to do. This guide is the documentation I wish I'd had the first time: the complete migration from a multi-stage GitHub Actions deployment pipeline to a fully declarative GitOps workflow with ArgoCD including the parts every other tutorial glosses over.

What you'll build: Production ready ArgoCD installation, ApplicationSet for multi cluster management, Image Updater for automated rollouts, RBAC configuration for multi-team environments, Rollback procedures that work under pressure.

Prerequisites: Kubernetes cluster (1.24+), kubectl, Helm 3, existing GitHub Actions CI pipeline. Estimated time: 4-6 hours for learning setup (30 days for production migration).

Phase 1: Assessment — Mapping Your Existing GitHub Actions Pipeline

Every successful migration begins with clarity. Before introducing GitOps, the existing CI/CD topology must be dissected—methodically, not casually.

Start by inventorying workflows

# .github/workflows/deploy.yml
name: Deploy Service

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: docker build -t myapp .

  deploy:
    needs: build
    steps:
      - run: kubectl apply -f k8s/
Enter fullscreen mode Exit fullscreen mode

This pipeline reveals implicit assumptions:

  • Deployment is tightly coupled with CI

  • Kubernetes manifests are applied imperatively

  • No state reconciliation exists

Map each component

Component Purpose
Build Step Artifact creation
Deploy Step Cluster mutation
Secrets Runtime configuration

The goal is not replication but transformation. GitOps demands declarative intent, not procedural execution.

Phase 2: ArgoCD Installation (Production-Grade Helm Values)

A default installation is rarely sufficient. Production demands rigor secure ingress, RBAC, and resource constraints.

Install ArgoCD using Helm

helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd -n argocd --create-namespace -f values.yaml
Enter fullscreen mode Exit fullscreen mode

A hardened values.yaml might resemble

server:
  service:
    type: ClusterIP

  ingress:
    enabled: true
    hosts:
      - argocd.example.com

configs:
  params:
    server.insecure: false

  cm:
    url: https://argocd.example.com

redis:
  enabled: true

controller:
  resources:
    limits:
      cpu: 500m
      memory: 512Mi
Enter fullscreen mode Exit fullscreen mode

Security is not ornamental. It is foundational. Misconfigured access at this stage invites future instability.

Phase 3: Structuring Your GitOps Repository

Git becomes the single source of truth. Structure, therefore, becomes paramount.

A canonical layout

gitops-repo/
├── apps/
│   ├── payments/
│   ├── auth/
├── environments/
│   ├── dev/
│   ├── staging/
│   └── prod/
└── infrastructure/
    ├── ingress/
    └── monitoring/
Enter fullscreen mode Exit fullscreen mode

Each directory encapsulates intent.

Example application manifest

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: payments-service
spec:
  source:
    repoURL: https://github.com/org/gitops-repo
    path: apps/payments
    targetRevision: HEAD
  destination:
    server: https://kubernetes.default.svc
    namespace: payments
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
Enter fullscreen mode Exit fullscreen mode

Declarative. Predictable. Auditable.

Phase 4: ApplicationSet Configuration for Multi-Team

At scale, manually defining applications becomes untenable. ApplicationSet introduces dynamic orchestration.

A Git generator example

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: team-apps
spec:
  generators:
    - git:
        repoURL: https://github.com/org/gitops-repo
        revision: HEAD
        directories:
          - path: apps/*
  template:
    metadata:
      name: '{{path.basename}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/org/gitops-repo
        targetRevision: HEAD
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{path.basename}}'
Enter fullscreen mode Exit fullscreen mode

Teams simply add a folder. The system reacts autonomously.

This is where GitOps begins to feel almost sentient responsive, adaptive, yet deterministic.

Phase 5: Migrating Your First Service (with Rollback Plan)

Start small. Select a non-critical service. Minimize risk while maximizing insight.

Step 1: Remove Deploy Step from CI

# Keep build, remove kubectl apply
- run: docker build -t myapp .
Enter fullscreen mode Exit fullscreen mode

Step 2: Push Kubernetes Manifests to GitOps Repo

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 2
Enter fullscreen mode Exit fullscreen mode

Step 3: Let ArgoCD Sync

ArgoCD reconciles desired state with actual state.

Rollback Strategy

Rollback becomes trivial

git revert <commit-hash>
git push origin main
Enter fullscreen mode Exit fullscreen mode

No manual intervention. No frantic patching. Just version control.

Phase 6: Image Updater for CI Integration

CI still builds images. But deployment is decoupled.

ArgoCD Image Updater bridges the gap

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  annotations:
    argocd-image-updater.argoproj.io/image-list: myapp=repo/myapp
    argocd-image-updater.argoproj.io/update-strategy: latest
Enter fullscreen mode Exit fullscreen mode

Pipeline pushes image

docker push repo/myapp:1.2.3
Enter fullscreen mode Exit fullscreen mode

Image Updater modifies Git

image: repo/myapp:1.2.3
Enter fullscreen mode Exit fullscreen mode

ArgoCD syncs automatically.

Elegant. Autonomous.

Phase 7: RBAC and Multi-Tenant Configuration

As teams proliferate, governance becomes essential.

Define roles

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
data:
  policy.csv: |
    p, role:dev, applications, get, */*, allow
    p, role:dev, applications, sync, */*, allow
    g, team-dev, role:dev
Enter fullscreen mode Exit fullscreen mode

Namespace isolation

spec:
  destination:
    namespace: team-a
Enter fullscreen mode Exit fullscreen mode

Each team operates within boundaries autonomous yet controlled.

Phase 8: Observability for Your GitOps Pipelines

Visibility transforms guesswork into certainty.

Enable metrics

controller:
  metrics:
    enabled: true
Enter fullscreen mode Exit fullscreen mode

Scrape with Prometheus

- job_name: 'argocd'
  static_configs:
    - targets: ['argocd-metrics:8082']
Enter fullscreen mode Exit fullscreen mode

Key metrics

  • Sync status

  • Reconciliation duration

  • Drift detection

Logs provide narrative

kubectl logs deployment/argocd-application-controller
Enter fullscreen mode Exit fullscreen mode

Without observability, GitOps becomes a black box. With it, a glass box.

The 5 Things No Tutorial Mentions (Learned the Hard Way)

1. Git Becomes Your Bottleneck

Frequent commits can overwhelm repositories. Optimize branching strategies. Avoid unnecessary churn.

2. Drift Happens More Than Expected

Manual changes in clusters still occur. ArgoCD corrects them—but not always instantly. Expect transient inconsistencies.

3. Secrets Are a Persistent Challenge

Plain YAML is insufficient. Integrate tools like sealed secrets or external secret managers.

4. ApplicationSet Can Spiral into Complexity

Dynamic generation is powerful—but dangerous. Poor templates create cascading misconfigurations.

5. Human Behavior Is the Hardest Problem

Engineers accustomed to imperative deployments resist change. Training is not optional. It is essential.

Migrating to ArgoCD is not merely a tooling shift. It is a philosophical transition—from imperative control to declarative intent.

The rewards are substantial. Stability. Traceability. Confidence.

But the journey demands discipline. And a willingness to relinquish old habits in favor of something far more resilient.

Top comments (0)