Running Magento on Kubernetes for one shop is a solved problem: you write a Helm chart with your assumptions baked in. Running it for many shops, or across dev, staging, and production on the same code path, is not. Every team ends up making the same decisions about database provisioning, Redis layout, consumer Deployments, cron groups, Varnish VCL, multi-store dispatch, upgrade orchestration, and public-edge TLS, and each makes them slightly differently.
We built a Kubernetes operator so those decisions live in one place. mage-operator turns a Magento, Adobe Commerce, or Mage-OS store into a single declarative resource. You bring the container image, and the operator owns the Kubernetes shape.
Not affiliated with, endorsed by, or sponsored by Adobe Inc. "Magento" and "Adobe Commerce" are trademarks of Adobe Inc.
Open source on GitHub
mage-operator is Apache 2.0 and the repository is public. It isn't ready for production yet, but on a kind cluster a store reaches Ready in about five minutes. The install steps are at the end of this post.
Solved for one shop, unsolved for many
The top-ranked guide to Magento 2 on Kubernetes walks through everything you set up by hand (PHP-FPM and NGINX pod patterns, database, Elasticsearch, two Redis roles, Varnish, cron with fork-disabling environment variables, init-container migrations, autoscalers, ingress) and then says, honestly: "we wouldn't recommend you run it on production as-is." PHOENIX-MEDIA's Helm chart, the most credible packaged option, is battle tested in production. It still leaves domains, VCL customization, secrets, storage classes, and SMTP to you, and it doesn't address multi-store. The most comprehensive free guide covers image building, zero-downtime deploys, HA, monitoring, and security across dozens of chapters, and never mentions an operator, because until now there wasn't one to mention.
Each of those is a reasonable answer for one shop. Run five shops, or one shop across three environments, and the duplicated decisions start to drift apart. This is the golden-path argument applied to commerce: a golden path only helps when every shop and environment follows the same one.
One resource in practice
A minimal MageStore is about fifteen lines:
apiVersion: mage.aknostic.com/v1alpha1
kind: MageStore
metadata:
name: store
spec:
image:
repository: registry.example.com/myshop/magento
tag: "2.4.8"
workloads:
frontend:
replicas: 1
bootstrap:
admin:
email: admin@example.comApply it, wait for the Ready condition, and read the operator-generated admin password from a Secret. The full spec covers what the guides have you assemble by hand:
| You used to decide | MageStore spec group |
|---|---|
| Database provisioning, users, backups | database (composed MariaDB, or external) |
| Search cluster | search (composed OpenSearch, or external) |
| Cache & sessions layout | cache (managed Valkey, or external) |
| Message queue | queue (composed RabbitMQ, or external) |
| Frontend scaling | workloads |
| Queue consumers as Deployments | consumers |
| Cron groups | cron |
| Media storage (S3) | media |
| Varnish + VCL | varnish |
| Transactional mail | smtp |
| Hosts, TLS, public edge | routing (Envoy Gateway) |
| Multi-store dispatch | stores |
| First install & admin | bootstrap |
| Version migrations | upgrade |
| NetworkPolicies, workload security contexts | security (v0.7.0 arc, in progress) |
| Admin URL path | bootstrap.admin.backendFrontname |
Two contracts keep it portable
First, the image contract. The operator never builds or modifies your Magento image. You bring one that honors a documented interface, and if you want to try the operator before building your own, the repo includes a reference test image that satisfies it.
Second, configuration through environment variables only. The operator does not generate env.php. It emits a Secret of individual environment variables (MAGENTO_DATABASE_HOST, MAGENTO_REDIS_HOST, and so on), and your env.php reads them with getenv(). We considered an ECE-Tools emission mode and dropped it. ECE-Tools is an Adobe Commerce Cloud construct, and this operator is for traditional-hosting shops on Hetzner, Scaleway, bare metal, or self-hosted Kubernetes.
Composing existing operators
The operator stays small by leaving solved problems to the projects that already solve them. mariadb-operator, opensearch-k8s-operator, and rabbitmq-cluster-operator handle databases, search, and queues, and Envoy Gateway handles the public edge. What's left for mage-operator is the Magento-specific glue: wiring those systems into one store, watching one resource, and reporting its status conditions.
Composition is the default, and every composed dependency has an external escape hatch. Point spec.database.external at your managed MySQL and the operator skips the composed database entirely.
The controller design follows the Kubebuilder good practices: idempotent reconciliation, and status conditions per the Kubernetes API conventions. Those practices say little about when to depend on someone else's operator and when to stop.
Where composition stops: CROSSSLOT
The first cache implementation composed a Valkey operator, the same way as MariaDB and OpenSearch. We reverted it, because with that operator in place setup:upgrade fails during cache-tag cleanup:
CROSSSLOT Keys in request don't hash to the same slot
ADR 009 traces the root cause. The upstream Valkey operator runs every deployment with cluster-enabled yes regardless of size, so even a single instance is in cluster mode. Magento's Redis cache backend sends multi-key DEL operations during cache-tag cleanup, and in cluster mode those fail unless every key hashes to the same slot. Magento's keys don't, and short of rewriting the cache backend there is no fix on the Magento side. Adobe has an open issue for it (#39224).
So the cache is a bare StatefulSet that the operator manages itself, with Sentinel for HA. It looks less elegant on the architecture diagram, but it matches Magento's cache semantics. Keeping the Valkey operator would have made every future cache bug depend on someone else's cluster topology.
Upgrades behind a fence
Everything above happens at install time, which is where the existing guides stop. The operator differs from them most in what happens when you upgrade.
Magento ships schema and data migrations with every release, and setup:upgrade is not transactional. ADR 008 spells out what that means: the command iterates over every module's schema and data patches and commits each one as it runs. "If the seventh patch of twenty-three fails, patches one through six are already in the database. There is no rollback."
So changing spec.image.tag starts a sequence:
- The fence goes up. A Service-selector flip takes the storefront out of traffic, and the operator holds consumers and cron so nothing writes into a half-migrated database.
setup:upgraderuns once, as a Job, against the new image.- The operator flushes the cache, then rolls the workloads to the new tag.
- The fence comes down. Traffic returns only when the schema and the code agree.
The existing paths don't model this boundary. The init-container pattern runs migrations on every pod start while traffic is live, and helm upgrade --wait confirms the rollout is ready without modelling Magento's schema migration at all.
How it was built: a two-hour calibration and 15 releases in 13 days
The build process left its artifacts in the repo, starting with a timed experiment. TIMING.md records a two-hour calibration session on 22 April 2026, from 04:52 to 06:40, with a budget table and actuals per slot. Its stated goal was to find out "what is underspecified, what the plan got right, and where the estimate will hold or break". v0.1.0 shipped three days later, and v0.6.4 thirteen days after that: fifteen releases, each with a changelog entry. Upgrade orchestration landed in v0.2.0 and the cache topology change in v0.3.0, and a sixteenth release arc, the v0.7.0 security baseline, is already underway.
Every load-bearing decision got an ADR. There are thirteen so far (ADR 001 is reserved and, in the repo's own words, "not yet written"), covering config emission modes, bootstrap as a Job, upgrade orchestration, cache topology, TLS at the public edge, base-URL derivation, and the security baseline. Development ran as planned sessions in Claude Code, with the repo carrying its own working context in CLAUDE.md, per-session plans, and per-session changelog entries. Claude Code assisted with the engineering, and the artifacts record the discipline around it: timed calibration, ADRs, and semver from day one.
We used the same ship-early, iterate-in-public approach for our GoAlert provisioning operator. Releases run through our own GitLab CI, which builds distroless images signed with cosign, uses SOPS-encrypted secrets, and runs end-to-end tests on a kind cluster, including upgrade and cache scenarios.
Where it stands
Fifteen releases in, the repo is candid about its limits. The changelog puts the current release at v0.6.4, with the v0.7.0 security-baseline arc in progress. ADR 014 defines its shape, a spec.security group has landed, and admin-URL randomization already works end to end. New stores get a random 16-character admin path stored in a Secret. Existing stores keep /admin/ on purpose, because breaking bookmarks in a patch release would violate the project's upgrade-safety rule. The README's status section is several versions behind the changelog.
Do not use this in production yet
The README warns against production use, and that warning still holds. Validation on real infrastructure (EKS and Scaleway Kapsule) runs in parallel with the releases.
Try it yourself
The operator is Apache 2.0 and the repository is public on GitHub. Install the dependency operators, install the mage-operator Helm chart from the OCI registry, apply a minimal MageStore pointing at an image that honors the image contract, and run kubectl wait --for=condition=Ready. On a kind cluster that takes about five minutes. You don't need to build your own image first: the bundled reference image works, though its first build takes around 30 minutes. If something breaks, file an issue in the repo.
If what you want is a commerce platform on Kubernetes that your team runs with confidence, across environments and through upgrades, that is the platform engineering work we do, and this operator is our version of it, in public.