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

| Digital

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:

  1. Go to Apps > Charts
  2. Select our private repository
  3. Choose the chart (wordpress-stack, app-stack…)
  4. Fill in the configuration form
  5. Click Install
Installing an application via Rancher

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.

Let’s discuss your needs →