MET : Helm Charts – Application Packaging (5/6)


Deploying an application on Kubernetes often requires dozens of YAML files. Helm solves this problem by allowing you to package, version, and deploy applications with a single command.
In the previous article, we saw how Kubernetes orchestrates our applications. But writing all those YAML files by hand is tedious and error-prone. Helm is the solution.
The problem: the YAML jungle
To deploy a WordPress application on Kubernetes, you need to create:
- A Deployment for WordPress
- A Service to expose WordPress
- An Ingress for the domain and SSL
- A PersistentVolumeClaim for files
- A Secret for database credentials
- A ConfigMap for configuration
- Potentially NetworkPolicies, ResourceQuotas…
That’s easily 200-300 lines of YAML per application. Multiply by the number of clients, and you understand the problem.
And if you want to change the domain? You need to modify multiple files. Add more RAM? More files again.
The solution: Helm, the Kubernetes package manager
Helm is often described as the “apt-get” or “brew” of Kubernetes. It allows you to:
- Package an application into a reusable “chart”
- Parameterize via a simple values file
- Version deployments
- Update or rollback easily
“A Helm Chart is a ready-to-use Kubernetes application, customizable via a configuration file.”
Anatomy of a Helm Chart
A Helm chart has a standard structure:
my-chart/ ├── Chart.yaml # Metadata (name, version, description) ├── values.yaml # Default values ├── templates/ # Templated YAML files │ ├── deployment.yaml │ ├── service.yaml │ ├── ingress.yaml │ ├── pvc.yaml │ └── _helpers.tpl # Utility functions └── README.md # Documentation
Chart.yaml: the metadata
# Chart.yaml apiVersion: v2 name: wordpress-stack description: WordPress with OpenLiteSpeed, optimized for performance type: application version: 3.10.0 # Chart version appVersion: "6.4" # WordPress version
values.yaml: default configuration
# values.yaml
domain: example.com
wordpress:
image: litespeedtech/openlitespeed-wordpress:6.4
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
database:
# MariaDB deployed in the same namespace as WordPress
name: wordpress
user: wordpress
# Password is injected via CI/CD variables
storage:
size: 2Gi
storageClass: longhorn
ssl:
enabled: true
issuer: letsencrypt-prod
backup:
enabled: true
schedule: "0 2 * * *" # Daily at 2am
Templates: dynamic YAML files
Templates use Go templating language. Simplified example:
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-wordpress
namespace: {{ .Release.Namespace }}
spec:
replicas: {{ .Values.wordpress.replicas | default 1 }}
selector:
matchLabels:
app: {{ .Release.Name }}-wordpress
template:
spec:
containers:
- name: wordpress
image: {{ .Values.wordpress.image }}
resources:
{{- toYaml .Values.wordpress.resources | nindent 12 }}
env:
- name: WORDPRESS_DB_HOST
value: {{ .Values.database.host }}
- name: WORDPRESS_DB_NAME
value: {{ .Values.database.name }}
The {{ }} are replaced by values from the values.yaml file during deployment.
Our custom WordPress chart
We created a wordpress-stack chart optimized for our needs:
Included features
- ✅ OpenLiteSpeed: Ultra-fast web server (alternative to Apache/Nginx)
- ✅ Automatic SSL: Via Cert-Manager and Let’s Encrypt
- ✅ Dedicated database: MariaDB and Redis dedicated to each client, in their own namespace
- ✅ Persistent storage: Via Longhorn with replication
- ✅ Automatic backup: Daily snapshots
- ✅ Integrated SFTP: File access for developers
- ✅ Configurable resources: Adapt to client needs
Deployment in practice
To deploy a new WordPress site, we simply create a values file:
# values-client-alpha.yaml
domain: www.client-alpha.com
wordpress:
resources:
requests:
memory: "512Mi"
cpu: "200m"
limits:
memory: "1Gi"
cpu: "1000m"
database:
name: client_alpha_wp
user: client_alpha
storage:
size: 2Gi
sftp:
enabled: true
username: alpha-sftp
Then a single command:
helm upgrade --install client-alpha ./wordpress-stack \ --namespace client-alpha \ --create-namespace \ --values values-client-alpha.yaml
Result: In less than 2 minutes, the site is live with SSL, connected to the database, with backup configured.
Our app-stack chart: for all applications
Beyond WordPress, we created a generic app-stack chart capable of deploying any containerized application:
# values-my-api.yaml
appName: my-api
namespace: my-api
deploymentMode: simple
app:
image:
repository: my-registry/my-api
tag: v2.1.0
port: 3000
env:
- name: NODE_ENV
value: production
resources:
requests:
memory: "128Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "200m"
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
ingress:
enabled: true
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-http"
backup:
enabled: true
This chart supports:
- Single or multi-container applications
- Ingress with automatic SSL
- Environment variables and secrets
- Configurable health checks
- Jobs and CronJobs
Our Helm workflow
1. Chart development
# Validate syntax helm lint ./my-chart # See generated YAML without deploying helm template my-release ./my-chart --values values.yaml # Test in a dev namespace helm upgrade --install test ./my-chart -n dev --values values-dev.yaml
2. Packaging and publishing
# Create package (.tgz) helm package ./my-chart # Publish to our private Helm repository # (hosted on our cluster) helm push my-chart-1.0.0.tgz oci://registry.our-domain.com/charts
3. Production deployment
# From the repository helm upgrade --install client-prod our-repo/wordpress-stack \ --namespace client-prod \ --values values-prod.yaml \ --version 3.10.0
Version management and rollback
Helm keeps a deployment history:
# View history helm history client-alpha -n client-alpha REVISION STATUS DESCRIPTION 1 superseded Install complete 2 superseded Upgrade complete 3 deployed Upgrade complete
In case of problems, instant rollback:
# Return to previous version helm rollback client-alpha 2 -n client-alpha
The site returns to its previous state in seconds.
Helm in Rancher
Rancher natively integrates Helm. From the interface:
- Go to Apps > Charts
- Select our private repository
- Choose the chart (wordpress-stack, app-stack…)
- Fill in the configuration form
- Click Install
Rancher automatically generates a form from values.yaml, making deployment accessible even to non-technical users.
Our Helm best practices
📦 Structure
- One chart per application type (wordpress-stack, app-stack…)
- Sensible default values in
values.yaml - Clear documentation in README
🔐 Security
- Never secrets in values → use references to Kubernetes Secrets
- Images with specific tags (no
:latest) - Mandatory resource limits
🔄 Versioning
- Semantic versioning (MAJOR.MINOR.PATCH)
- Maintained CHANGELOG
- Tests before publication
📁 Values organization
values/
├── values-dev.yaml # Dev environment
├── values-staging.yaml # Pre-production
└── clients/
├── client-alpha.yaml
├── client-beta.yaml
└── client-gamma.yaml
The result
With our Helm charts:
| Before | After |
|---|---|
| 200+ lines of YAML per site | 20 lines of configuration |
| 1-2 hours of configuration | 5 minutes deployment |
| Copy-paste between projects | Reusable chart and versioned |
| Complex manual rollback | One command to go back |
| Separate documentation | The chart IS the documentation |
What’s next?
We now have:
- ✅ Infrastructure created by Terraform
- ✅ Servers configured by Ansible
- ✅ Kubernetes cluster managed via Rancher
- ✅ Applications packaged in Helm charts
But we still deploy manually. In the final article of this series, we’ll see how GitLab CI/CD and GitOps enable automatic deployment as soon as we push code.
🚀 Want to standardize your deployments?
We can create custom Helm charts for your applications, or train you on their use. Save time and reduce errors.


