ddtcorex

28 Sept 2026 · 6 min read

Migrating a Magento project from Warden to Govard

A step-by-step Warden to Govard migration map with side-by-side commands, service equivalents and a rollback-safe cutover checklist

Part 2 of the Govard in Depth series — moving a real Magento project off Warden without losing a day.

Migrate on a branch, not on faith

The safest migration never touches the working Warden setup until Govard demonstrably works. Create a migration branch, add Govard configuration beside the existing .warden directory, and let both stacks coexist. Developers keep shipping on Warden while you prove Govard on the same codebase. Only when govard env up plus database import plus storefront 200 all pass do you flip the documented default.

This coexistence is possible because Govard keeps its state in .govard.yml and its own containers and volumes. It does not rewrite Warden files, and Warden does not know Govard exists. The one resource you must separate is ports and hostnames: give the Govard environment a distinct URL during the trial so both routers can run without fighting over port 443.

Mapping the two stacks side by side

Start by inventorying what Warden runs for this project, then write the Govard equivalent. A typical Magento Warden project has a PHP-FPM container, MariaDB or MySQL, Redis, Elasticsearch or OpenSearch, plus the shared Warden footprint (Traefik, Mutagen, DNS resolver). Govard folds the per-project pieces into its blueprint and replaces the shared footprint with its own lean proxy.

Concern Warden Govard
Project definition .warden/warden-env.yml + .warden/.env .govard.yml at repo root
Start/stop warden env up / warden env down govard env up / govard env down
Global services warden svc up (Traefik, Mutagen) Built-in proxy, no extra step
File sync (macOS) Mutagen sessions Native mounts plus profile tuning
PHP commands warden env exec php-fpm bin/magento ... govard magento ... shortcut
Database export warden db dump / import variants govard db dump / govard db import

The base configuration for the migrated project usually fits on one screen:

# .govard.yml (base layer)
framework: magento2
php:
  version: "8.3"
services:
  database: mariadb:11.4
  cache: redis:7
  search: opensearch:2
domains:
  app: app.magento.test

Compare versions deliberately. If Warden runs MariaDB 10.6 and PHP 8.2, do not "upgrade while migrating" unless you intend to. Match first, verify, then bump versions as a separate change. Migration debugging is hard enough without a simultaneous PHP upgrade hiding in the diff.

Step 1: scaffold and boot empty

With the branch checked out, scaffold the Govard config and boot the empty stack:

govard env up
govard env ps
govard config profile

govard env ps should show PHP-FPM, database, Redis, and search containers healthy. govard config profile prints the effective merged configuration; confirm the PHP version and service images match what you wrote, not what some profile override injected. If the storefront shows an install screen or a connection error at this stage, that is expected: no code or data has been wired in yet beyond the skeleton.

Keep Warden running while you do this only if ports do not collide. If both stacks claim the same hostname, stop one before judging the other. The trial hostname in the snippet above exists precisely to avoid this confusion.

Step 2: carry the database and media across

Export from Warden, import into Govard. The exact Warden dump command varies by project age, but the shape is stable: produce a plain .sql file, then feed it to Govard:

# from the Warden stack
warden db dump > /tmp/warden-base.sql

# from the Govard stack (same branch, same commit)
govard db import --file /tmp/warden-base.sql
govard magento cache:flush

Media follows the same path. If the project stores pub/media on the host (typical with Warden bind mounts), the Govard environment mounting the same repo root sees it already. If media lives in a Docker volume, copy it out first with docker cp and place it under pub/media before booting Govard, or accept placeholder images for the trial and sync full media later.

After import, update base URLs to the Govard hostname and flush cache. Then verify the storefront, the admin login, and one checkout-adjacent page (cart or checkout). Search deserves its own check: trigger a full reindex and run one catalog search query, because search backend mismatches (Elasticsearch 7 versus OpenSearch 2) are the most common silent breakage in migrations.

Step 3: port the daily commands

Migrations fail socially more often than technically: developers type old commands from muscle memory and conclude the new tool is broken. Write the translation table into the project README during migration week:

warden env up                  # becomes: govard env up
warden env exec php-fpm bash   # becomes: govard sh php
warden env exec php-fpm bin/magento cache:flush  # becomes: govard magento cache:flush
warden db dump                 # becomes: govard db dump --out /tmp/base.sql

Cover Xdebug, cron, and consumers explicitly, because these are where Warden projects accumulate local customizations. If the project relied on warden debug enable, record the Govard Xdebug equivalent and verify a breakpoint lands in the IDE before declaring parity. If cron ran inside Warden's PHP container, confirm Govard's cron handling matches (schedule, user, output). Queue consumers and indexers running as separate processes need the same treatment.

Step 4: cut over and clean up

Cutover is a documentation change plus a branch merge, not a flag day. When two or three developers have worked a full day each inside Govard (code, reindex, place a test order, run the test suite), update the README to make govard env up the default, merge the migration branch, and keep Warden files for one sprint as a safety net. Deleting .warden in the same merge that introduces .govard.yml forces anyone with an in-flight Warden database to recover; deleting it two weeks later costs nothing.

The rollback plan stays trivial during that window: govard env down, warden env up, continue. No data has been destroyed because each stack owns its volumes. Say this explicitly in the migration announcement; it is what gives hesitant teammates permission to try.

Pitfalls seen in real migrations

  • Migrating and upgrading PHP together. Match versions first. Bump PHP as a follow-up commit with its own test run.
  • Search backend drift. Elasticsearch versus OpenSearch, major version mismatches, and missing index prefixes break search silently. Reindex and query after every import.
  • Mutagen muscle memory. Developers will run sync commands that no longer exist and blame Govard. The README table prevents this.
  • Hostname collisions. Running both routers against the same domain produces failures that look like application bugs. Use a distinct trial hostname until cutover.
  • Forgetting cron and consumers. The storefront works, but scheduled jobs silently stop. Verify cron output and consumer status, not just pages.

Next in this series: govard-go-internals

govardmagento2local-dev