GitOps workflows and patterns using ArgoCD and Flux for declarative Kubernetes deployments. Use when implementing CI/CD for Kubernetes, managing multi-environment deployments, or adopting declarative infrastructure practices.
/plugin marketplace add NickCrew/claude-cortex/plugin install nickcrew-claude-ctx-2@NickCrew/claude-cortexThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Expert guidance for implementing production-grade GitOps workflows using ArgoCD and Flux CD, covering declarative deployment patterns, progressive delivery strategies, multi-environment management, and secure secret handling for Kubernetes infrastructure.
1. Declarative
2. Versioned and Immutable
3. Pulled Automatically
4. Continuously Reconciled
Operational advantages:
Developer advantages:
Structure:
gitops-repo/
├── apps/
│ ├── production/
│ │ ├── frontend/
│ │ │ ├── kustomization.yaml
│ │ │ ├── deployment.yaml
│ │ │ └── service.yaml
│ │ ├── backend/
│ │ └── database/
│ ├── staging/
│ └── development/
├── infrastructure/
│ ├── ingress-nginx/
│ ├── cert-manager/
│ ├── external-secrets/
│ └── monitoring/
│ ├── prometheus/
│ └── grafana/
├── clusters/
│ ├── production/
│ │ └── cluster-config.yaml
│ ├── staging/
│ └── development/
└── base/
├── apps/
└── infrastructure/
Characteristics:
Best for: Organizations with unified platform teams, shared infrastructure components, need for consistency across environments
Structure:
# Infrastructure repo
infrastructure-gitops/
├── clusters/
│ ├── production/
│ └── staging/
├── shared/
│ ├── ingress/
│ ├── monitoring/
│ └── security/
└── argocd/
└── applications/
# Application repos
app-frontend-gitops/
├── base/
│ ├── deployment.yaml
│ └── service.yaml
└── overlays/
├── production/
└── staging/
app-backend-gitops/
├── helm/
│ └── values-{env}.yaml
└── manifests/
Characteristics:
Best for: Large organizations, independent teams, microservices architectures, different compliance requirements per service
Structure:
app-gitops/
├── main (development)
├── staging (staging env)
└── production (production env)
Each branch contains:
├── manifests/
│ ├── deployment.yaml
│ └── service.yaml
└── config/
└── values.yaml
Characteristics:
Considerations:
Best for: Small teams, simple applications, teams familiar with Git flow
Standard installation:
# Install ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Access UI
kubectl port-forward svc/argocd-server -n argocd 8080:443
# Get initial password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
Production ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-ingress
namespace: argocd
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: nginx
rules:
- host: argocd.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 443
tls:
- hosts:
- argocd.example.com
secretName: argocd-tls
Basic application:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: frontend-app
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io # Enable cascading delete
spec:
project: production
source:
repoURL: https://github.com/org/gitops-repo
targetRevision: main
path: apps/production/frontend
# Optional: Helm values
helm:
valueFiles:
- values-production.yaml
parameters:
- name: image.tag
value: v1.2.3
# Optional: Kustomize
kustomize:
images:
- gcr.io/org/frontend:v1.2.3
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # Delete resources removed from Git
selfHeal: true # Reconcile manual changes
allowEmpty: false # Prevent accidental empty sync
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # Ignore HPA-managed replicas
Root application:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: applications
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/gitops-repo
targetRevision: main
path: argocd/applications
directory:
recurse: true
jsonnet: {}
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
Application structure:
argocd/
├── applications/
│ ├── infrastructure.yaml # Infrastructure apps
│ ├── production-apps.yaml # Production apps
│ └── staging-apps.yaml # Staging apps
└── projects/
├── infrastructure.yaml
├── production.yaml
└── staging.yaml
Multi-cluster deployment:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: frontend-multicluster
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: production-east
url: https://prod-east.k8s.example.com
environment: production
- cluster: production-west
url: https://prod-west.k8s.example.com
environment: production
- cluster: staging
url: https://staging.k8s.example.com
environment: staging
template:
metadata:
name: 'frontend-{{cluster}}'
spec:
project: '{{environment}}'
source:
repoURL: https://github.com/org/gitops-repo
targetRevision: main
path: 'apps/{{environment}}/frontend'
destination:
server: '{{url}}'
namespace: frontend
syncPolicy:
automated:
prune: true
selfHeal: true
Git directory generator:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: all-apps
spec:
generators:
- git:
repoURL: https://github.com/org/gitops-repo
revision: main
directories:
- path: apps/production/*
template:
metadata:
name: '{{path.basename}}'
spec:
source:
repoURL: https://github.com/org/gitops-repo
targetRevision: main
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{path.basename}}'
syncPolicy:
automated: {}
Bootstrap Flux with GitHub:
# Install Flux CLI
curl -s https://fluxcd.io/install.sh | sudo bash
# Bootstrap Flux (creates repo structure)
flux bootstrap github \
--owner=org \
--repository=gitops-repo \
--branch=main \
--path=clusters/production \
--personal=false \
--token-auth
Bootstrap result:
clusters/production/
├── flux-system/
│ ├── gotk-components.yaml
│ ├── gotk-sync.yaml
│ └── kustomization.yaml
GitRepository source:
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: frontend-app
namespace: flux-system
spec:
interval: 1m
url: https://github.com/org/frontend-app
ref:
branch: main
secretRef:
name: github-token
ignore: |
# Exclude non-deployment files
/*.md
/docs/
HelmRepository source:
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bitnami
namespace: flux-system
spec:
interval: 10m
url: https://charts.bitnami.com/bitnami
OCIRepository source:
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: OCIRepository
metadata:
name: app-manifests
namespace: flux-system
spec:
interval: 5m
url: oci://ghcr.io/org/manifests
ref:
tag: latest
Application deployment:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: frontend-app
namespace: flux-system
spec:
interval: 5m
path: ./deploy/production
prune: true
wait: true
timeout: 5m
sourceRef:
kind: GitRepository
name: frontend-app
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: frontend
namespace: production
patches:
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 5
target:
kind: Deployment
name: frontend
Dependency management:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: app-stack
namespace: flux-system
spec:
interval: 5m
path: ./apps
prune: true
sourceRef:
kind: GitRepository
name: gitops-repo
# Wait for infrastructure
dependsOn:
- name: infrastructure
- name: databases
Deploying Helm charts:
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: nginx-ingress
namespace: flux-system
spec:
interval: 10m
chart:
spec:
chart: ingress-nginx
version: '4.8.x'
sourceRef:
kind: HelmRepository
name: ingress-nginx
namespace: flux-system
values:
controller:
replicaCount: 3
metrics:
enabled: true
# Override values from ConfigMap
valuesFrom:
- kind: ConfigMap
name: nginx-values
valuesKey: values.yaml
Branch-based flow:
# Promote staging to production
git checkout main
git merge staging --no-ff -m "Promote staging to production"
git push origin main
# ArgoCD/Flux automatically sync production
Tag-based flow:
# Development tracks main
source:
targetRevision: main
# Staging tracks release candidates
source:
targetRevision: v1.2.3-rc1
# Production tracks stable releases
source:
targetRevision: v1.2.3
Implementation:
# Create release candidate
git tag v1.2.3-rc1
git push origin v1.2.3-rc1
# After validation, promote to production
git tag v1.2.3
git push origin v1.2.3
Base configuration:
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 3
template:
spec:
containers:
- name: app
image: app:latest
Environment overlays:
# overlays/staging/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
images:
- name: app
newTag: v1.2.3-staging
patches:
- patch: |-
- op: replace
path: /spec/replicas
value: 2
target:
kind: Deployment
# overlays/production/kustomization.yaml
images:
- name: app
newTag: v1.2.3 # Promote by updating tag
patches:
- patch: |-
- op: replace
path: /spec/replicas
value: 5
ImageRepository:
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageRepository
metadata:
name: frontend
namespace: flux-system
spec:
image: gcr.io/org/frontend
interval: 1m
ImagePolicy:
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImagePolicy
metadata:
name: frontend-policy
namespace: flux-system
spec:
imageRepositoryRef:
name: frontend
policy:
semver:
range: 1.x.x # Only stable 1.x releases
ImageUpdateAutomation:
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: frontend-automation
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: gitops-repo
git:
checkout:
ref:
branch: main
commit:
author:
email: fluxcd@example.com
name: Flux CD
messageTemplate: |
Update {{range .Updated.Images}}{{println .}}{{end}}
push:
branch: main
update:
path: ./apps/production
strategy: Setters
Install controller:
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml
# Install kubeseal CLI
brew install kubeseal
Encrypt secrets:
# Create regular secret
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=supersecret \
--dry-run=client -o yaml > secret.yaml
# Encrypt for cluster
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
# Commit sealed-secret.yaml to Git
git add sealed-secret.yaml
git commit -m "Add encrypted database credentials"
SealedSecret manifest:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: production
spec:
encryptedData:
username: AgBj7V8X...
password: AgCK9Qw2...
template:
metadata:
name: db-credentials
namespace: production
type: Opaque
Install ESO:
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets -n external-secrets-system --create-namespace
SecretStore (AWS Secrets Manager):
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets-manager
namespace: production
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
ExternalSecret:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: db-credentials
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: production/database/credentials
property: username
- secretKey: password
remoteRef:
key: production/database/credentials
property: password
ClusterSecretStore (shared):
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "external-secrets"
Setup (with age encryption):
# Install SOPS
brew install sops
# Install age
brew install age
# Generate age key
age-keygen -o key.txt
# Configure .sops.yaml
cat <<EOF > .sops.yaml
creation_rules:
- path_regex: .*/production/.*
age: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
EOF
Encrypt secrets:
# Create secret
cat <<EOF > secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
stringData:
username: admin
password: supersecret
EOF
# Encrypt with SOPS
sops --encrypt --in-place secret.yaml
# Commit encrypted file
git add secret.yaml
Flux integration:
# Kustomization with SOPS
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: app
spec:
decryption:
provider: sops
secretRef:
name: sops-age
sourceRef:
kind: GitRepository
name: gitops-repo
Rollout resource:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: frontend
spec:
replicas: 10
revisionHistoryLimit: 3
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: frontend:v2
strategy:
canary:
steps:
- setWeight: 10 # 10% traffic to new version
- pause: {duration: 2m} # Wait 2 minutes
- setWeight: 25
- pause: {duration: 5m}
- setWeight: 50
- pause: {duration: 5m}
- setWeight: 75
- pause: {duration: 5m}
# Automated analysis
analysis:
templates:
- templateName: success-rate
startingStep: 2
args:
- name: service-name
value: frontend
# Traffic routing
trafficRouting:
istio:
virtualService:
name: frontend
routes:
- primary
Analysis template:
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
args:
- name: service-name
metrics:
- name: success-rate
interval: 1m
count: 5
successCondition: result[0] >= 0.95
failureLimit: 3
provider:
prometheus:
address: http://prometheus:9090
query: |
sum(rate(
http_requests_total{service="{{args.service-name}}",status!~"5.."}[1m]
)) /
sum(rate(
http_requests_total{service="{{args.service-name}}"}[1m]
))
Install Flagger:
kubectl apply -k github.com/fluxcd/flagger//kustomize/istio
Canary resource:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: frontend
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: frontend
service:
port: 80
targetPort: 8080
analysis:
interval: 1m
threshold: 5 # Number of iterations
maxWeight: 50 # Max traffic to canary
stepWeight: 10 # Traffic increase per iteration
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1m
webhooks:
- name: load-test
url: http://flagger-loadtester/
timeout: 5s
metadata:
cmd: "hey -z 1m -q 10 -c 2 http://frontend-canary/"
Revert commit:
# Find problematic commit
git log --oneline
# Revert specific commit
git revert abc123
git push origin main
# ArgoCD/Flux automatically syncs rollback
Rollback to previous tag:
# Production currently on v1.2.3
# Rollback to v1.2.2
git checkout production
git reset --hard v1.2.2
git push --force origin production
# Update Application targetRevision
kubectl patch application frontend \
-n argocd \
--type merge \
-p '{"spec":{"source":{"targetRevision":"v1.2.2"}}}'
History and rollback:
# View deployment history
argocd app history frontend
# Rollback to specific revision
argocd app rollback frontend 5
# Rollback to previous revision
argocd app rollback frontend
Automatic rollback:
# Rollout with automatic rollback
apiVersion: argoproj.io/v1alpha1
kind: Rollout
spec:
strategy:
canary:
analysis:
templates:
- templateName: success-rate
startingStep: 1
# Auto-rollback on analysis failure
abortScaleDownDelaySeconds: 30
Suspend automation:
# Suspend Kustomization
flux suspend kustomization frontend
# Manually rollback Deployment
kubectl rollout undo deployment/frontend -n production
# Resume after validation
flux resume kustomization frontend
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.