A production-ready Kubernetes deployment for a Go backend application with PostgreSQL database. This project includes best practices such as StatefulSet for the database, readiness/liveness probes, resource limits, init container for database migrations, Horizontal Pod Autoscaler, and Ingress for external access. The structure follows Helm chart conventions, making it easy to package and manage with Helm in the future.
go-app-chart/
├── templates/
│ ├── namespace.yaml
│ ├── secret.yaml
│ ├── configmap.yaml
│ ├── postgres-statefulset.yaml
│ ├── postgres-service.yaml
│ ├── backend-deployment.yaml
│ ├── backend-service.yaml
│ ├── ingress.yaml
│ └── hpa.yaml
└── README.md
All Kubernetes manifests are located in the templates/ directory. Although this is not yet a fully templated Helm chart, the layout is Helm‑compatible: you can easily add Chart.yaml and values.yaml to convert it into a reusable Helm chart.
Internet
│
▼
Ingress (go-ingress)
│
▼
Service (go-service)
│
▼
Deployment (go-backend) ── HPA (3-10 replicas)
│ │
│ │
▼ │
PostgreSQL StatefulSet │
│ │
▼ │
PersistentVolume (10Gi) ────┘
- Ingress: Routes external HTTP traffic to the backend service.
- Backend Service: Internal ClusterIP service that load‑balances across Go application pods.
- Backend Deployment: Runs the Go application with configured resource limits, probes, and an init container that waits for PostgreSQL and runs database migrations.
- HorizontalPodAutoscaler (HPA): Automatically scales the backend pods between 3 and 10 replicas based on CPU utilization.
- PostgreSQL StatefulSet: Runs a single PostgreSQL instance with persistent storage. Uses a headless service for stable network identity.
- ConfigMap & Secret: Store non‑sensitive configuration (database name, user, host) and sensitive data (database password) respectively.
- A running Kubernetes cluster (version 1.19+)
kubectlconfigured to communicate with your cluster- (Optional)
helmif you plan to convert to a Helm chart - An Ingress controller (e.g., NGINX Ingress) installed in the cluster (for Ingress to work)
Place all the YAML manifests in a directory named go-app-chart/templates/. Ensure the file names match those listed above.
It is recommended to apply the resources in the following order to avoid race conditions:
# Create the namespace first
kubectl apply -f templates/namespace.yaml
# Create secrets and configmap (these don't depend on other resources)
kubectl apply -f templates/secret.yaml
kubectl apply -f templates/configmap.yaml
# Deploy PostgreSQL StatefulSet and its headless service
kubectl apply -f templates/postgres-statefulset.yaml
kubectl apply -f templates/postgres-service.yaml
# Wait for PostgreSQL to be ready (optional but recommended)
kubectl wait --for=condition=ready pod -l app=postgres -n go-app --timeout=120s
# Deploy the backend application
kubectl apply -f templates/backend-deployment.yaml
kubectl apply -f templates/backend-service.yaml
# Finally, create the Ingress and HPA
kubectl apply -f templates/ingress.yaml
kubectl apply -f templates/hpa.yamlkubectl get all -n go-appYou should see the PostgreSQL pod, backend pods, services, and the HPA.
If you have set up an Ingress controller and configured DNS (or used a local /etc/hosts entry for goapp.local), you can access the application at http://goapp.local.
The ConfigMap holds all non‑sensitive configuration:
| Key | Description | Example Value |
|---|---|---|
POSTGRES_DB |
Name of the database to create | appdb |
POSTGRES_USER |
PostgreSQL superuser | postgres |
DB_HOST |
Hostname of the PostgreSQL service | postgres |
DB_PORT |
Port of PostgreSQL | 5432 |
DB_NAME |
Database name for the backend | appdb |
DB_USER |
Database user for the backend | postgres |
The Secret contains the PostgreSQL password. The value must be base64‑encoded:
echo -n 'YourStrongPassword' | base64Replace the POSTGRES_PASSWORD field in secret.yaml with your encoded password.
Resource requests and limits are set for both PostgreSQL and the Go backend. Adjust them in the respective manifests according to your application’s needs.
The HPA is configured to scale the backend deployment between 3 and 10 replicas when CPU usage exceeds 70%. You can modify these values in hpa.yaml.
The Ingress is configured for host goapp.local. Replace this with your actual domain and ensure your Ingress controller is properly set up. If you are using a different Ingress class, update ingressClassName accordingly.
To remove all resources created by this project:
kubectl delete namespace go-appNote: This will delete the namespace and all resources inside it, including the persistent volume claim holding your database data. Make sure to back up any important data before deletion.
The project is already structured like a Helm chart. To turn it into a fully functional Helm chart:
-
Create a
Chart.yamlfile in the root directory:apiVersion: v2 name: go-app description: A Go backend with PostgreSQL type: application version: 0.1.0 appVersion: 1.0.0
-
Create a
values.yamlfile and move all configurable parameters there (e.g., replica counts, image tags, resource sizes). -
Replace hard‑coded values in the templates with Helm template directives (e.g.,
{{ .Values.replicaCount }}). -
Test with
helm template .and then install withhelm install my-release ..
- The init container in
backend-deployment.yamlusespg_isreadyto wait for PostgreSQL. Ensure your application image includespg_isready(part ofpostgresql-client) or install it in the init container. - For production environments, consider using a more robust PostgreSQL operator for high availability, backups, and automated failover.
- The Secret uses a static base64‑encoded password. In production, integrate with a secrets management solution (e.g., HashiCorp Vault, External Secrets).
Feel free to adapt this project to your needs. If you have improvements or find issues, please open an issue or submit a pull request.