Skip to content

GitOps Basics — Declarative Deployments

DodaTech Updated 2026-06-24 8 min read

In this tutorial, you'll learn about GitOps Basics. We cover key concepts, practical examples, and best practices.

GitOps is an operational framework that uses Git as the single source of truth for declarative infrastructure and application deployments, with automated reconciliation ensuring the live state matches the repository.

In this tutorial, you'll learn GitOps basics — the core principles of declarative configuration, automated reconciliation, pull-based deployments, and how to implement GitOps with Kubernetes and popular tools like Argo CD and Flux. GitOps improves deployment reliability, auditability, and developer experience by applying Git workflows to operations. By the end, you'll set up a GitOps pipeline for a sample application.

Real-world use: DodaTech uses GitOps to manage all production infrastructure across Doda Browser, DodaZIP, and Durga Antivirus Pro's backend services. Every change goes through a pull request, review, and automated sync.

flowchart TD
  A[Developer pushes change] --> B[Git Repository]
  B --> C{Pull Request}
  C --> D[Review & Approve]
  D --> E[Merge to main]
  E --> F[GitOps Operator]
  F --> G[Compare state]
  G --> H{Desired == Actual?}
  H -->|Yes| I[No action]
  H -->|No| J[Apply changes]
  J --> K[Update cluster state]
  K --> L[Report sync status]
  L --> B

Core GitOps Principles

GitOps is built on four fundamental principles.

## 1. Declarative Configuration
The entire system is described declaratively in configuration files.
No imperative scripts or manual steps. What's in the repository is
what should be running.

## 2. Git as Single Source of Truth
Git is the authoritative source for all configuration. There is no
configuration outside of Git. If it's not in the repository, it
doesn't exist in production.

## 3. Automated Reconciliation
An operator continuously compares the desired state (Git) with the
actual state (production) and corrects any drift automatically.

## 4. Pull-Based Deployments
The cluster pulls from the repository, not the other way around.
This eliminates the need for CI/CD to have cluster credentials,
improving security.

Declarative Configuration

Define your application declaratively using Kubernetes manifests.

# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dodatech-api
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: dodatech-api
  template:
    metadata:
      labels:
        app: dodatech-api
    spec:
      containers:
      - name: api
        image: dodatech/api:v3.2.0
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
        resources:
          limits:
            memory: "512Mi"
            cpu: "500m"

---
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: dodatech-api
spec:
  selector:
    app: dodatech-api
  ports:
  - port: 443
    targetPort: 8080
  type: LoadBalancer

Setting Up Argo CD

Argo CD is the most popular GitOps operator for Kubernetes.

# Install Argo CD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Get the initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d

# Port forward to access the UI
kubectl port-forward svc/argocd-server -n argocd 8080:443

# Login via CLI
argocd login localhost:8080 --username admin

Registering an Application

Connect Argo CD to your Git repository.

# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: dodatech-api
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/dodatech/infrastructure.git
    targetRevision: main
    path: k8s/production/api
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
# Register the application
kubectl apply -f application.yaml

# Check sync status
argocd app get dodatech-api

# Sync manually (if auto-sync is off)
argocd app sync dodatech-api

Expected output:

$ argocd app get dodatech-api
Name:               dodatech-api
Project:            default
Server:             https://kubernetes.default.svc
Namespace:          production
URL:                https://localhost:8080/applications/dodatech-api
Repo:               https://github.com/dodatech/infrastructure.git
Target:             main
Path:               k8s/production/api
Sync Policy:        Automated (Prune, Self Heal)
Sync Status:        Synced
Health Status:      Healthy

GitOps Workflow

A complete deployment cycle using GitOps.

# 1. Developer updates the image tag
echo 'dodatech/api:v3.3.0' > k8s/production/api/version.txt
sed -i 's|image: dodatech/api:v3.2.0|image: dodatech/api:v3.3.0|' \
  k8s/production/api/deployment.yaml

# 2. Create a pull request
git checkout -b release-v3.3.0
git add k8s/production/api/
git commit -m "Release v3.3.0: update API image to v3.3.0"
git push origin release-v3.3.0
# Create PR on GitHub → team reviews → merge to main

# 3. Argo CD detects the change (auto-sync enabled)
# It automatically applies the new deployment
# The rollout happens with zero downtime (if configured)

# 4. Monitor the rollout
argocd app wait dodatech-api --health

# 5. Rollback if needed (revert the commit)
git revert HEAD
git push origin main
# Argo CD reverts automatically

Using Kustomize with GitOps

Kustomize overlays are a natural fit for GitOps workflows.

# k8s/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml

images:
  - name: dodatech/api
    newTag: v3.2.0
# k8s/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

namespace: production

replicas:
  - name: dodatech-api
    count: 5

patches:
  - target:
      kind: Deployment
    patch: |-
      - op: add
        path: /spec/template/spec/containers/0/env
        value:
          - name: LOG_LEVEL
            value: info

Argo CD automatically resolves the Kustomize overlay:

# application-gitops.yaml
spec:
  source:
    repoURL: https://github.com/dodatech/infrastructure.git
    path: k8s/overlays/production  # Point to the overlay

Flux CD Alternative

Flux is another popular GitOps operator with a different approach.

# Install Flux CLI
curl -s https://fluxcd.io/install.sh | sudo bash

# Bootstrap Flux
flux bootstrap github \
  --owner=dodatech \
  --repository=infrastructure \
  --branch=main \
  --path=./clusters/production

# Add a source (where config lives)
flux create source git dodatech-api \
  --url=https://github.com/dodatech/infrastructure \
  --branch=main

# Create a kustomization
flux create kustomization dodatech-api \
  --source=dodatech-api \
  --path="./k8s/production/api" \
  --prune=true \
  --interval=5m

Drift Detection and Remediation

GitOps operators continuously detect and fix configuration drift.

# Simulate drift (manual kubectl edit)
kubectl edit deployment dodatech-api -n production
# Change replicas from 3 to 2

# Argo CD detects the drift and self-heals
$ argocd app get dodatech-api
...
Sync Status:        OutOfSync  # ← detected drift

# Within seconds, Argo CD reverts back to 3 replicas
$ kubectl get deployment dodatech-api -n production
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
dodatech-api    3/3     3            3           5m

GitOps Security Benefits

Pull-based deployments eliminate the need for cluster credentials in CI.

# Traditional CI/CD (push-based) — cluster credentials exposed
# .github/workflows/deploy.yaml
- name: Deploy to Kubernetes
  run: |
    kubectl apply -f k8s/  # Needs KUBECONFIG secret
    # Risk: any code change can access the cluster

# GitOps (pull-based) — no credentials in CI
# .github/workflows/deploy.yaml
- name: Update config
  run: |
    git add k8s/
    git commit -m "Update configuration"
    git push
  # CI only needs Git push permission, not cluster access

Common Errors

  1. Config drift from manual kubectl commands — Anyone with cluster access can make changes that diverge from Git. Use Argo CD's self-healing to revert, and audit permissions to prevent manual changes.
  2. Sync conflicts with auto-heal — If the cluster state diverges and auto-heal is on, the operator reverts changes immediately. Turn off auto-heal during incident response to allow manual overrides.
  3. Secrets management — Kubernetes Secrets in Git are base64-encoded, not encrypted. Use Sealed Secrets, External Secrets Operator, or SOPS to encrypt secrets before committing.
  4. Cluster bootstrap loop — If the GitOps operator itself isn't declared in Git, you get a bootstrap problem. Use Flux's bootstrap command or Argo CD's App of Apps pattern.
  5. Sync waves and ordering — Resources may need to be applied in a specific order (CRDs before CRs, namespaces before resources). Use Argo CD sync waves or Flux depends-on to control ordering.

Practice Questions

What is the primary difference between GitOps and CI/CD?

CI/CD focuses on building and testing code, then pushing artifacts to an environment. GitOps focuses on the Git repository as the source of truth for the environment state. In GitOps, CI builds artifacts; a GitOps operator reconciles the live environment to match the declared state in Git.

What is pull-based deployment in GitOps?

In pull-based deployment, the cluster operator (Argo CD, Flux) continuously polls the Git repository for changes and applies them. The CI pipeline never has direct access to the cluster. This is more secure than push-based, where CI has cluster credentials.

How does GitOps handle secrets?

Secrets should never be committed in plaintext. Use Sealed Secrets (encrypted secrets decryptable only by the cluster), External Secrets Operator (syncs from AWS Secrets Manager, Vault, etc.), or Mozilla SOPS (encrypts files in Git, decrypts at sync time).

What is the difference between Argo CD and Flux?

Argo CD has a richer UI, supports multiple clusters from one instance, and offers sync waves for complex deployments. Flux is lighter, integrates natively with Kubernetes controllers, and has a simpler setup. Both implement GitOps. Choose based on team preference.

Can GitOps be used without Kubernetes?

Yes. GitOps principles apply to any declarative infrastructure: Terraform, Pulumi, AWS CloudFormation, or even application configuration. Tools like Atlantis and Terraform Cloud provide GitOps workflows for Terraform. The key is declarative config + automated reconciliation

Challenge

Set up a complete GitOps pipeline. Create a GitHub repository with Kubernetes manifests for a sample application. Install Argo CD on a local Kubernetes cluster (kind or minikube). Register the application with Argo CD, enable auto-sync and self-healing. Make a change to the manifests and verify Argo CD syncs automatically. Introduce drift manually with kubectl and confirm self-healing reverts it. Then implement the same pipeline with Flux for comparison.

Real-World Task

Migrate the DodaTech backend deployment from a push-based CI/CD pipeline to GitOps. All production infrastructure for Doda Browser and Durga Antivirus Pro APIs is defined as Kubernetes manifests in the infrastructure repository. Set up Argo CD to synchronize three clusters (staging, production-eu, production-us). Configure automated sync with self-healing, set up sync waves to ensure database migrations run before application deployments, implement Sealed Secrets for database credentials, and establish a PR review process for all infrastructure changes.


Previous: GitHub Actions | Related: Web Deployment & CI/CD | Related: Kubernetes Guide

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro