25.09.2026・TechStuff
25.09.2026・TechStuff

Shopware 6 Hidden Gems #22: cart:migrate — switching cart storage on a live shop

Fabian Blechschmidt

Moving carts to Redis is one of the standard performance upgrades for busy Shopware installations — the cart table is one of the hottest write paths in the whole system, and MySQL isn’t thrilled about it. The hosting docs cover the configuration. What they skip is the operational question that decides whether you can actually do it on a shop with traffic: what happens to the carts that exist right now?

Flip the storage config on a Friday afternoon and every active cart vanishes. Not deleted — just invisible, sitting in a backend nobody reads anymore. Customers mid-checkout lose their cart. Support tickets follow.

The core has the answer, and it’s one command with a name only the commands reference knows: cart:migrate.

The gem

# after switching config from MySQL to Redis:
bin/console cart:migrate sql

# rolling back from Redis to MySQL:
bin/console cart:migrate redis

The single argument is the source storage (Checkout/Cart/Command/CartMigrateCommand.php:60-83) — you tell it where the carts currently live, and it copies them to the other side. Both directions, same command. Batched (50 per chunk), with a progress bar, using the same compression and serialization the cart persisters use — so a compressed Redis cart lands as a valid MySQL cart and vice versa.

The details are quietly solid: the SQL side iterates the cart table by auto-increment cursor (createIterator(), lines 86-97 — keyset pagination, part 9 says hello), the Redis side scans for the persister’s key prefix, and expiry is respected: carts get their remaining TTL carried over based on the configured shopware.cart.expire_days.

The second argument nobody knows

bin/console cart:migrate redis "redis://old-redis-host:6379/2"

The optional url argument creates a separate Redis connection for the source (execute(), lines 69-72). That unlocks the scenarios the plain form can’t do:

The runbook

For the standard MySQL→Redis upgrade on a live shop:

  1. Configure Redis cart storage (shopware.cart.storage), deploy — new carts now go to Redis.
  2. Immediately run bin/console cart:migrate sql — existing MySQL carts are copied over, with sensible TTLs.
  3. Done. No maintenance window, the overlap is seconds.

And the part I appreciate most: the rollback is symmetrical. If Redis turns out to be misconfigured or undersized, cart:migrate redis brings every cart back to MySQL. Storage migrations you can reverse are storage migrations you can attempt on a Tuesday.

TL;DR

bin/console cart:migrate <redis|sql> [url] copies all carts from one storage backend to the other — batched, compressed correctly, TTL-aware, in both directions, optionally from a different Redis via the URL argument (infrastructure moves, disaster recovery). It’s the missing operational half of the documented „use Redis for carts“ advice: switch the config, migrate the existing carts, lose nobody’s checkout.

Next up: sitemap:generate --force — and why your sitemap sometimes silently stops updating.

Migrated carts recently? Drop me an email — especially if the rollback saved you!


Found in Shopware 6.7.0.0: Checkout/Cart/Command/CartMigrateCommand.php — source argument and custom Redis URL at lines 60-72, keyset iterator at 86-97, Redis key scan and batched multi-insert at 100-120, TTL handling via injected expireDays.