Performing an Upgrade

The Composer and bin/magento command sequence that moves a store to Magento 2.4.9, in the order that works.

Syntaxcomposer require-commerce magento/product-community-edition 2.4.9 --no-update

Performing the upgrade is two halves. Composer changes the code in vendor/. Then bin/magento rebuilds everything derived from that code: the database schema, the generated classes under generated/, and the static files under pub/static. Skipping the second half is why a store still behaves like the old version after a successful composer update.

The Composer half

Install Adobe's root update plugin once, with composer require magento/composer-root-update-plugin ~2.0 --no-update followed by composer update. It adds a require-commerce command that compares the default root composer.json of your current version with the target version's and applies the differences to your file, so autoload paths and scripts that changed between releases are not left behind. Then raise the metapackage version and resolve.

The Magento half

Order matters. setup:upgrade applies declarative schema and data patches and updates the module list. setup:di:compile regenerates factories, proxies and interceptors. setup:static-content:deploy is mandatory in production mode, where nothing is generated on demand. Then flush the cache and reindex.

What trips people up

Delete generated/ and the cache directories before setup:upgrade: stale interceptors from the old version are a common source of errors that look like your code's fault. On Adobe Commerce on cloud infrastructure these commands run in the build and deploy hooks instead of by hand.

Example

Example · bash
# Upgrading Magento Open Source to 2.4.9. Run from the project root.

# 1. Take the store out of service and stop background work
bin/magento maintenance:enable
bin/magento cron:remove

# 2. Keep a copy of the file about to be rewritten
cp composer.json composer.json.bak

# 3. Install Adobe's root update plugin (once per project)
composer require magento/composer-root-update-plugin ~2.0 --no-update
composer update

# 4. Raise the metapackage version, then resolve everything
composer require-commerce magento/product-community-edition 2.4.9 --no-update
composer update

# 5. Throw away everything generated by the old version
rm -rf generated/code/* generated/metadata/*
rm -rf var/cache/* var/page_cache/* var/view_preprocessed/*

# 6. Schema, data patches and module list
bin/magento setup:upgrade

# 7. Regenerate factories, proxies and interceptors
bin/magento setup:di:compile

# 8. Production mode only: nothing is built on request there
bin/magento setup:static-content:deploy -f en_US

# 9. Caches, indexes, cron, service
bin/magento cache:flush
bin/magento indexer:reindex
bin/magento maintenance:disable

# Then confirm the database agrees with the code
bin/magento setup:db:status
# All modules are up to date.

When to use it

  • Writing the runbook for a maintenance window, with the commands in the order they must run.
  • Diagnosing a store that still behaves like the old version after composer update succeeded.
  • Adding the upgrade commands to a deploy pipeline so the next upgrade is not done by hand.
  • Checking that the schema and data patches actually ran before reopening the store.

More examples

Abbreviated commands and the full names they stand for

Worth recognising, because other people's runbooks and blog posts are full of the short forms. Use them while working by hand. Write the full name in anything committed: an abbreviation that is unique today stops being unique when a later version adds a command, and the pipeline then fails with an ambiguous-command error instead of upgrading.

Example · bash
# Magento's CLI is Symfony Console, which accepts any unambiguous
# abbreviation of a command name. These pairs are the same command.

bin/magento c:f                  # bin/magento cache:flush
bin/magento s:up                 # bin/magento setup:upgrade
bin/magento s:d:c                # bin/magento setup:di:compile
bin/magento i:rei                # bin/magento indexer:reindex

# Short and long option names are also the same thing
bin/magento setup:static-content:deploy -f en_US
bin/magento setup:static-content:deploy --force en_US

# An abbreviation that matches two commands is refused rather than guessed,
# so write the full name in scripts and deploy pipelines.

What the Composer step actually changes

An extract of the project root composer.json after the upgrade. Two things matter here. The metapackage version is the upgrade, and it is the only version number you set by hand; every core module version comes from it. The allow-plugins list is the one Composer 2 consults before loading any plugin, so the root update plugin must appear there or require-commerce is never registered. Commit this file together with composer.lock.

Example · json
{
    "require": {
        "magento/product-community-edition": "2.4.9",
        "magento/composer-root-update-plugin": "~2.0"
    },
    "config": {
        "allow-plugins": {
            "magento/composer-dependency-version-audit-plugin": true,
            "magento/composer-root-update-plugin": true,
            "magento/inventory-composer-installer": true,
            "laminas/laminas-dependency-plugin": true
        }
    }
}

Confirming the upgrade landed

Run these before taking maintenance mode off. setup:db:status is the useful one in a deploy script: it prints "All modules are up to date." and exits 0 when the schema and data patches have all run, and exits 2 when setup:upgrade is still required. var/report holds one file per uncaught exception, so a new file with a fresh timestamp is a smoke test failing before any human notices.

Example · bash
# Does the database match the code? Exit code 2 means setup:upgrade
# still has work to do, which a pipeline can test for.
bin/magento setup:db:status
echo $?

# Which version is actually installed
bin/magento --version

# Any module left disabled or missing by the upgrade
bin/magento module:status --disabled

# Indexers must all say Ready and Update by Schedule for a live store
bin/magento indexer:status

# The first place a broken upgrade shows up
tail -n 50 var/log/system.log var/log/exception.log
ls -l var/report/ | tail

Discussion

  • Be the first to comment on this lesson.