GitOps is a paradigm where Git is the single source of truth for infrastructure and application state. Instead of CI pushing changes to the cluster, a GitOps agent pulls desired state from Git and continuously reconciles the cluster to match it.
Jenkins is push-based - it holds cluster credentials and forces changes in. If Jenkins is compromised, the cluster is compromised. ArgoCD is pull-based - it runs inside the cluster and watches Git. CI never needs cluster access. ArgoCD also detects and corrects drift automatically (selfHeal) - if someone manually changes something in the cluster outside of Git, ArgoCD reverts it. Jenkins has no concept of that.
fintrack-gitops/
├── apps/
│ ├── frontend.yaml - ArgoCD Application for frontend
│ ├── backend.yaml - ArgoCD Application for backend
│ └── cluster.yaml - ArgoCD Application for cluster addons
└── manifests/
├── frontend/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── ingress.yaml
└── backend/
├── deployment.yaml
├── service.yaml
├── hpa.yaml
├── sealedsecret.yaml
└── alerts.yaml
kubectl apply -f fintrack-gitops/apps/frontend.yaml
kubectl apply -f fintrack-gitops/apps/backend.yaml
kubectl apply -f fintrack-gitops/apps/cluster.yaml
Secrets encrypted with the cluster's public key before being committed to Git. The private key never leaves the cluster. Even if someone copies sealedsecret.yaml from GitHub, it's useless without the cluster's private key.
Every time a new cluster is created, the sealed-secrets controller generates a new keypair. Old sealed secrets cannot be decrypted by the new cluster. You must reseal:
cat > /tmp/backend-secret.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
name: fintrack-backend-secrets
namespace: fintrack
type: Opaque
stringData:
DATABASE_URL: "postgresql://fintrack_admin:<password>@<rds-endpoint>:5432/fintrack"
JWT_SECRET: "<your-jwt-secret>"
EOF
kubeseal \
--controller-name sealed-secrets \
--controller-namespace sealed-secrets \
--format yaml \
< /tmp/backend-secret.yaml \
> fintrack-gitops/manifests/backend/sealedsecret.yaml
rm /tmp/backend-secret.yaml
cd fintrack-gitops
git add manifests/backend/sealedsecret.yaml
git commit -m "fix: reseal secrets for new cluster"
git push

This is a PostgreSQL connection string. Breaking it down:
postgresql://fintrack_admin:FintrackDB2025Secure@fintrack-dev-db.csna2aeyggjv.us-east-1.rds.amazonaws.com:5432/fintrack
postgresql:// - protocol (PostgreSQL)
fintrack_admin - RDS master username (set in terraform.tfvars → db_username)
FintrackDB2025Secure - RDS master password (set in terraform.tfvars → db_password)
fintrack-dev-db.csna2aeyggjv.us-east-1.rds.amazonaws.com - RDS endpoint (from terraform output → db_endpoint, remove the :5432 part)
5432 - default PostgreSQL port
fintrack - database name (set in RDS module)