Learn to install Helm 3 on Linux, configure private repositories with authentication, implement security best practices with RBAC, and integrate with CI/CD pipelines for automated Kubernetes deployments.
Prerequisites
- Active Kubernetes cluster with kubectl configured
- Administrative access to install packages
- Basic understanding of Kubernetes concepts
- GPG installed for chart signing (optional)
What this solves
Helm 3 is the de facto package manager for Kubernetes, allowing you to deploy, upgrade, and manage complex applications using pre-packaged charts. This tutorial covers installing Helm 3, configuring private repositories with authentication, implementing security best practices including RBAC, and integrating with CI/CD pipelines for production environments.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you have the latest package information and security updates.
sudo apt update && sudo apt upgrade -y
Install required dependencies
Install curl and other utilities needed for downloading and verifying Helm packages.
sudo apt install -y curl gnupg2 software-properties-common apt-transport-https
Download and install Helm 3
Download the latest stable version of Helm 3 from the official repository and install it system-wide.
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt update
sudo apt install -y helm
Verify Helm installation
Confirm Helm is properly installed and check the version to ensure you have Helm 3.
helm version
helm help
Configure kubectl context
Ensure you have a working Kubernetes cluster connection before proceeding with Helm configuration. If you need to set up Kubernetes, refer to our Kubernetes cluster installation guide.
kubectl cluster-info
kubectl get nodes
Configure private Helm repositories
Add official Helm repositories
Add the official stable Helm chart repository and other commonly used repositories.
helm repo add stable https://charts.helm.sh/stable
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
Configure private repository with authentication
Add a private Helm repository with username and password authentication. Replace the URL and credentials with your actual private repository details.
helm repo add private-repo https://charts.example.com --username myuser --password mypassword
helm repo update
Configure repository with certificate authentication
For repositories using client certificates, configure authentication using certificate files.
mkdir -p ~/.helm/certs
Copy your certificate files to ~/.helm/certs/
helm repo add secure-repo https://secure-charts.example.com --cert-file ~/.helm/certs/client.crt --key-file ~/.helm/certs/client.key --ca-file ~/.helm/certs/ca.crt
List and verify repositories
Verify all repositories are properly configured and accessible.
helm repo list
helm search repo nginx
Create and deploy custom Helm charts
Create a new Helm chart
Generate a new Helm chart template with the standard directory structure and files.
helm create myapp
cd myapp
ls -la
Customize Chart.yaml
Edit the Chart.yaml file to define your application metadata and dependencies.
apiVersion: v2
name: myapp
description: A Helm chart for my application
type: application
version: 0.1.0
appVersion: "1.0.0"
maintainers:
- name: Your Name
email: your.email@example.com
keywords:
- web
- application
dependencies:
- name: postgresql
version: "12.1.2"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
Configure values.yaml
Define default configuration values that can be overridden during deployment.
replicaCount: 2
image:
repository: nginx
pullPolicy: IfNotPresent
tag: "1.21.6"
service:
type: ClusterIP
port: 80
ingress:
enabled: true
className: "nginx"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: myapp.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: myapp-tls
hosts:
- myapp.example.com
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
postgresql:
enabled: true
auth:
postgresPassword: "secure-password-here"
database: "myapp"
Deploy the chart
Install your custom chart with a specific release name and namespace.
helm dependency update
kubectl create namespace myapp
helm install myapp-release ./myapp --namespace myapp --create-namespace
Implement Helm security and RBAC
Create service account for Helm
Create a dedicated service account with minimal required permissions for Helm operations.
apiVersion: v1
kind: ServiceAccount
metadata:
name: helm-service-account
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: helm-cluster-role
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: helm-cluster-role-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: helm-cluster-role
subjects:
- kind: ServiceAccount
name: helm-service-account
namespace: kube-system
kubectl apply -f helm-rbac.yaml
Configure Helm security policies
Create a security policy to restrict Helm chart installations and enforce security standards.
apiVersion: v1
kind: ConfigMap
metadata:
name: helm-security-policy
namespace: kube-system
data:
policy.yaml: |
security:
allowedRegistries:
- "docker.io"
- "quay.io"
- "gcr.io"
- "registry.example.com"
forbiddenImages:
- "*:latest"
- "*:master"
requiredLabels:
- "app.kubernetes.io/name"
- "app.kubernetes.io/version"
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
resources:
required: true
limits:
memory: "1Gi"
cpu: "1000m"
kubectl apply -f helm-security-policy.yaml
Enable chart signing and verification
Generate GPG keys for signing charts and configure Helm to verify signatures before installation.
gpg --full-generate-key
Follow the prompts to create a key
gpg --list-secret-keys --keyid-format LONG
Note your key ID from the output
helm package --sign --key 'Your Name' --keyring ~/.gnupg/secring.gpg myapp
helm verify myapp-0.1.0.tgz
Set up automated updates and rollbacks
Create upgrade script
Create an automated script for updating Helm releases with safety checks and rollback capabilities.
#!/bin/bash
set -e
RELEASE_NAME="$1"
CHART_PATH="$2"
NAMESPACE="$3"
VALUES_FILE="$4"
if [ $# -ne 4 ]; then
echo "Usage: $0 "
exit 1
fi
echo "Upgrading release: $RELEASE_NAME"
echo "Chart path: $CHART_PATH"
echo "Namespace: $NAMESPACE"
echo "Values file: $VALUES_FILE"
Backup current release
echo "Creating backup..."
helm get all $RELEASE_NAME --namespace $NAMESPACE > "backup-$RELEASE_NAME-$(date +%Y%m%d-%H%M%S).yaml"
Update dependencies
echo "Updating dependencies..."
helm dependency update $CHART_PATH
Dry run first
echo "Performing dry run..."
helm upgrade $RELEASE_NAME $CHART_PATH --namespace $NAMESPACE --values $VALUES_FILE --dry-run
Actual upgrade
echo "Performing upgrade..."
helm upgrade $RELEASE_NAME $CHART_PATH --namespace $NAMESPACE --values $VALUES_FILE --wait --timeout 10m
Verify deployment
echo "Verifying deployment..."
kubectl get pods --namespace $NAMESPACE
helm test $RELEASE_NAME --namespace $NAMESPACE
echo "Upgrade completed successfully!"
chmod +x helm-upgrade.sh
Configure automatic rollback
Create a monitoring script that automatically rolls back deployments if health checks fail.
#!/bin/bash
set -e
RELEASE_NAME="$1"
NAMESPACE="$2"
HEALTH_CHECK_URL="$3"
MAX_RETRIES=5
RETRY_INTERVAL=30
echo "Monitoring release: $RELEASE_NAME in namespace: $NAMESPACE"
for i in $(seq 1 $MAX_RETRIES); do
echo "Health check attempt $i/$MAX_RETRIES"
if curl -f -s "$HEALTH_CHECK_URL" > /dev/null; then
echo "Health check passed"
exit 0
else
echo "Health check failed, attempt $i/$MAX_RETRIES"
if [ $i -eq $MAX_RETRIES ]; then
echo "All health checks failed, initiating rollback..."
helm rollback $RELEASE_NAME --namespace $NAMESPACE
echo "Rollback completed"
exit 1
else
echo "Waiting $RETRY_INTERVAL seconds before next attempt..."
sleep $RETRY_INTERVAL
fi
fi
done
chmod +x helm-monitor.sh
Integrate with CI/CD pipelines
Create GitLab CI pipeline configuration
Configure a GitLab CI pipeline for automated Helm deployments. This integrates well with GitLab CE installations.
stages:
- lint
- build
- test
- deploy-staging
- deploy-production
variables:
HELM_VERSION: "3.13.2"
KUBECTL_VERSION: "1.28.4"
CHART_PATH: "./charts/myapp"
before_script:
- curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
- chmod 700 get_helm.sh
- ./get_helm.sh --version v$HELM_VERSION
- curl -LO "https://dl.k8s.io/release/v$KUBECTL_VERSION/bin/linux/amd64/kubectl"
- chmod +x kubectl && sudo mv kubectl /usr/local/bin/
helm-lint:
stage: lint
script:
- helm lint $CHART_PATH
- helm template myapp $CHART_PATH --values $CHART_PATH/values-staging.yaml > /dev/null
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
helm-package:
stage: build
script:
- helm dependency update $CHART_PATH
- helm package $CHART_PATH --version $CI_COMMIT_SHA
artifacts:
paths:
- "*.tgz"
expire_in: 1 week
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
deploy-staging:
stage: deploy-staging
environment:
name: staging
url: https://staging.example.com
script:
- kubectl config use-context staging-cluster
- helm upgrade --install myapp-staging $CHART_PATH
--namespace staging
--values $CHART_PATH/values-staging.yaml
--set image.tag=$CI_COMMIT_SHA
--wait --timeout 10m
- helm test myapp-staging --namespace staging
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
deploy-production:
stage: deploy-production
environment:
name: production
url: https://app.example.com
script:
- kubectl config use-context production-cluster
- helm upgrade --install myapp-prod $CHART_PATH
--namespace production
--values $CHART_PATH/values-production.yaml
--set image.tag=$CI_COMMIT_SHA
--wait --timeout 15m
- helm test myapp-prod --namespace production
when: manual
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
Create environment-specific values files
Create separate values files for different environments to manage configuration differences.
replicaCount: 1
image:
repository: registry.example.com/myapp
tag: latest
service:
type: NodePort
port: 80
ingress:
enabled: true
hosts:
- host: staging.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: staging-tls
hosts:
- staging.example.com
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
postgresql:
enabled: true
auth:
database: "myapp_staging"
replicaCount: 3
image:
repository: registry.example.com/myapp
tag: stable
service:
type: ClusterIP
port: 80
ingress:
enabled: true
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: production-tls
hosts:
- app.example.com
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
postgresql:
enabled: false # Use external database in production
Verify your setup
Test your Helm installation and configurations with these verification commands:
# Verify Helm version and functionality
helm version
helm list --all-namespaces
Test repository access
helm repo list
helm search repo nginx
Verify RBAC configuration
kubectl auth can-i create deployments --as=system:serviceaccount:kube-system:helm-service-account
kubectl get clusterrolebindings | grep helm
Check deployed releases
helm list --namespace myapp
helm status myapp-release --namespace myapp
Verify chart signing
helm verify myapp-0.1.0.tgz
Test rollback functionality
helm history myapp-release --namespace myapp
helm rollback myapp-release 1 --namespace myapp --dry-run
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| "release not found" error | Release installed in different namespace | helm list --all-namespaces to find release |
| Repository authentication fails | Incorrect credentials or expired tokens | helm repo remove repo-name && helm repo add repo-name URL --username user --password pass |
| Chart dependencies not found | Dependencies not updated | helm dependency update before installation |
| Permission denied errors | Insufficient RBAC permissions | Review and update ClusterRole permissions in helm-rbac.yaml |
| Upgrade hangs or times out | Resource constraints or failed readiness probes | helm upgrade --timeout 15m and check pod logs |
| Chart verification fails | Missing or incorrect GPG keys | gpg --import public-key.asc and verify key trust |
| Values not applied correctly | YAML syntax errors or wrong file path | helm template to debug value interpolation |
| Rollback fails | Previous revision corrupted or missing | helm history release-name and rollback to known good revision |
Next steps
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Default values
HELM_VERSION="v3.13.2"
PRIVATE_REPO_URL=""
PRIVATE_REPO_USER=""
PRIVATE_REPO_PASS=""
# Usage message
usage() {
echo "Usage: $0 [options]"
echo "Options:"
echo " -v, --version VERSION Helm version to install (default: $HELM_VERSION)"
echo " -r, --repo-url URL Private repository URL"
echo " -u, --repo-user USER Private repository username"
echo " -p, --repo-pass PASS Private repository password"
echo " -h, --help Show this help message"
exit 1
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-v|--version)
HELM_VERSION="$2"
shift 2
;;
-r|--repo-url)
PRIVATE_REPO_URL="$2"
shift 2
;;
-u|--repo-user)
PRIVATE_REPO_USER="$2"
shift 2
;;
-p|--repo-pass)
PRIVATE_REPO_PASS="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo -e "${RED}Unknown option: $1${NC}"
usage
;;
esac
done
# Cleanup function for rollback
cleanup() {
echo -e "${RED}Installation failed. Cleaning up...${NC}"
rm -f /tmp/helm-*.tar.gz
rm -rf /tmp/linux-amd64
rm -f /usr/share/keyrings/helm.gpg 2>/dev/null || true
rm -f /etc/apt/sources.list.d/helm-stable-debian.list 2>/dev/null || true
}
trap cleanup ERR
# Check prerequisites
echo -e "${YELLOW}[1/9] Checking prerequisites...${NC}"
if [[ $EUID -ne 0 ]] && ! command -v sudo >/dev/null 2>&1; then
echo -e "${RED}Error: This script requires root privileges or sudo${NC}"
exit 1
fi
# Auto-detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
PKG_UPGRADE="apt upgrade -y"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf check-update || true"
PKG_INSTALL="dnf install -y"
PKG_UPGRADE="dnf update -y"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum check-update || true"
PKG_INSTALL="yum install -y"
PKG_UPGRADE="yum update -y"
;;
*)
echo -e "${RED}Unsupported distribution: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}Cannot detect distribution. /etc/os-release not found.${NC}"
exit 1
fi
echo -e "${GREEN}Detected distribution: $PRETTY_NAME${NC}"
# Update system packages
echo -e "${YELLOW}[2/9] Updating system packages...${NC}"
sudo $PKG_UPDATE
sudo $PKG_UPGRADE
# Install dependencies
echo -e "${YELLOW}[3/9] Installing required dependencies...${NC}"
case "$PKG_MGR" in
apt)
sudo $PKG_INSTALL curl gnupg2 software-properties-common apt-transport-https ca-certificates
;;
dnf|yum)
sudo $PKG_INSTALL curl gnupg2 ca-certificates
;;
esac
# Check if kubectl is available
echo -e "${YELLOW}[4/9] Checking Kubernetes connectivity...${NC}"
if ! command -v kubectl >/dev/null 2>&1; then
echo -e "${YELLOW}Warning: kubectl not found. Install kubectl and configure cluster access before using Helm.${NC}"
else
if kubectl cluster-info >/dev/null 2>&1; then
echo -e "${GREEN}Kubernetes cluster connectivity verified${NC}"
else
echo -e "${YELLOW}Warning: Cannot connect to Kubernetes cluster. Configure kubectl before using Helm.${NC}"
fi
fi
# Install Helm based on distribution
echo -e "${YELLOW}[5/9] Installing Helm $HELM_VERSION...${NC}"
case "$PKG_MGR" in
apt)
# Add Helm GPG key and repository for Debian/Ubuntu
curl -fsSL https://baltocdn.com/helm/signing.asc | sudo gpg --dearmor -o /usr/share/keyrings/helm.gpg
sudo chmod 644 /usr/share/keyrings/helm.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo chmod 644 /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt update
sudo $PKG_INSTALL helm
;;
*)
# Manual installation for RHEL-based distributions
HELM_TARBALL="helm-${HELM_VERSION}-linux-amd64.tar.gz"
cd /tmp
curl -fsSL "https://get.helm.sh/${HELM_TARBALL}" -o "$HELM_TARBALL"
tar -zxf "$HELM_TARBALL"
sudo mv linux-amd64/helm /usr/local/bin/helm
sudo chmod 755 /usr/local/bin/helm
sudo chown root:root /usr/local/bin/helm
rm -rf linux-amd64 "$HELM_TARBALL"
;;
esac
# Verify Helm installation
echo -e "${YELLOW}[6/9] Verifying Helm installation...${NC}"
if helm version --short; then
echo -e "${GREEN}Helm installed successfully${NC}"
else
echo -e "${RED}Helm installation verification failed${NC}"
exit 1
fi
# Configure standard repositories
echo -e "${YELLOW}[7/9] Adding standard Helm repositories...${NC}"
helm repo add stable https://charts.helm.sh/stable
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
echo -e "${GREEN}Standard repositories added${NC}"
# Configure private repository if provided
echo -e "${YELLOW}[8/9] Configuring repositories...${NC}"
if [[ -n "$PRIVATE_REPO_URL" ]]; then
if [[ -n "$PRIVATE_REPO_USER" && -n "$PRIVATE_REPO_PASS" ]]; then
helm repo add private-repo "$PRIVATE_REPO_URL" --username "$PRIVATE_REPO_USER" --password "$PRIVATE_REPO_PASS"
echo -e "${GREEN}Private repository added with authentication${NC}"
else
helm repo add private-repo "$PRIVATE_REPO_URL"
echo -e "${GREEN}Private repository added${NC}"
fi
fi
# Update repositories
helm repo update
echo -e "${GREEN}Repository indexes updated${NC}"
# Final verification
echo -e "${YELLOW}[9/9] Final verification...${NC}"
echo -e "${GREEN}Installed Helm version:${NC}"
helm version --short
echo -e "${GREEN}Configured repositories:${NC}"
helm repo list
echo -e "${GREEN}Testing repository search:${NC}"
helm search repo nginx --max-col-width=80 | head -5
echo ""
echo -e "${GREEN}✅ Helm 3 installation completed successfully!${NC}"
echo ""
echo -e "${YELLOW}Next steps:${NC}"
echo "1. Ensure kubectl is configured to connect to your Kubernetes cluster"
echo "2. Create your first chart: helm create myapp"
echo "3. Install a chart: helm install myrelease stable/nginx-ingress"
echo "4. List releases: helm list"
echo ""
echo -e "${YELLOW}Security recommendations:${NC}"
echo "• Configure RBAC for Helm service accounts"
echo "• Use --verify flag when installing charts"
echo "• Regularly update chart repositories: helm repo update"
echo "• Store sensitive values in Kubernetes secrets, not values.yaml"
Review the script before running. Execute with: bash install.sh