This repository contains the Helm chart and Argo CD configuration used to deploy the GovTool stack on Kubernetes.
GovTool is deployed to a multi-node Kubernetes cluster using:
- Helm for Kubernetes deployment configuration
- Argo CD for GitOps-based deployment and synchronization
- Kubernetes Ingress for routing traffic
- Kubernetes Secrets for runtime configuration
- A PostgreSQL instance for the PDF/proposal pillar service
GitHub Repository
-> Argo CD Application
-> Helm Chart
-> Kubernetes Resources
-> GovTool ServicesContext: govtool-k8s-kushal-admin
Cluster: govtool-k8s
Namespace: govtoolk8s-govtool-control-0 control-plane
k8s-govtool-control-1 control-plane
k8s-govtool-control-2 control-plane
k8s-govtool-worker-1 worker
k8s-govtool-worker-2 workerThe following GovTool services are deployed in the govtool namespace:
govtool-frontend
govtool-backend
govtool-metadata
govtool-outcomes
govtool-pdf
govtool-pdf-dbGovTool is exposed through an NGINX Ingress.
Host: k8s.dev.gov.tools
Ingress class: nginx/ -> govtool-frontend
/api -> govtool-backend
/api/metadata -> govtool-metadata
/api/outcomes -> govtool-outcomes
/api/pdf -> govtool-pdfArgo CD is installed in the argocd namespace and manages the GovTool deployment from GitHub.
Application: govtool
Namespace: argocd
Repository: https://github.com/cardanoapi/govtool-argo.git
Branch: main
Chart path: .
Target namespace: govtoolCommit pushed to GitHub
-> Argo CD detects the change
-> Argo CD renders the Helm chart
-> Argo CD syncs Kubernetes resources
-> GovTool stack is updatedThe Helm chart defines:
- Deployments
- Services
- Ingress
- Runtime environment variables
- Secret/config mounts
- PDF database volume mount
- Optional PDF database migration Job
helm template govtool .helm lint .helm upgrade --install govtool . -n govtool --create-namespaceRuntime secrets are created directly in Kubernetes and are not committed to Git.
Required secrets:
govtool-backend-config
govtool-outcomes-db
govtool-pdf-db
govtool-pdf-envThe backend reads its configuration from:
/run/secrets/config.jsonExample creation:
kubectl create secret generic govtool-backend-config \
-n govtool \
--from-literal=config.json='{
"dbsyncconfig": {
"host": "DB_HOST",
"dbname": "DB_NAME",
"user": "DB_USER",
"password": "DB_PASSWORD",
"port": 5432
},
"port": 9876,
"host": "0.0.0.0",
"cachedurationseconds": 20,
"sentrydsn": "",
"sentryenv": ""
}'kubectl create secret generic govtool-outcomes-db \
-n govtool \
--from-literal=DATABASE_HOST='DB_HOST' \
--from-literal=DATABASE_PORT='5432' \
--from-literal=DATABASE_NAME='DB_NAME' \
--from-literal=DATABASE_USER='DB_USER' \
--from-literal=DATABASE_PASSWORD='DB_PASSWORD'kubectl create secret generic govtool-pdf-db \
-n govtool \
--from-literal=POSTGRES_DB='postgres' \
--from-literal=POSTGRES_USER='postgres' \
--from-literal=POSTGRES_PASSWORD='POSTGRES_PASSWORD'kubectl create secret generic govtool-pdf-env \
-n govtool \
--from-literal=DATABASE_USERNAME='postgres' \
--from-literal=DATABASE_PASSWORD='POSTGRES_PASSWORD' \
--from-literal=APP_KEYS='APP_KEYS' \
--from-literal=API_TOKEN_SALT='API_TOKEN_SALT' \
--from-literal=ADMIN_JWT_SECRET='ADMIN_JWT_SECRET' \
--from-literal=TRANSFER_TOKEN_SALT='TRANSFER_TOKEN_SALT' \
--from-literal=JWT_SECRET='JWT_SECRET' \
--from-literal=REFRESH_SECRET='REFRESH_SECRET' \
--from-literal=POSTGRESQL_REPLICATION_USE_PASSFILE='false'govtool-pdf-db runs PostgreSQL for the PDF/proposal pillar service.
The PDF database uses a hostPath mount on a selected worker node.
Pod mount path: /var/lib/postgresql/data
Node host path: /var/lib/govtool/pdf-postgresThe PDF DB pod is pinned to a worker node using a node label.
kubectl label node k8s-govtool-worker-1 govtool-pdf-db=trueThe Helm values use:
nodeSelector:
govtool-pdf-db: "true"The PDF DB migration is handled by a Kubernetes Job managed through Argo CD.
Job: govtool-pdf-db-migration
ConfigMap: govtool-pdf-db-migrationThe migration Job mounts schema.sql from the ConfigMap and applies it to govtool-pdf-db.
pdfDbMigration:
enabled: trueAfter the migration succeeds, disable it again:
pdfDbMigration:
enabled: falseThis prevents the migration Job from running again on every Argo sync.
kubectl get application govtool -n argocdkubectl describe application govtool -n argocdkubectl annotate application govtool -n argocd \
argocd.argoproj.io/refresh=hard \
--overwritekubectl get pods -n govtool
kubectl get svc -n govtool
kubectl get ingress -n govtool