Inhoud van dit artikel
Magento 2 draait goed op Kubernetes als je de applicatie stateless maakt: PHP-FPM en webserver in pods zonder lokale data, media op object storage, sessies en cache in Valkey of Redis, cron en queue-consumers als aparte workloads, en de database bij voorkeur managed buiten het cluster. Static content en DI-compilatie bouw je in het image, nooit bij het opstarten. Hieronder per component hoe wij dat inrichten, met de YAML erbij.
Het overzicht
Een productie-opzet bestaat uit deze onderdelen:
| Component | Kubernetes-object | Waar staat de data? |
|---|---|---|
| Gateway / TLS | Gateway + HTTPRoute | Certificaten via cert-manager |
| Varnish | Deployment of StatefulSet | In geheugen, wegwerpbaar |
| Nginx + PHP-FPM | Deployment | Nergens, stateless |
| Cron | CronJob | Nergens |
| Queue-consumers | Deployment per consumer | RabbitMQ of database-queue |
| Valkey / Redis | StatefulSet of managed | Cache en sessies |
| OpenSearch | Operator of managed | Zoekindex |
| Database | Managed, buiten het cluster | MySQL of MariaDB |
| Media | Object storage of RWX-volume | Buiten de pods |
Het verkeer loopt van de Gateway naar Varnish, en van Varnish naar Nginx met PHP-FPM. Alles wat state heeft, staat buiten de web-pods.
Controleer de versies altijd tegen de officiële systeemeisen van Adobe. Volgens de huidige tabel ondersteunt Magento 2.4.8-p5 PHP 8.3 en 8.4, OpenSearch 3 en Valkey 8.1. Magento 2.4.9 gaat naar PHP 8.5, Valkey 9, MySQL 8.4 of MariaDB 12.3, RabbitMQ 4.3 en Varnish 8. Bouw je images op een combinatie die Adobe ondersteunt.
Eén image voor alles
We bouwen één image per release. Web, cron en consumers draaien hetzelfde image, alleen met een ander commando. Zo weet je zeker dat cron dezelfde code draait als de frontend.
In het image zitten:
- de code via
composer install --no-dev; - de gecompileerde DI (
setup:di:compile); - de static content (
setup:static-content:deploy); - een
app/etc/config.phpmet de modules, scopes en thema's.
Niet in het image: app/etc/env.php met wachtwoorden, pub/media en var/. Hoe je dat image bouwt en uitrolt zonder downtime, staat in Magento deployen op Kubernetes zonder downtime.
Nginx en PHP-FPM
We draaien Nginx en PHP-FPM als twee containers in dezelfde pod. Ze delen de code via het image en praten over 127.0.0.1:9000. Dat houdt de configuratie simpel en de latency laag.
apiVersion: apps/v1
kind: Deployment
metadata:
name: magento-web
spec:
replicas: 3
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: magento-web
template:
metadata:
labels:
app: magento-web
spec:
terminationGracePeriodSeconds: 30
containers:
- name: php-fpm
image: registry.example.nl/shop/magento:1f3c9a2
resources:
requests:
cpu: "500m"
memory: 1Gi
limits:
memory: 2Gi
volumeMounts:
- name: env-php
mountPath: /var/www/html/app/etc/env.php
subPath: env.php
- name: nginx
image: registry.example.nl/shop/magento-nginx:1f3c9a2
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health_check.php
port: 8080
periodSeconds: 5
lifecycle:
preStop:
exec:
command: ["sleep", "10"]
volumes:
- name: env-php
secret:
secretName: magento-env-php
De punten die ertoe doen:
/health_check.phpzit standaard in Magento en controleert onder meer de databaseverbinding en de cache. Gebruik hem voor de readiness probe, niet voor de liveness probe. Anders herstart Kubernetes al je pods tegelijk als de database even hapert.preStopmet een korte sleep geeft de load balancer tijd om de pod uit rotatie te halen voordat PHP-FPM stopt.- Een geheugen-limit, geen CPU-limit. CPU-limits veroorzaken throttling bij pieken. Geheugen-limits beschermen de node tegen een weggelopen proces.
Voeg een PodDisruptionBudget toe, zodat een node-upgrade nooit alle web-pods tegelijk weghaalt.
Varnish
Varnish draait als eigen workload voor de web-pods. Magento genereert de VCL met bin/magento varnish:vcl:generate; die zetten we in een ConfigMap.
Het lastige punt is purgen. Magento stuurt een PURGE-request naar elke host in http_cache_hosts. Draai je drie Varnish-replica's achter één Service, dan komt een purge maar bij één replica aan. De andere twee serveren oude content.
Twee oplossingen:
- Varnish als StatefulSet met een headless Service, en elke pod los in de configuratie:
bin/magento setup:config:set \
--http-cache-hosts=varnish-0.varnish:6081,varnish-1.varnish:6081
- Eén Varnish-instantie met genoeg geheugen en een snelle herstart. Bij veel shops is dat voldoende, en het is eenvoudiger.
Hoe je Varnish voor Magento afstelt, los van Kubernetes, lees je in Varnish configureren voor Magento 2.
Gateway API en TLS
Het community-project ingress-nginx is in maart 2026 met pensioen gegaan: geen releases en geen securityfixes meer. Voor nieuwe clusters gebruiken we de Gateway API, met een implementatie als Envoy Gateway, NGINX Gateway Fabric, Traefik of Cilium. Waarom en hoe je migreert: ingress-nginx stopt, wat nu?.
Een HTTPRoute die verkeer naar Varnish stuurt:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: shop
spec:
parentRefs:
- name: public
namespace: gateway
hostnames:
- www.voorbeeldshop.nl
rules:
- backendRefs:
- name: varnish
port: 6081
TLS termineert op de Gateway. Certificaten vraagt cert-manager aan en vernieuwt hij automatisch. Zorg dat Magento weet dat het verkeer via HTTPS binnenkwam, via de X-Forwarded-Proto-header en de offloader-instelling. Meer op onze pagina over ingress, Gateway API en TLS.
Sessies en cache in Valkey of Redis
Sessies in bestanden werken niet met meerdere pods. Een bezoeker die op een andere pod landt, is zijn winkelwagen kwijt. Sessies gaan dus naar Valkey of Redis, net als de default cache en de page cache als je geen Varnish gebruikt.
Een uitsnede uit env.php:
'session' => [
'save' => 'redis',
'redis' => [
'host' => getenv('SESSION_REDIS_HOST'),
'port' => '6379',
'database' => '0',
'disable_locking' => '1',
],
],
'cache' => [
'frontend' => [
'default' => [
'backend' => 'Magento\\Framework\\Cache\\Backend\\Redis',
'backend_options' => [
'server' => getenv('CACHE_REDIS_HOST'),
'port' => '6379',
'database' => '0',
],
],
],
],
We gebruiken aparte instanties voor sessies en cache. Een cache:flush mag nooit sessies raken, en een volle cache mag geen sessies wegdrukken.
Over de keuze tussen Valkey en Redis: Redis wijzigde in 2024 de licentie, waarna de Linux Foundation de fork Valkey startte. Redis 8 kwam in 2025 met AGPLv3 als extra licentie-optie. Magento ondersteunt Valkey officieel. Voor nieuwe opzetten kiezen wij Valkey.
Let bij Helm-charts op de bron van het image. Bitnami heeft in 2025 zijn gratis images grotendeels naar een legacy-repository verplaatst die geen updates meer krijgt. Een chart die nog naar die images wijst, draait in productie op ongepatchte software.
OpenSearch
Magento 2.4.8 en hoger gebruiken OpenSearch. Je kunt OpenSearch in het cluster draaien met de officiële OpenSearch Kubernetes Operator, of een managed variant van je cloudaanbieder nemen.
Onze afweging: in het cluster als je de nodes en het geheugen hebt en iemand die de index-upgrades begrijpt. Managed als je zoekfunctie bedrijfskritisch is en je geen zin hebt in shard-beheer. OpenSearch heeft persistent storage nodig en is gevoelig voor geheugen. Geef het eigen nodes of in elk geval harde requests.
De database: buiten het cluster
We zetten de database bijna altijd buiten het cluster, bij een managed dienst. De redenen:
- back-ups, point-in-time restore en failover zijn daar standaard geregeld;
- upgrades van MySQL of MariaDB zijn geen cluster-operatie;
- de database is de laatste component die je wilt laten meebewegen met node-upgrades.
Wil je toch in het cluster, gebruik dan een operator die back-ups en failover regelt, en test het herstel. Een back-up die je nooit hebt teruggezet, is een hoop, geen back-up.
Media en bestanden
pub/media moet gedeeld zijn tussen alle pods. Er zijn twee routes:
- Remote storage naar S3-compatibele object storage. Magento heeft dit ingebouwd sinds 2.4.2:
bin/magento setup:config:set \
--remote-storage-driver=aws-s3 \
--remote-storage-bucket=shop-media \
--remote-storage-region=eu-central-1
- Een ReadWriteMany-volume via NFS, CephFS of de bestandsdienst van je cloud. Werkt met elke extensie, maar is trager en een extra component om te beheren.
Remote storage is netter, maar test je extensies. Niet elke extensie schrijft via het Magento-filesystem; sommige gebruiken directe PHP-bestandsfuncties en breken dan.
var/ en generated/ horen niet op een gedeeld volume. Logs gaan naar stdout, zodat je log-stack ze oppikt.
Cron als CronJob
Magento verwacht dat bin/magento cron:run elke minuut draait. Op Kubernetes is dat een CronJob:
apiVersion: batch/v1
kind: CronJob
metadata:
name: magento-cron
spec:
schedule: "* * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 0
template:
spec:
restartPolicy: Never
containers:
- name: cron
image: registry.example.nl/shop/magento:1f3c9a2
command: ["bin/magento", "cron:run"]
concurrencyPolicy: Forbid voorkomt dat runs elkaar inhalen als een indexer lang duurt. Elke minuut een nieuwe pod starten kost wel opstarttijd. Bij zware cron-belasting draaien we daarom soms een Deployment met een eenvoudige lus die elke minuut cron:run aanroept. Vastlopende cron herken je aan dezelfde signalen als op een server: een groeiende cron_schedule-tabel en jobs die blijven hangen op status running.
Queue-consumers als Deployments
Standaard start Magento queue-consumers vanuit cron. Op Kubernetes zet je dat uit en draai je elke belangrijke consumer als eigen Deployment:
'cron_consumers_runner' => [
'cron_run' => false,
],
containers:
- name: consumer
image: registry.example.nl/shop/magento:1f3c9a2
command:
- bin/magento
- queue:consumers:start
- product_action_attribute.update
- --max-messages=1000
Met --max-messages stopt de consumer na een aantal berichten en start Kubernetes hem opnieuw. Dat houdt geheugenlekken in toom. Draai je RabbitMQ, dan kun je consumers met KEDA schalen op de queue-lengte. Meer over RabbitMQ in Magento: RabbitMQ in Magento 2: async processing. Over schalen: autoscaling met HPA, KEDA en Karpenter.
Secrets en configuratie
env.php is gewoon PHP. Wij laten het waarden lezen met getenv() en vullen die environment-variabelen vanuit Kubernetes Secrets. De Secrets zelf komen uit een externe vault via External Secrets Operator, zodat er geen wachtwoorden in Git staan.
Instellingen uit de admin die per omgeving verschillen, zoals base-URL's, zet je in env.php of via CONFIG__DEFAULT__...-variabelen. Dan zijn ze reproduceerbaar en niet afhankelijk van iemand die in de admin klikt. Hoe we secrets, RBAC en image-scans inrichten, staat op secrets en security op Kubernetes.
Waar we op letten bij de eerste opzet
- Requests op basis van metingen, niet op gevoel. Meet eerst een week, stel dan bij.
- Eén namespace per omgeving, met eigen Secrets en NetworkPolicies.
- Varnish-purge testen voordat je live gaat: wijzig een product en controleer elke Varnish-pod.
- Een loadtest op staging met cron en consumers actief, niet alleen de frontend.
- Een draaiboek voor database-herstel en voor het terugzetten van een release.
Het totaalplaatje met onze werkwijze staat op Magento 2 op Kubernetes.
Veelgestelde vragen
Moet static content echt in het image?+
Ja. Static content deployen bij het opstarten van een pod maakt opstarten traag en onvoorspelbaar, en elke pod doet hetzelfde werk opnieuw. In het image is het één keer gebouwd en identiek op elke pod.
Kan ik Magento sessies in de database opslaan?+
Dat kan, maar het belast je database met elke paginaweergave. Valkey of Redis is sneller en houdt de database vrij voor bestellingen en catalogusdata.
Hoeveel Varnish-pods heb ik nodig?+
Vaak één, met voldoende geheugen. Meerdere replica's kunnen, maar dan moet elke pod purges ontvangen. Dat vraagt een StatefulSet met adresseerbare pods in http_cache_hosts.
Werkt Hyvä anders op Kubernetes dan Luma?+
Niet wezenlijk. Hyvä bouwt de Tailwind-CSS bij de build; dat doe je in de image-build, net als de static content.
Is een RWX-volume voor pub/media een probleem?+
Het werkt, maar is trager dan lokale schijf en een extra component om te beheren. Remote storage naar object storage is schaalbaarder, mits je extensies ermee overweg kunnen.





