Operator-based Helm chart for deploying Mattermost Enterprise Edition on Kubernetes with Redis caching and VPN sidecar support.
- Operator-based deployment using Mattermost CRD (
installation.mattermost.com/v1beta1) - In-cluster Redis via Bitnami subchart for cluster caching
- OpenVPN sidecar for internal network access with split-tunnel routing
- TLS via cert-manager with automatic certificate management
- GitHub Actions CI/CD with automated health checks and rollback
- Kubernetes 1.21+
- Helm 3.x
- Mattermost Operator installed in the cluster
- cert-manager with a ClusterIssuer (e.g.,
letsencrypt-prod) - nginx ingress controller
Create these secrets in the target namespace before deploying:
| Secret | Keys | Description |
|---|---|---|
mysql-connection |
DB_CONNECTION_STRING |
PostgreSQL/MySQL connection string |
s3-access-key |
accesskey, secretkey |
S3-compatible storage credentials |
mattermost-license |
license |
Mattermost Enterprise license |
openvpn-config |
config.ovpn |
OpenVPN client configuration file |
openvpn-credentials |
username, password |
OpenVPN authentication |
# OpenVPN configuration
kubectl -n mattermost create secret generic openvpn-config \
--from-file=config.ovpn=/path/to/your/client.ovpn
# OpenVPN credentials
kubectl -n mattermost create secret generic openvpn-credentials \
--from-literal=username='YOUR_VPN_USERNAME' \
--from-literal=password='YOUR_VPN_PASSWORD'kubectl -n mattermost create secret generic mysql-connection \
--from-literal=DB_CONNECTION_STRING='postgres://user:pass@host:5432/dbname?sslmode=require'kubectl -n mattermost create secret generic s3-access-key \
--from-literal=accesskey='YOUR_ACCESS_KEY' \
--from-literal=secretkey='YOUR_SECRET_KEY'kubectl -n mattermost create secret generic mattermost-license \
--from-file=license=/path/to/license.mattermost-license# Update dependencies
helm dependency update ./charts/mattermost
# Install
helm upgrade --install mattermost ./charts/mattermost \
--namespace mattermost \
--timeout 5m \
--atomichelm install mattermost ./charts/mattermost \
--namespace mattermost \
--dry-run --debughelm template mattermost ./charts/mattermost \
--namespace mattermost \
> rendered.yaml| Parameter | Description | Default |
|---|---|---|
mattermost.version |
Mattermost version | 11.1.1 |
mattermost.image |
Docker image | mattermost/mattermost-enterprise-edition |
mattermost.replicas |
Number of replicas | 2 |
mattermost.size |
Size preset | 5000users |
mattermost.database.secret |
Database secret name | mysql-connection |
mattermost.fileStore.bucket |
S3 bucket name | glia-group |
mattermost.fileStore.url |
S3 endpoint | nyc3.digitaloceanspaces.com |
mattermost.fileStore.secret |
S3 credentials secret | s3-access-key |
mattermost.licenseSecret |
License secret name | mattermost-license |
redis.enabled |
Enable Redis subchart | true |
vpn.enabled |
Enable VPN sidecar | true |
vpn.routes |
CIDR ranges to route via VPN | ["192.168.1.0/24"] |
ingress.enabled |
Enable ingress | true |
ingress.host |
Primary hostname | group.glia.org |
redirectIngress.enabled |
Enable redirect ingress | true |
redirectIngress.host |
Redirect hostname | group.emlondon.ca |
mattermost:
version: "11.1.1"
image: mattermost/mattermost-enterprise-edition
imagePullPolicy: IfNotPresent
replicas: 2
size: 5000users
database:
secret: mysql-connection
fileStore:
bucket: glia-group
url: nyc3.digitaloceanspaces.com
secret: s3-access-key
licenseSecret: mattermost-license
env:
- name: MM_FILESETTINGS_AMAZONS3SSE
value: "true"
- name: MM_FILESETTINGS_AMAZONS3SSL
value: "true"
- name: MM_SERVICESETTINGS_ENABLEAPIUSERDELETION
value: "true"
- name: MM_LDAPSERVER
value: auth.emlondon.ca
dnsConfig:
nameservers:
- 8.8.8.8
resources:
limits:
cpu: 4
memory: 8Gi
requests:
cpu: 500m
memory: 500Mi
redis:
enabled: true
auth:
enabled: true
master:
persistence:
enabled: false
vpn:
enabled: true
image: ghcr.io/wrmilling/openvpn-client:latest
configSecret: openvpn-config
credentialsSecret: openvpn-credentials
routes:
- 192.168.1.0/24
ingress:
enabled: true
host: group.glia.org
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
kubernetes.io/ingress.class: nginx
tlsSecret: mm-glia
redirectIngress:
enabled: true
host: group.emlondon.ca
target: https://group.glia.org$request_uri
tlsSecret: mm-redirect-emlondon-tls
crdName: mm-glia┌──────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ Namespace: mattermost │
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ mm-glia Pod ││
│ │ ┌─────────────────┐ ┌─────────────────┐ ││
│ │ │ mattermost │ │ openvpn-client │ ││
│ │ │ :8065 (app) │ │ (tun0) │ ││
│ │ │ :8067 (metrics)│ │ 192.168.1.x→VPN│ ││
│ │ │ │ │ others→default │ ││
│ │ └────────┬────────┘ └─────────────────┘ ││
│ │ │ shared netns ││
│ └───────────┼─────────────────────────────────────────────────┘│
│ │ │
│ ┌───────────▼───────────┐ ┌─────────────────────────────┐ │
│ │ mm-glia Service │ │ Redis Master │ │
│ │ ClusterIP: None │ │ :6379 │ │
│ │ 8065, 8067 │ │ (cluster cache) │ │
│ └───────────┬───────────┘ └─────────────────────────────┘ │
│ │ │
│ ┌───────────▼───────────┐ │
│ │ Ingress │ │
│ │ group.glia.org │ │
│ │ group.emlondon.ca → │ │
│ │ redirect to glia │ │
│ └───────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
- Add a repository secret named
KUBE_CONFIGcontaining a base64-encoded kubeconfig:
kubectl config view --raw --minify | base64 -w 0- The workflow automatically:
- Lints the Helm chart on PRs
- Deploys on push to
main - Waits for Mattermost CR to reach
stablestate - Rolls back on failure
- Push to main: Automatic deployment
- Pull request: Lint only (no deployment)
- Manual dispatch: Deploy via GitHub UI
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Lint │ ──▶ │ Deploy │ ──▶ │ Health Check│
└─────────────┘ └─────────────┘ └──────┬──────┘
│
┌──────────▼──────────┐
│ Success │ Rollback │
└─────────────────────┘
# Mattermost CR status
kubectl get mattermost -n mattermost
# Pod status
kubectl get pods -n mattermost -l "installation.mattermost.com/installation=mm-glia"
# Redis status
kubectl get pods -n mattermost -l "app.kubernetes.io/name=redis"# Mattermost logs
kubectl logs -n mattermost -l "installation.mattermost.com/installation=mm-glia" -c mattermost -f
# VPN sidecar logs
kubectl logs -n mattermost -l "installation.mattermost.com/installation=mm-glia" -c openvpn-client -f# Port forward for local testing
kubectl port-forward -n mattermost svc/mm-glia 8065:8065
# Access at http://localhost:8065# Check CR events
kubectl describe mattermost mm-glia -n mattermost
# Check operator logs
kubectl logs -n mattermost -l "app=mattermost-operator" -f# Check VPN sidecar logs
kubectl logs -n mattermost -l "installation.mattermost.com/installation=mm-glia" -c openvpn-client
# Verify secrets exist
kubectl get secrets -n mattermost openvpn-config openvpn-credentials# Verify Redis is running
kubectl get pods -n mattermost -l "app.kubernetes.io/name=redis"
# Check Redis logs
kubectl logs -n mattermost -l "app.kubernetes.io/component=master" -f
# Test Redis connection from Mattermost pod
kubectl exec -n mattermost deployment/mm-glia -c mattermost -- \
nc -zv mm-glia-redis-master 6379# Update dependencies
helm dependency update ./charts/mattermost
# Upgrade
helm upgrade mattermost ./charts/mattermost \
--namespace mattermost \
--timeout 5m \
--atomichelm uninstall mattermost --namespace mattermostNote: This will not delete the pre-existing secrets or PVCs created by the operator.
This chart is provided as-is. Mattermost Enterprise requires a valid license.