GitOps makes Kubernetes deployments declarative and auditable: the desired cluster state lives in Git, and automated tools reconcile the cluster to match. Claude Code generates ArgoCD Application manifests, Flux Kustomizations, multi-environment promotion workflows, and the supporting tooling for secrets management and progressive delivery.
ArgoCD Application Setup
Set up ArgoCD to manage our application across dev, staging, and production.
Each environment is a separate namespace. Auto-sync dev, manual approval for production.
App of Apps Pattern
# argocd/applications/app-of-apps.yaml
# The "root" app that manages all other apps
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: app-of-apps
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/your-org/infra-repo
targetRevision: main
path: argocd/applications # This directory contains all child apps
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
# argocd/applications/api-dev.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: api-dev
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/infra-repo
targetRevision: main
path: environments/dev
destination:
server: https://kubernetes.default.svc
namespace: api-dev
syncPolicy:
automated: # Auto-sync dev
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
revisionHistoryLimit: 5
# argocd/applications/api-production.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: api-production
namespace: argocd
spec:
project: production # Restricted project
source:
repoURL: https://github.com/your-org/infra-repo
targetRevision: main
path: environments/production
destination:
server: https://kubernetes.default.svc
namespace: api-production
syncPolicy:
# NO automated sync — requires manual approval
syncOptions:
- CreateNamespace=true
# Notification on sync result
info:
- name: Slack Channel
value: '#deploys'
Kustomize Overlay Structure
Generate a Kustomize structure for dev/staging/production overlays.
Production gets 3 replicas, dev gets 1. Production has resource limits.
k8s/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── configmap.yaml
└── overlays/
├── dev/
│ └── kustomization.yaml
├── staging/
│ └── kustomization.yaml
└── production/
├── kustomization.yaml
└── patches/
├── replicas.yaml
└── resources.yaml
# k8s/base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 1
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: your-registry/api:latest # ArgoCD will track image updates
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: api-config
- secretRef:
name: api-secrets
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
# k8s/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: api-production
resources:
- ../../base
patches:
- path: patches/replicas.yaml
- path: patches/resources.yaml
images:
- name: your-registry/api
newTag: "1.5.2" # Pinned version in production — updated by automation
# k8s/overlays/production/patches/replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
# k8s/overlays/production/patches/resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
template:
spec:
containers:
- name: api
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1000m"
memory: "512Mi"
Automated Image Promotion
When a new Docker image is pushed, automatically update the dev overlay's
image tag and create a PR to promote it to production.
# .github/workflows/promote.yml
name: Promote image
on:
registry_package:
types: [published]
jobs:
update-dev:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.INFRA_REPO_TOKEN }}
repository: your-org/infra-repo
- name: Update dev image tag
run: |
IMAGE_TAG=${{ github.event.registry_package.package_version.name }}
cd k8s/overlays/dev
kustomize edit set image your-registry/api:${IMAGE_TAG}
git config user.name "deploy-bot"
git config user.email "[email protected]"
git add kustomization.yaml
git commit -m "chore: update api image to ${IMAGE_TAG} in dev"
git push
promote-to-production:
runs-on: ubuntu-latest
needs: update-dev
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.INFRA_REPO_TOKEN }}
repository: your-org/infra-repo
- name: Create production promotion PR
run: |
IMAGE_TAG=${{ github.event.registry_package.package_version.name }}
BRANCH="promote/${IMAGE_TAG}"
git checkout -b $BRANCH
cd k8s/overlays/production
kustomize edit set image your-registry/api:${IMAGE_TAG}
git add kustomization.yaml
git commit -m "chore: promote api ${IMAGE_TAG} to production"
git push origin $BRANCH
gh pr create \
--title "Promote api ${IMAGE_TAG} to production" \
--body "## Changes\n- Image tag: \`${IMAGE_TAG}\`\n- Auto-generated promotion PR" \
--reviewer "@your-org/platform-team" \
--base main \
--head $BRANCH
env:
GH_TOKEN: ${{ secrets.INFRA_REPO_TOKEN }}
When the PR is merged into main, ArgoCD detects the change and prompts for manual sync approval in the production ArgoCD app.
Secrets Management with Sealed Secrets
Store encrypted secrets in Git. Use Sealed Secrets so secrets
can be committed safely but only decrypted in the cluster.
# Install the kubeseal CLI
brew install kubeseal
# Get the cluster's public key (one-time setup)
kubeseal --fetch-cert \
--controller-name=sealed-secrets-controller \
--controller-namespace=sealed-secrets \
> sealed-secrets-public-cert.pem
# Create a secret and seal it for a specific namespace
kubectl create secret generic api-secrets \
--from-literal=DATABASE_URL="postgres://..." \
--from-literal=JWT_SECRET="your-secret" \
--dry-run=client -o yaml | \
kubeseal \
--cert sealed-secrets-public-cert.pem \
--scope namespace-wide \
-o yaml > k8s/overlays/production/sealed-secret.yaml
# Commit sealed-secret.yaml to Git — safe to store in version control
# The cluster controller decrypts it using its private key
# k8s/overlays/production/sealed-secret.yaml — committed to Git
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: api-secrets
namespace: api-production
spec:
encryptedData:
DATABASE_URL: AgBy8hF39K7Nk... # Encrypted — safe to commit
JWT_SECRET: AgDF93km2...
Argo Rollouts — Progressive Delivery
Add a canary deployment strategy. Send 10% of traffic to the new version,
analyze metrics, then progressively roll out if healthy.
# k8s/base/rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: api
spec:
replicas: 10
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: your-registry/api:latest
ports:
- containerPort: 3000
strategy:
canary:
canaryService: api-canary
stableService: api-stable
trafficRouting:
nginx:
stableIngress: api-ingress
steps:
- setWeight: 10 # 10% to canary
- pause: { duration: 5m }
- analysis: # Check metrics before proceeding
templates:
- templateName: success-rate
- setWeight: 30
- pause: { duration: 5m }
- setWeight: 60
- pause: { duration: 5m }
- setWeight: 100
---
# AnalysisTemplate — define what "healthy" means
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
metrics:
- name: success-rate
interval: 1m
successCondition: result[0] >= 0.95 # 95% success rate required
failureLimit: 3
provider:
prometheus:
address: http://prometheus:9090
query: |
sum(
rate(http_requests_total{
service="api-canary",
status!~"5.."
}[5m])
) /
sum(
rate(http_requests_total{
service="api-canary"
}[5m])
)
If success rate drops below 95%, Argo Rollouts automatically aborts and routes all traffic back to the stable version.
For the Kubernetes infrastructure that GitOps manages, see the Kubernetes guide for cluster setup and Helm chart patterns. For the CI/CD pipelines that build images and trigger promotions, see the GitHub Actions guide. The Claude Skills 360 bundle includes GitOps skill sets for ArgoCD, Flux, and progressive delivery patterns. Start with the free tier to try Kustomize overlay generation.