ArgoCD — GitOps Continuous Delivery
Overview
LuckyPlans uses ArgoCD for pull-based GitOps continuous delivery. ArgoCD watches the Git repository for changes to Helm values files and automatically synchronizes deployments to the target Kubernetes clusters.
GitOps Flow
Developer pushes code to main
│
▼
CI workflow (lint, test, build)
│
▼
Docker Build & Push (ghcr.io/takeshi-su57/<service>:sha-<hash>)
│
▼
Update Tags workflow commits new image tags to values files [skip ci]
│
▼
ArgoCD detects change in Git → auto-syncs
│
▼
Post-sync hook runs smoke tests (health, web, GraphQL)
Architecture
Components
| Component | Location | Purpose |
|---|---|---|
| ArgoCD Helm values | infrastructure/argocd/values-base.yaml, values-prod.yaml | ArgoCD server configuration |
| Application manifest | infrastructure/argocd/apps/luckyplans-prod.yaml | ArgoCD Application resource for prod |
| Install script | infrastructure/scripts/install-argocd.sh | One-time ArgoCD installation on prod cluster |
| Smoke test hook | infrastructure/helm/luckyplans/templates/smoke-test-job.yaml | Post-sync verification |
| Tag update workflow | .github/workflows/update-tags.yml | CI commits image tags to values files |
Environment Strategy
ArgoCD is used only for prod. Local development uses direct Helm (pnpm deploy:local).
| Environment | CD Method | Image Update | Approval |
|---|---|---|---|
| Local | Direct Helm | Manual (k3d import) | None |
| Prod | ArgoCD (auto-sync) | CI updates values | Automatic |
Installation
Prerequisites
- k3d cluster running with Traefik ingress
- Helm 3.x installed
- kubectl configured for the target cluster
- GitHub personal access token (PAT) with repo read access
- CD push token (required with branch protection): Fine-grained PAT with Contents: read+write scope stored as the
CD_PUSH_TOKENrepository secret
Install ArgoCD
./infrastructure/scripts/install-argocd.sh --github-token ghp_xxx
The script:
- Adds the ArgoCD Helm repo
- Installs ArgoCD into the
argocdnamespace with base + prod values - Waits for ArgoCD server, repo server, and application controller to be ready
- Prints the initial admin password
- Applies the Traefik HTTPS redirect middleware for the ArgoCD UI
- Applies the
luckyplans-prodApplication manifest - Prints the ArgoCD UI access URL
Accessing ArgoCD UI
URL: https://luckyplans.xyz/argocd
Initial login:
- Username:
admin - Password: retrieve with:
kubectl -n argocd get secret argocd-initial-admin-secret \ -o jsonpath='{.data.password}' | base64 -d
Operations
Check Sync Status
argocd app get luckyplans-prod --server <argocd-url> --grpc-web
Rollback
Option 1: Git revert (recommended)
git log --oneline -5
git revert <commit-sha>
git push origin main
Option 2: ArgoCD UI rollback (temporary — auto-sync overrides immediately)
- Open the Application in ArgoCD UI
- Click History and Rollback
- Select a previous revision → Rollback
Smoke Tests
Smoke tests run automatically as ArgoCD post-sync hooks. They verify:
- API Gateway health (
/healthreturns{"status":"ok"}) - Web frontend (HTTP 200 on
/) - GraphQL endpoint (
{ health }query returns expected response)
kubectl -n luckyplans logs job/smoke-test
Secret Management
Secrets (JWT_SECRET, DB_PASSWORD) are not stored in Git. They use Helm’s lookup function to preserve existing values from the cluster across ArgoCD syncs.
First-time setup:
kubectl -n luckyplans create secret generic luckyplans-secrets \
--from-literal=JWT_SECRET="$(openssl rand -base64 48)" \
--from-literal=DB_PASSWORD="your-db-password"
Troubleshooting
Application stuck in “Progressing”
kubectl -n luckyplans get pods
kubectl -n luckyplans describe pod <pod-name>
Application shows “OutOfSync” after sync
Handled by ignoreDifferences + RespectIgnoreDifferences=true in the Application manifests — tells ArgoCD to skip diffing the Secret resource since Helm’s lookup function renders a different value each time in the repo server.
CI tag update commit conflicts
The update-tags workflow uses a concurrency group to prevent races. If a conflict occurs, re-run the workflow manually.
update-tags workflow fails to push to main
Create a fine-grained PAT with Contents: read+write scope and store it as the CD_PUSH_TOKEN repository secret.