This guide explains how to set up a throw-away local Kubernetes cluster using Kind for developing and testing the RIG-Cluster infrastructure.
The RIG-Cluster can be deployed on a local Kind (Kubernetes in Docker) cluster for development and testing. This setup includes:
- Kind cluster with Kubernetes v1.32.0
- NGINX Ingress Controller for routing HTTP traffic
- CloudNativePG (CNPG) operator for PostgreSQL management
- ArgoCD for GitOps deployments
- Forgejo as a local Git server for user application repositories
- Infrastructure services: PostgreSQL, Keycloak, MinIO, Chisel, Operations Manager
Install these tools before proceeding:
# macOS (using Homebrew)
brew install kind kubectl kustomize sops age pwgen go-task docker
# Verify installations
kind version
kubectl version --client
kustomize version
sops --version
age --version
task --version
docker --version- kind: Latest
- kubectl: 1.29+
- kustomize: 5.0+
- sops: 3.8+
- age: 1.1+
- Docker: 20.10+ (running and accessible)
- task: 3.0+
For the impatient, here's the one-command setup:
# Select local cluster configuration
task select-cluster
# Choose option 1 for local cluster
# Complete automated setup
task setup-local-clusterThis will:
- Create the Kind cluster
- Install required operators
- Configure CoreDNS
- Prepare ArgoCD operator
- Build and load Docker images
- Generate encryption keys and secrets
Then follow the "Next steps" output to complete the setup.
If you prefer to understand each step or need to troubleshoot:
task select-cluster
# Choose option 1 for local clusterThis creates .env-taskfile-current with local cluster settings.
task create-local-kind-clusterThis creates a cluster named gitops-fluxcd using kind-config.yaml with:
- Kubernetes v1.32.0
- Port mappings (80:80, 443:443)
- Ingress-ready node labels
Verify:
kind get clusters
kubectl cluster-info
kubectl get nodestask install-ingress-nginxInstalls the NGINX Ingress Controller for Kind, which handles routing to services via *.kind domains.
Verify:
kubectl get pods -n ingress-nginx
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=90stask install-cnpg-operatorInstalls the CNPG operator v1.23.0 for managing PostgreSQL clusters.
Verify:
kubectl get pods -n cnpg-system
kubectl get crds | grep postgresqltask configure-coredns-kind-domainsPatches CoreDNS to rewrite *.kind domains to the ingress controller, enabling internal DNS resolution.
Verify:
kubectl run -it --rm --restart=Never --image=busybox:1.28 dnstest -- nslookup keycloak.kindtask prepare-argocd-operatorDownloads and customizes the ArgoCD operator for local development:
- Disables webhooks
- Applies fake Prometheus CRD
- Builds operator manifest
Verify:
ls -l bootstrap/crd/operator/argocd-operator-install.yamltask update-cmp-kustomize-sopsBuilds the ArgoCD Config Management Plugin for processing SOPS-encrypted kustomize files.
task update-operations-managerBuilds and loads the Operations Manager image.
task build-postgresql-imageBuilds a custom PostgreSQL image with Dutch/English full-text search support (only if needed).
Verify all images:
docker exec -it gitops-fluxcd-control-plane crictl images | grep -E "(rig-cmp|operations-manager|postgresql)"task generate-age-keyCreates an AGE key pair at security/key.txt for SOPS encryption.
Important: This file is gitignored and must be backed up securely.
Verify:
ls -l security/key.txt
head -1 security/key.txt # Should show "# created:"task generate-bootstrap-secrets-for-clusterGenerates SOPS-encrypted secrets for ArgoCD:
- Admin credentials
- Repository credentials
- Git daemon access
Creates secrets-overview-bootstrap-local.yaml with plaintext passwords - copy and delete this file.
Verify:
ls -l bootstrap/rig-system/kustomize/overlays/local/*.sops.yamltask generate-infrastructure-secrets-for-clusterGenerates SOPS-encrypted secrets for infrastructure services:
- PostgreSQL admin password
- Keycloak admin credentials
- Keycloak database credentials
- Forgejo database credentials
- MinIO admin credentials
Creates secrets-overview-infrastructure-local.yaml - copy and delete this file.
Verify:
ls -l infrastructure/bootstrap/infrastructure/secrets/config/overlays/local/*.sops.yamlkubectl create namespace rig-system
kubectl create secret generic sops-age-key \
--from-file=key=security/key.txt \
-n rig-systemThis allows ArgoCD to decrypt SOPS-encrypted files.
Verify:
kubectl get secret sops-age-key -n rig-systemIn a separate terminal, run from the repository root:
git daemon \
--base-path=. \
--export-all \
--reuseaddr \
--informative-errors \
--verbose \
--enable=upload-pack \
--port=9090 \
--listen=0.0.0.0 \
--log-destination=stderrThis serves the infrastructure repository to ArgoCD at git://host.docker.internal:9090/.
Verify:
# In another terminal
git ls-remote git://localhost:9090/task bootstrap-argo-systemDeploys ArgoCD and the Operations Manager to the rig-system namespace.
Verify:
kubectl get pods -n rig-system
kubectl get applications -n rig-systemArgoCD will automatically deploy all infrastructure components:
- PostgreSQL cluster
- Forgejo (Git server)
- Keycloak (SSO)
- MinIO (S3 storage)
- Chisel (tunneling)
- Operations Manager
Monitor progress:
# Watch pods
kubectl get pods -n rig-system -w
# Check ArgoCD applications
kubectl get applications -n rig-system
# View ArgoCD logs
kubectl logs -n rig-system deployment/argocd-server -fAdd to /etc/hosts:
127.0.0.1 argo.kind keycloak.kind minio.kind operations-manager.kind forgejo.kind
Service URLs:
- ArgoCD: http://argo.kind (admin / admin)
- Forgejo: http://forgejo.kind (admin / admin)
- Operations Manager: http://operations-manager.kind
- Keycloak: http://keycloak.kind (admin / )
- MinIO: http://minio.kind (admin / )
Port forwarding alternative:
# ArgoCD
kubectl port-forward svc/argocd-server -n rig-system 8080:80
# Forgejo
kubectl port-forward svc/forgejo -n rig-system 3000:3000Namespace: rig-system
Cluster name: rig-db
Managed roles:
keycloak- Keycloak databaseforgejo- Forgejo database
Connection:
# Read-write service
rig-db-rw.rig-system.svc.cluster.local:5432
# Read-only service
rig-db-ro.rig-system.svc.cluster.local:5432Access:
kubectl exec -it rig-db-1 -n rig-system -- psql -U postgresPurpose: Stores user application repositories and ArgoCD application manifests.
Repositories created by bootstrap:
admin/user-applications- User application codeadmin/argo-user-applications- ArgoCD application manifests
Web UI: http://forgejo.kind Credentials: admin / admin
Operations Manager Integration: The Operations Manager automatically uses Forgejo for local development:
GIT_PROJECTS_SERVER_URL: Points to ForgejoGIT_ARGO_APPLICATIONS_URL: Points to Forgejo- No code changes needed - configuration-driven
Repository URLs:
http://forgejo.rig-system.svc.cluster.local:3000/admin/user-applications.git
http://forgejo.rig-system.svc.cluster.local:3000/admin/argo-user-applications.git
Purpose: GitOps continuous delivery for Kubernetes.
Applications:
production-infrastructure- Deploys infrastructure componentsuser-applications- Deploys user projects (created by Operations Manager)
SOPS Support: ArgoCD includes a CMP (Config Management Plugin) sidecar that decrypts SOPS-encrypted files during sync.
Purpose: Self-service portal for creating and managing RIG projects.
Features:
- Create projects with database, Keycloak realm, MinIO buckets
- Generate ArgoCD applications
- Manage project lifecycle
- SSO integration
User → Ingress (*.kind) → Services
├─ ArgoCD (GitOps controller)
├─ Forgejo (Git server)
│ ├─ user-applications repo
│ └─ argo-user-applications repo
├─ PostgreSQL CNPG Cluster
│ ├─ rig-db (main database)
│ ├─ keycloak DB (managed role)
│ └─ forgejo DB (managed role)
├─ Keycloak (SSO/Identity)
├─ MinIO (S3 storage)
└─ Operations Manager (API/UI)
└─ Uses Forgejo for repos
Keycloak OIDC flows require stable internal DNS resolution. Services must communicate using domain names rather than cluster IPs, but standard CoreDNS cannot resolve the .kind TLD used for local development.
A DNS rewrite rule redirects all *.kind domains to the NGINX ingress controller.
Task: configure-coredns-kind-domains
Implementation:
- Patches CoreDNS ConfigMap (
kube-systemnamespace) - Inserts rewrite rule after
readyplugin:rewrite stop { name regex (.+)\.kind ingress-nginx-controller.ingress-nginx.svc.cluster.local answer auto } - Restarts CoreDNS deployment
Outcome: DNS queries for keycloak.kind, argo.kind, etc. resolve to the ingress controller service within the cluster.
Verification:
kubectl run -it --rm --restart=Never --image=busybox:1.28 dnstest -- nslookup keycloak.kindDNS rewriting enables:
- Service discovery using friendly domain names
- Proper Keycloak OIDC redirect URIs and issuer URLs
- Consistent pod behavior without per-pod configuration
- No host machine
/etc/hostsmanagement for internal resolution
Alternative approaches (host file modifications, external DNS) were rejected due to maintenance overhead and inconsistent behavior across pods.
Problem: Cluster creation fails
# Check Docker is running
docker ps
# Check for port conflicts
lsof -i :80
lsof -i :443
# Remove existing cluster
task uninstall-local-kind-cluster
task create-local-kind-clusterProblem: Can't access cluster
# Check cluster exists
kind get clusters
# Check kubeconfig
kubectl config get-contexts
kubectl config use-context kind-gitops-fluxcd
# Verify nodes
kubectl get nodesProblem: CNPG operator not ready
# Check operator pods
kubectl get pods -n cnpg-system
# Check operator logs
kubectl logs -n cnpg-system deployment/cnpg-controller-manager
# Reinstall
kubectl delete -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.23/releases/cnpg-1.23.0.yaml
task install-cnpg-operatorProblem: Ingress controller not ready
# Check ingress pods
kubectl get pods -n ingress-nginx
# Check logs
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller
# Reinstall
kubectl delete namespace ingress-nginx
task install-ingress-nginxProblem: ImagePullBackOff errors
# Check images in kind cluster
docker exec -it gitops-fluxcd-control-plane crictl images
# Rebuild and reload image
task update-cmp-kustomize-sops
task update-operations-manager
# Verify image present
docker exec -it gitops-fluxcd-control-plane crictl images | grep <image-name>Problem: ArgoCD can't decrypt SOPS files
# Check sops-age-key secret exists
kubectl get secret sops-age-key -n rig-system
# Verify key content
kubectl get secret sops-age-key -n rig-system -o jsonpath='{.data.key}' | base64 -d | head -5
# Recreate secret
kubectl delete secret sops-age-key -n rig-system
kubectl create secret generic sops-age-key \
--from-file=key=security/key.txt \
-n rig-systemProblem: Can't decrypt locally
# Check SOPS_AGE_KEY_FILE
export SOPS_AGE_KEY_FILE=security/key.txt
# Verify age key
age --version
cat security/key.txt
# Test decryption
sops -d bootstrap/rig-system/kustomize/overlays/local/argocd-admin-secret.yaml.sops.yamlProblem: Can't resolve *.kind domains
# Test DNS from within cluster
kubectl run -it --rm --restart=Never --image=busybox:1.28 dnstest -- nslookup keycloak.kind
# Check CoreDNS configuration
kubectl get configmap coredns -n kube-system -o yaml
# Reapply CoreDNS patch
task configure-coredns-kind-domains
# Restart CoreDNS
kubectl rollout restart deployment coredns -n kube-systemProblem: Can't access services from browser
# Check /etc/hosts
cat /etc/hosts | grep kind
# Add entries
sudo sh -c 'echo "127.0.0.1 argo.kind keycloak.kind minio.kind operations-manager.kind forgejo.kind" >> /etc/hosts'
# Test with curl
curl -v http://argo.kindProblem: Forgejo bootstrap job fails
# Check bootstrap job
kubectl get jobs -n rig-system | grep forgejo-bootstrap
# View job logs
kubectl logs -n rig-system job/forgejo-bootstrap
# Check Forgejo pod
kubectl get pods -n rig-system -l app=forgejo
kubectl logs -n rig-system -l app=forgejo
# Rerun bootstrap
kubectl delete job forgejo-bootstrap -n rig-system
kubectl apply -k infrastructure/bootstrap/infrastructure/forgejo/config/baseProblem: Can't access Forgejo
# Check Forgejo pod
kubectl get pods -n rig-system -l app=forgejo
# Check service
kubectl get svc forgejo -n rig-system
# Check ingress
kubectl get ingress forgejo -n rig-system
# Port forward
kubectl port-forward svc/forgejo -n rig-system 3000:3000
# Access at http://localhost:3000Problem: Repositories not created
# Check if admin user exists
kubectl exec -n rig-system forgejo-0 -- forgejo admin user list
# Check database connection
kubectl exec -n rig-system forgejo-0 -- forgejo doctor check --run database
# Manually create repos via UI
# Login to http://forgejo.kind with admin/admin
# Create repositories manuallyProblem: ArgoCD applications stuck in sync
# Check application status
kubectl get applications -n rig-system
# Describe application
kubectl describe application production-infrastructure -n rig-system
# Check ArgoCD logs
kubectl logs -n rig-system deployment/argocd-server
kubectl logs -n rig-system deployment/argocd-repo-server
# Manually sync
kubectl patch application production-infrastructure -n rig-system --type merge -p '{"metadata":{"annotations":{"argocd.argoproj.io/refresh":"normal"}}}'Problem: Git daemon connection fails
# Check git daemon is running
ps aux | grep "git daemon"
# Test git daemon
git ls-remote git://localhost:9090/
# Restart git daemon
# Kill existing process
pkill -f "git daemon"
# Start again
git daemon --base-path=. --export-all --reuseaddr --informative-errors --verbose --enable=upload-pack --port=9090 --listen=0.0.0.0 --log-destination=stderrtask uninstall-local-kind-clusterThis deletes the entire Kind cluster and all resources.
# Delete ArgoCD
kubectl delete -k bootstrap/rig-system/kustomize/overlays/local
# Delete infrastructure
kubectl delete -k infrastructure/bootstrap/clusters/local
# Delete namespace
kubectl delete namespace rig-system- Make infrastructure changes in the repository
- Git daemon serves changes automatically
- ArgoCD detects changes and syncs (or manually refresh)
- Verify in cluster using kubectl
- Never commit
security/key.txtto git - Back up AGE key securely
- Delete
secrets-overview-*.yamlfiles after copying passwords - Rotate secrets regularly
- Storage: Kind uses Docker volumes, clean up old clusters regularly
- Images: Use
docker system pruneto clean up unused images - Logs: View logs with
kubectl logsinstead of storing them
# Quick rebuild and update operations manager
task update-operations-manager
# Quick restart of ArgoCD
kubectl rollout restart deployment -n rig-system
# Force ArgoCD sync
kubectl patch application production-infrastructure -n rig-system --type merge -p '{"metadata":{"annotations":{"argocd.argoproj.io/refresh":"hard"}}}'Once the cluster is running:
- Explore ArgoCD UI at http://argo.kind
- Create a test project via Operations Manager
- Browse Forgejo repos at http://forgejo.kind
- Configure Keycloak realms for SSO testing
- Test MinIO buckets for S3 storage