Patches
Security patch releases, the Quality Patches Tool, your own composer patches, and keeping a record of what is applied.
./vendor/bin/magento-patches apply MDVA-31321A patch changes core code without changing the version you are on. Magento work uses three distinct kinds, and confusing them wastes days.
Security patch releases
These are versions, not patch files: 2.4.9-p1 and similar. They contain security fixes only, so they are applied exactly like an upgrade, with a metapackage version bump and the usual rebuild commands, but with far less to re-test than a minor release.
Quality patches
The Quality Patches Tool is a Composer package, magento/quality-patches, that carries individual fixes for both Magento Open Source and Adobe Commerce. Each patch has an ID such as MDVA-31321. The tool lists what applies to your installed version with its status, applies by ID, and reverts by ID. Patches distributed directly by Adobe Commerce Support need a support contract; the published quality patches do not.
Your own patches
For a fix that exists only in a GitHub commit, or your own one-line change to a vendor file, use the cweagans/composer-patches plugin. Commit the diff file to the repository and list it under extra.patches in composer.json. That entry is the record of what is applied.
What trips people up
Patched files live in vendor/, which is not in version control. Anything applied by hand on a server disappears at the next composer install. Patching has to be part of the build.
Example
# The Quality Patches Tool: individual fixes for the version you are on.
# Install once; it is a normal Composer requirement
composer require magento/quality-patches
# Every patch available for the installed version, with:
# ID, title, type (Optional or Deprecated) and status
# (Applied, Not applied, or N/A when it cannot apply here)
./vendor/bin/magento-patches status
# Apply one by ID
./vendor/bin/magento-patches apply MDVA-31321
# Several at once. Identical to running apply once per ID.
./vendor/bin/magento-patches apply MDVA-31321 MDVA-43443
# Patches edit files under vendor/, so clear the cache afterwards
bin/magento cache:clean
# Undo one, or everything the tool applied
./vendor/bin/magento-patches revert MDVA-31321
./vendor/bin/magento-patches revert --all
# New patches arrive by updating the package, not by upgrading Magento
composer update magento/quality-patches
# Every apply and revert is recorded in var/log/patch.logWhen to use it
- Applying a single published fix without taking on a whole minor upgrade.
- Carrying a one-line vendor fix across deploys so it survives composer install.
- Deciding, at the start of an upgrade, which existing patches can be dropped.
- Answering a security audit with a list of exactly what is patched.
More examples
Your own patch, declared in composer.json
An extract of the project root composer.json. The patch files sit in a committed directory such as patches/, keyed by the package they modify, with a sentence saying why each one exists. The plugin reapplies them on every composer install, which is what makes a patched vendor/ reproducible on a build server. composer-exit-on-patch-failure turns a patch that no longer applies into a failed build instead of a quietly unpatched deploy, which is what you want the day you upgrade. After installing, run bin/magento setup:di:compile and bin/magento cache:flush.
{
"require": {
"cweagans/composer-patches": "^1.7"
},
"extra": {
"composer-exit-on-patch-failure": true,
"patches": {
"magento/module-payment": {
"Upstream fix for the duplicate order grid row": "patches/magento-payment-order-grid.diff"
},
"magento/framework": {
"Local fix pending 2.4.9-p1": "patches/framework-cache-tag.diff"
}
}
},
"config": {
"allow-plugins": {
"cweagans/composer-patches": true
}
}
}Taking a security patch release
Reach for this when Adobe publishes a security release for the version you already run. The sequence is the upgrade sequence, because that is what it is, but the scope of change is security fixes only, so the re-testing is a smoke test rather than a full regression pass. Note that -p releases are cumulative within a minor version: 2.4.9-p2 includes what 2.4.9-p1 fixed, so there is no need to step through them.
# A security patch release is a version bump, applied like an upgrade.
# Example: moving from 2.4.9 to a published 2.4.9-p1.
bin/magento maintenance:enable
cp composer.json composer.json.bak
composer require-commerce magento/product-community-edition 2.4.9-p1 --no-update
composer update
rm -rf generated/code/* var/cache/* var/page_cache/*
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f en_US # production mode only
bin/magento cache:flush
bin/magento maintenance:disable
# Confirm what is now installed
bin/magento --versionKeeping a record someone else can read
Use this when an upgrade starts, because every patch is a question: is this fix already in the target version? A quality patch that is included upstream will refuse to apply, or show as N/A, and your own patches will fail to apply with composer-exit-on-patch-failure set. Both are the signal to delete the entry rather than to force it. The composer.json and patches/ directory under version control are what makes that review possible months later.
# Which quality patches are applied right now
./vendor/bin/magento-patches status | grep -i applied
# Which version of the patch package the project pins
composer show magento/quality-patches
# Your own patches: the composer.json entry is the record
git log --oneline -- patches/ composer.json
# The tool's own history on this machine
tail -n 40 var/log/patch.log
Discussion