What is GitOps?

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.

Why ArgoCD over Jenkins?

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.

Repository Structure

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

Apply ArgoCD Applications

kubectl apply -f fintrack-gitops/apps/frontend.yaml
kubectl apply -f fintrack-gitops/apps/backend.yaml
kubectl apply -f fintrack-gitops/apps/cluster.yaml

Sealed Secrets - Why?

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.

Reseal Secrets on Every New Cluster

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

image.png


Understanding the Backend Secrets

DATABASE_URL

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)