If you’ve ever deployed microservices on Kubernetes, you know the pain: endless YAML files, copy-pasting configs, and maintaining slightly different manifests for every service. It works… until you need to scale. That’s where Helm comes in. By creating your own charts, you can transform messy manifests into reusable building blocks that scale seamlessly as your app grows.
In this post, I’ll walk you through how I built a shopping application with multiple microservices, deployed on AWS EKS with Terraform, but more importantly, how I made the whole setup modular with custom Helm charts.
As a DevOps engineer, here’s what you should know before deploying a microservices app:
- Which microservices you need to deploy
- Which services talk to each other
- How they communicate
- Which database(s) they use
- Which ports each service runs on
Why Write Your Own Helm Charts?
- Reusability - no duplicate YAML
- Scalability - add new services easily
- Flexibility - use values.yaml for environment-specific configs
- Consistency - Redis, ingress, microservices share structure
Instead of a one-off deployment, this approach gave me a framework to grow the application without headaches.
Prerequisites
Before diving in, you should have:
- Basic Kubernetes knowledge – deployments, services, and ingress
- Basic Terraform knowledge – how to provision infrastructure (init, apply, destroy)
Infrastructure First: Terraform + EKS
I used Terraform to provision the foundation:
- A VPC with subnets
- An EKS cluster
- IAM roles and networking With this, spinning up or tearing down the cluster is just:
cd infra
terraform init
terraform apply --auto-approve
terraform destroy --auto-approve
Building Helm Charts for Each Component
Instead of dumping all microservices into one manifest, I built a chart per component:
manifests/
├── charts/
│ ├── ingress/
│ ├── redis/
│ └── shopping-ms/
├── values/
├── helmfile.yaml
└── issuer.yaml
I created chart for redis database, ingress and the microservice.
helm create <chart-name>
For example, i created a chart for my shopping microservice:
helm create shopping-ms
This will generate a directory structure like:
shopping-ms/
├── charts/
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── _helpers.tpl
├── values.yaml
├── Chart.yaml
└── .helmignore
From here, templates can be edited with placeholders to fit the microservice and the values.yaml will carry the default values for the chart.
Now going two level above the shopping-ms/, in the manifests/, inside the values/ we can then have seperate values.yaml for each microservice, the values in these yaml will override the default values in the values.yaml in the chart directory.
Securing Ingress with TLS
- Install Nginx Ingress Controller in the cluster, which is responsible for creating loadbalancer and actually handling the traffic. Deployment and service in the Ingress chart is basically rules that the controller follows to know how to handle the traffic.
I used the helm repo for nginx-ingress controller and cert-manager directly from helm to minimize lot of overhead, i would get to the cert-manager in a bit, hang on!
# Add Helm repo for ingress-nginx
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
# Install ingress-nginx into its own namespace
kubectl create namespace ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--set controller.publishService.enabled=true
# Verify Ingress Controller
kubectl get pods -n ingress-nginx
# Check the LoadBalancer service
kubectl get svc -n ingress-nginx
- You should have an existing domain name or create one, point your domain to the LoadBalancer IP with a CNAME record.
- Install cert-manager in the cluster.
This handle the automatic creation of certificates and renewal before it expires. However, a clusterissuer or issuer is needed to tell cert-manager what CA (certificate authority) to use. Also annotation of
cert-manager.io/cluster-issuer: letsencrypt-prod
must be defined in the ingress.yaml so ingress can request for the certificate successfully.
kubectl create namespace cert-manager
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--set installCRDs=true
# This installs cert-manager and the CRDs (otherwise issuers won’t work, which is needed for TLS).
# Verify Installation
kubectl get pods -n cert-manager
- We need to apply the issuer.yaml, so the cert-manager is already to serve certificate when requested for.
kubectl apply -f issuer.yaml
Key notes
- Ingress controller is cluster-wide, not namespace-specific
- cert-manager is also a cluster-wide resource
- Use ClusterIssuer instead of Issuer for certificates that apply across namespaces
Deploying with Helmfile
Managing each chart individually quickly becomes painful. That’s why I used Helmfile, a single command to apply or destroy all charts at once.
helmfile apply
helmfile destroy
Access the application
Once deployed, the shopping app was live at:
https://<your-domain>
Troubleshooting Tips
- HTTPS not working? Ensure cert-manager is installed before applying your issuer
- Hit Let’s Encrypt rate limits? Use the staging issuer first
- Services not exposed? Double-check ingress annotations in your Helm chart
- Beginner level with kubernetes? I suggest you start with a single deployment file then turn it into helm charts.
Lessons Learned
- Start with charts early, don’t wait until you have YAML sprawl.
- Helmfile is a lifesaver, one command to apply/destroy everything.
- Charts are portable, I can now drop Redis or ingress into any new cluster.
Wrap up
This project showed me how writing reusable Helm charts makes Kubernetes deployments scalable, consistent, and future-proof. Instead of juggling manifests, I now have modular building blocks I can apply anywhere.
Next, I’m planning to extend these charts for monitoring (Prometheus + Grafana) so the setup can evolve into a full production-ready stack.
Full codebase here: Github repo
Top comments (0)