DEV Community

Magevanta
Magevanta

Posted on • Originally published at magevanta.com

Magento 2 Composer: Best Practices for Production Deployments

Composer is the foundation of every Magento 2 project, yet most deployment workflows treat it as an afterthought. Here's how to do it right — from local development to production deployment.

Always commit composer.lock

This is non-negotiable. The lockfile pins every dependency to an exact version, including transitive dependencies. Without it:

  • composer install on your CI server might install different versions than your local machine
  • A third-party package releases a breaking change and your next deploy breaks
  • Security patches get installed automatically (sometimes good, sometimes unexpected)
# Always commit this
git add composer.lock

# Never add this to .gitignore
Enter fullscreen mode Exit fullscreen mode

The composer.lock is your reproducibility guarantee.

Separate require from require-dev

Production servers should never install development dependencies (PHPUnit, PHPStan, Rector, etc.). They add weight and introduce unnecessary attack surface.

{
    "require": {
        "magento/product-community-edition": "~2.4.7",
        "bettermagento/suite": "^1.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.0",
        "phpstan/phpstan": "^1.10",
        "rector/rector": "^1.0"
    }
}
Enter fullscreen mode Exit fullscreen mode

In your deployment:

# Production
composer install --no-dev --optimize-autoloader --no-interaction

# Local / CI
composer install
Enter fullscreen mode Exit fullscreen mode

The --optimize-autoloader flag generates a class map instead of relying on PSR-4 file scanning — measurably faster in production.

Private package repositories

For proprietary modules (like BetterMagento), you need a private Composer repository. Options:

Option 1: Private Packagist
The easiest. Mirror public packages + host private ones. Expensive but worth it for teams.

Option 2: Satis
Self-hosted, open source. Generate a static JSON repository from your private packages:

composer create-project composer/satis --stability=dev
Enter fullscreen mode Exit fullscreen mode
{
    "name": "My Private Repo",
    "homepage": "https://packages.example.com",
    "repositories": [
        { "type": "vcs", "url": "git@github.com:yourorg/private-module.git" }
    ],
    "require-all": true
}
Enter fullscreen mode Exit fullscreen mode

Option 3: path repositories for local development

When developing a module locally alongside your Magento install:

{
    "repositories": [
        {
            "type": "path",
            "url": "../bettermagento/module-query-optimizer",
            "options": {
                "symlink": true
            }
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Composer symlinks the local directory — changes are reflected immediately without re-installing.

The Magento Composer plugin

Magento uses magento/composer-dependency-version-audit-plugin and magento/composer-root-update-plugin. These handle:

  • Routing modules to app/code/ vs vendor/
  • Managing app/etc/ configuration during updates
  • Version constraint auditing

Keep these plugins updated alongside Magento upgrades. Mismatched plugin versions are a common source of confusing upgrade failures.

Deployment workflow

A robust Magento deployment with Composer:

#!/bin/bash
set -e

# 1. Pull latest code
git pull origin main

# 2. Install dependencies (no dev, optimized autoloader)
composer install --no-dev --optimize-autoloader --no-interaction

# 3. Compile DI
php bin/magento setup:di:compile

# 4. Deploy static content
php bin/magento setup:static-content:deploy -f en_US

# 5. Run database upgrades
php bin/magento setup:upgrade --keep-generated

# 6. Flush caches
php bin/magento cache:flush

# 7. Warm critical pages
curl -s https://your-store.com/ > /dev/null
curl -s https://your-store.com/catalog/category/view/id/2 > /dev/null
Enter fullscreen mode Exit fullscreen mode

The --keep-generated flag on setup:upgrade skips regenerating compiled code — use it in production when you've already run setup:di:compile.

Security: audit your dependencies

# Check for known vulnerabilities
composer audit

# Update all packages to latest compatible versions
composer update --with-all-dependencies

# Check for outdated packages
composer outdated
Enter fullscreen mode Exit fullscreen mode

Set up automated security scanning in your CI pipeline. GitHub Actions example:

- name: Composer security audit
  run: composer audit --format=json --no-dev
Enter fullscreen mode Exit fullscreen mode

Fail the build on critical vulnerabilities, warn on high.

Managing Magento updates

When a new Magento patch or minor release drops:

# 1. Check what will change
composer require magento/product-community-edition:~2.4.8 --dry-run

# 2. Apply the update
composer require magento/product-community-edition:~2.4.8

# 3. Review changed files in vendor/
git diff composer.lock | grep "^+" | grep '"version"'

# 4. Run the full upgrade process
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy -f
Enter fullscreen mode Exit fullscreen mode

Never run composer update without specifying packages on a production Magento install — it can update hundreds of packages at once, making rollbacks impossible to diagnose.

Quick reference

Command When to use
composer install --no-dev --optimize-autoloader Every production deploy
composer install Local development, CI
composer require vendor/package Add a new dependency
composer update vendor/package Update one package specifically
composer audit Weekly security check
composer outdated Before planning upgrades

Get these habits right and your Magento deployments become predictable, reproducible, and secure.


Originally published on magevanta.com

Top comments (0)