Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Migrating a Symfony 4 App to Symfony 7

By Kokil Thapa | Last reviewed: September 2026

Migrating a Symfony 4 app to Symfony 7 is not a single composer bump. Symfony dropped one major version per year after 4.x, and each jump removes APIs your 4.x code still calls. You must raise PHP, clear deprecations, and upgrade through 5, 6, and 7 in order. This guide walks through the path I use on production Symfony apps, including the multi-environment config and deployment checks that keep staging honest before production.

What is the correct upgrade path when migrating a Symfony 4 app to Symfony 7?

Symfony does not support jumping from 4.x straight to 7.x. The supported route is 4.4 LTS → 5.4 → 6.4 LTS → 7.x. Each stop exposes deprecations that become hard errors in the next major.

Symfony 4.4 reached end of life in November 2023. If you still run 4.4, treat security patches as gone and plan the migration as risk reduction, not optional cleanup. Symfony 6.4 LTS is supported until November 2027. Symfony 7.x is the current stable line in 2026, with Symfony 8.1 available if you want the newest APIs and PHP 8.4.1 minimum.

On client projects I maintain, the first decision is target version: stop at 6.4 LTS for maximum stability, or land on 7.x for current features. This article targets Symfony 7 because that is what most teams ask for in 2026.

Symfony 4 to 7 Upgrade PathSymfony 4.4PHP 7.1+Symfony 5.4PHP 7.2+Symfony 6.4PHP 8.1+Symfony 7.xPHP 8.2+At Each StepFix deprecations before the next bumpRun tests, update bundles, commit
Migrating a Symfony 4 app to Symfony 7 follows a strict sequential path through 5.4 and 6.4 LTS releases.

Plan for three to six months on a medium codebase with custom bundles and legacy controllers. A small API with good test coverage can finish in weeks. Budget time for third-party packages that never updated past Symfony 5.

How do you prepare a Symfony 4 codebase before starting the upgrade?

Preparation saves more time than any composer flag. You want a clean git baseline, a working CI pipeline, and a list of every deprecation before you touch version constraints.

Audit the current stack

Record PHP version, Symfony patch level, Doctrine ORM version, and every bundle in composer.json. Export production environment variables and compare them against .env and .env.local. Misaligned env files cause more post-upgrade failures than framework changes.

Run the built-in deprecation helper on Symfony 4.4 first:

composer require --dev symfony/phpunit-bridge
SYMFONY_DEPRECATIONS_HELPER=max[total]=0 vendor/bin/simple-phpunit

Set max[total]=0 only after you have cleared existing noise. Start with weak mode to collect a full list without failing the build.

Establish safety nets

  1. Ensure PHPUnit or Pest covers critical paths — checkout, auth, admin CRUD, API endpoints.
  2. Clone production data into staging with anonymised PII if regulations require it.
  3. Enable the Symfony test suite in CI so every branch runs bin/console lint:container and bin/console doctrine:schema:validate.
  4. Document custom compiler passes, event subscribers, and security voters. These break silently more often than controllers.

If the app uses hexagonal architecture, domain code may survive untouched. Infrastructure adapters around Doctrine, Messenger, and HTTP clients need the most attention.

Pre-Migration ChecklistGit tag + CI greenDeprecation auditBundle compat checkStaging mirrorPHP upgrade planRollback script readyOnly then change composer.json constraints
Preparation checklist before migrating a Symfony 4 app to Symfony 7 — skip these steps and you will debug in production.

What PHP version do you need before upgrading to Symfony 7?

Symfony 7 requires PHP 8.2 or higher. Symfony 4.4 ran on PHP 7.1. That gap is the hardest part of many migrations.

Raise PHP on a branch before or alongside the Symfony 5 upgrade. PHP 8.0 removed the each() function and changed error handling. PHP 8.1 added enums and readonly properties. PHP 8.2 deprecated dynamic properties. PHP 8.3 and 8.4 add further type strictness.

On Ubuntu 24 servers I run PHP 8.3 or 8.4 alongside older versions using update-alternatives. Symfony 7 works fine on PHP 8.5 when you are ready. Symfony 8.1 requires PHP 8.4.1 minimum if you plan to continue past 7.x later.

Validate extensions before switching FPM pools:

php -m | grep -E 'intl|mbstring|xml|curl|zip|pdo_mysql'
composer check-platform-reqs

Doctrine ORM 2.x does not run on Symfony 7. Plan a move to Doctrine ORM 3.x during the Symfony 6 step. Review the official Doctrine ORM upgrade guide for removed annotations and mapping changes.

Symfony versionMinimum PHPSupport status (2026)Notes
4.4 LTS7.1.3End of lifeStarting point for most legacy apps
5.47.2.5End of lifeTransitional; fix 4.x deprecations here
6.4 LTS8.1Active LTS to Nov 2027Doctrine ORM 3, native types
7.x8.2Active stableTarget for this migration
8.18.4.1Current majorOptional next step after 7.x

How do you upgrade Symfony dependencies step by step?

Use the Symfony flex recipes and official upgrade tooling. The Symfony major version upgrade guide is the primary reference. Work on a dedicated branch. Merge only when tests pass at each major boundary.

Step 1: Symfony 4.4 → 5.4

Update the extra Symfony constraint in composer.json:

"extra": {
    "symfony": {
        "require": "5.4.*"
    }
}

Then run:

composer update "symfony/*" --with-all-dependencies
bin/console cache:clear
bin/console debug:container --deprecations

Common fixes at this stage:

  • Replace guard authenticators with the new authenticator manager. See the Symfony security firewall guide for current YAML structure.
  • Move services.yml defaults from _defaults autowire-only to explicit autoconfigure: true where needed.
  • Replace SwiftMailer with symfony/mailer and symfony/mime.
  • Update framework.yaml — remove templating, confirm csrf_protection and session blocks.

Step 2: Symfony 5.4 → 6.4

Raise PHP to 8.1 minimum first. Update the constraint to 6.4.* and run composer update again.

Doctrine changes hit hard here. Replace annotations with PHP 8 attributes unless you keep doctrine/annotations temporarily. Run Doctrine migrations in a transaction on staging before production.

Remove @Route annotations from controllers unless you use symfony/routing attributes. The bin/console debug:router output should match your sitemap and API docs exactly.

Step 3: Symfony 6.4 → 7.x

Confirm PHP 8.2+. Set the constraint to 7.4.* or the latest 7.x patch your team approves:

"extra": {
    "symfony": {
        "require": "7.4.*"
    }
}
composer update "symfony/*" --with-all-dependencies
composer outdated "symfony/*"
bin/console about

Symfony 7 removes more legacy class aliases. Watch for:

  • UserInterface changes in security — password upgrade interfaces moved.
  • Removed support for XML validation mapping in some components.
  • Console command default name changes — update cron entries that call commands by old names.
  • framework.messenger routing if you use async — validate against the Messenger async guide.

Use the JSON formatter when debugging API responses that break after serializer changes. Symfony 7 tightens normalisation defaults compared to Symfony 4.

Per-Major Composer WorkflowChange constraintcomposer.jsoncomposer updatesymfony/* packagesFix deprecationsRector + manualRun test suitePHPUnit + pantherDeploy stagingSmoke test URLsRepeat nextmajor version
Repeat this composer and test workflow at Symfony 5, 6, and 7 before merging to main.

What breaking changes hit Symfony 4 apps hardest in Symfony 7?

Not every deprecation screams in logs. These areas cause production incidents if you rush the final jump.

Security and authentication

Symfony 4 often used fos/user-bundle or custom guard authenticators. Symfony 7 expects the authenticator system with LoginFormAuthenticator or custom authenticators implementing AuthenticatorInterface. Password hashers moved to password_hashers in security config. Test login, logout, remember-me, and API token auth on staging with real cookies.

Forms, validation, and serialization

Form types that relied on ChoiceType with choices_as_values need rewriting. Validation groups still work, but annotation-based constraints should become PHP attributes. The Symfony serializer ignores null fields differently unless you configure skip_null_values explicitly.

Third-party bundles

Check Packagist for Symfony 7 compatible releases before you start. Abandoned bundles force replacement:

  • SwiftMailer → Symfony Mailer (done by Symfony 6).
  • SensioFrameworkExtraBundle → native attributes in Symfony 6+.
  • Old admin generators → EasyAdmin 4.x or custom CRUD.

When a bundle has no update, extract the 200 lines you actually use into a project service. I have done this on legal-tech portals where a PDF bundle stalled at Symfony 5. The fork lived longer than expected, but it unblocked the upgrade.

Caching and performance

Symfony 7 defaults favour cache.system and Redis adapters over APCu-only setups. If you use Redis, align pool config with the Symfony cache component guide. Warm caches after deploy:

APP_ENV=prod APP_DEBUG=0 bin/console cache:clear
APP_ENV=prod APP_DEBUG=0 bin/console cache:warmup

Reload PHP-FPM after deploy so opcache picks up new files. I use the same pattern described in Symfony deployment on Ubuntu VPS — symlink release, warmup, reload FPM, run smoke tests.

How do you test and deploy after reaching Symfony 7?

Testing proves the migration worked. Deployment puts it in front of users without downtime surprises.

Automated testing layers

  1. Unit tests — domain services, validators, custom constraints.
  2. Integration tests — Doctrine repositories, Messenger handlers, WebTestCase for forms.
  3. Contract tests — compare API JSON schemas against mobile or SPA clients.
  4. Load tests — run k6 load tests on staging at 2× expected traffic.

Run bin/console lint:yaml config/ and bin/console lint:twig templates/. Broken Twig after upgrade usually means deprecated filters from old symfony/twig-bridge extensions.

Production cutover

Schedule during low traffic. Nepal-based businesses often prefer late evening or early morning windows outside Dashain/Tihar peaks if those affect their users.

Deployment checklist:

  • Put app in maintenance mode if you cannot run blue-green.
  • Pull release tag, run composer install --no-dev --optimize-autoloader with Composer 2.10.
  • Run pending migrations: bin/console doctrine:migrations:migrate --no-interaction.
  • Clear and warmup prod cache.
  • Reload PHP-FPM and verify bin/console about reports Symfony 7.x.
  • Hit health-check URLs, login flow, one write operation, one queued job.

For teams without in-house DevOps, professional website migration support covers PHP version changes, zero-downtime deploys, and rollback planning. Similar work applies to enterprise Symfony applications with document workflows and payment callbacks.

Symfony 7 Deploy PipelineGit tagComposerMigrationsCache warmLivePost-Deploy VerificationHealth check, auth, queue worker, cronCompare error rate in logs for 24 hours
Deploy pipeline after migrating a Symfony 4 app to Symfony 7 — verify queues and cron, not just HTTP 200.

Keep Symfony 6.4 LTS as a rollback tag in git if the 7.x deploy fails. Restore the previous release directory, reload FPM, and investigate before retrying. Document the incident — the support and maintenance retainer clients sign often starts with upgrade war stories like this.

Compare Symfony against Laravel if you are deciding future greenfield work. The Symfony 7 vs Laravel 12 article covers container philosophy, hiring, and ecosystem trade-offs. Symfony wins on long-lived enterprise apps with strict domain boundaries. Laravel 13 ships on PHP 8.3 minimum if you pivot stacks entirely — that is a rewrite, not a migration.

Reference projects with complex workflows — like Mijar Law Associates client portal or Adventure Third Pole Trek booking CRM — benefit from incremental upgrades because they cannot afford multi-week downtime. The same incremental discipline applies to Symfony 4 codebases even when the UI looks fine.

Apply twelve-factor principles during the upgrade: externalise config, treat logs as streams, and keep dev/prod parity. Upgrades expose env drift faster than any audit spreadsheet.

For automated refactors, Rector with rector/rector-symfony removes hundreds of manual edits. Run it in small commits:

composer require --dev rector/rector rector/rector-symfony
vendor/bin/rector process src/ --dry-run
vendor/bin/rector process src/

Review every Rector diff. It misses business-specific edge cases. Pair Rector with phpstan at level 6 or higher once you reach Symfony 6.

After migration, schedule testing and optimisation to catch N+1 queries Doctrine 3 surfaces under load. Symfony 4 apps often hid slow queries behind APCu. Symfony 7 with Redis expects you to fix the database layer properly.

If your app exposes REST or GraphQL, validate responses against the API Platform guide if you adopt or already use API Platform. Version your API URL prefix so mobile clients can fall back during cutover.

Console commands in cron deserve a pass too. The Symfony console commands guide documents naming changes that break Sunday-night batch jobs silently.

Instrument with OpenTelemetry if you lack visibility. The OpenTelemetry instrumentation guide helps compare error rates before and after migration using real trace data instead of guesswork.

On the PHP side, read the official PHP 8.2 migration notes for language-level breaks your static analysis might miss.

Key Takeaways

  • Upgrade sequentially through Symfony 5.4 and 6.4 LTS — never jump directly from 4.4 to 7.x.
  • Raise PHP to 8.2+ before the final Symfony 7 composer update and run composer check-platform-reqs.
  • Fix every deprecation at each major boundary using SYMFONY_DEPRECATIONS_HELPER and Rector.
  • Replace abandoned bundles (SwiftMailer, SensioFrameworkExtraBundle) early in the 5.x step.
  • Deploy with cache warmup, PHP-FPM reload, migration run, and 24-hour log monitoring.
  • Keep a Symfony 6.4 rollback tag until production proves stable on Symfony 7.

People Also Ask

Can you skip Symfony 5 and 6 and upgrade directly to Symfony 7?

No. Symfony's backward-compatibility policy removes deprecated code each major release. A direct jump leaves hundreds of unresolved breaks in security, routing, and dependency injection. The supported path is 4.4 → 5.4 → 6.4 → 7.x with deprecation fixes at each stop.

How long does migrating a Symfony 4 app to Symfony 7 take?

A small app with good tests and maintained bundles can finish in four to eight weeks. Medium enterprise apps with custom authentication, Doctrine 2 mappings, and abandoned bundles typically need three to six months. Timeline depends on test coverage and how many deprecations you ignored while on 4.4.

Is Symfony 6.4 LTS enough, or do you need Symfony 7?

Symfony 6.4 LTS is supported until November 2027 and suits teams that want stability without chasing the latest APIs. Choose Symfony 7 if you need current component features, longer support runway beyond 6.4, or plan to eventually move to Symfony 8.1 on PHP 8.4.1.

What happens to Doctrine ORM 2 during the upgrade?

Doctrine ORM 2.x does not support Symfony 7. Upgrade to Doctrine ORM 3.x during the Symfony 6 step. Expect mapping changes from annotations to PHP 8 attributes, namespace updates, and stricter type handling in repositories.

Plan your Symfony upgrade with a clear runway

Migrating a Symfony 4 app to Symfony 7 is predictable when you treat it as a series of small, tested releases rather than one risky launch weekend. Audit deprecations today, raise PHP on staging this week, and walk the major versions one at a time. Your future self — and every cron job on the server — will thank you.

Need help scoping a Symfony upgrade, PHP lift, or zero-downtime deploy? Contact us to review your codebase and build a migration plan that fits your traffic and budget. For ongoing work after cutover, see support and maintenance services and explore related Symfony content on the blog.

Frequently Asked Questions

No. Symfony removes deprecated APIs each major release. The supported path is 4.4 LTS to 5.4 to 6.4 LTS to 7.x, fixing deprecations at every stop.

Symfony 7 requires PHP 8.2 or higher. Symfony 4.4 ran on PHP 7.1.3, so raise PHP before or alongside the Symfony 5 upgrade.

Small apps with good tests finish in weeks to eight weeks. Medium codebases with custom bundles need three to six months.

Symfony does not support jumping from 4.x straight to 7.x. The supported route is 4.4 LTS, then 5.4, then 6.4 LTS, then 7.x. Symfony 4.4 reached end of life in November 2023, so security patches are gone and the migration is risk reduction. Symfony 6.4 LTS is supported until November 2027. In 2026, Symfony 7.x is the current stable line, with Symfony 8.1 available if you want newer APIs and can meet its PHP 8.4.1 minimum later.

Start with a clean git baseline, a working CI pipeline, and a full deprecation list before changing version constraints. Audit PHP version, Symfony patch level, Doctrine ORM version, and every bundle in composer.json. Compare production environment variables against .env files, because env drift causes more post-upgrade failures than framework changes. Run symfony/phpunit-bridge with SYMFONY_DEPRECATIONS_HELPER, starting in weak mode. Ensure PHPUnit or Pest covers checkout, auth, admin CRUD, and API endpoints. Document custom compiler passes, event subscribers, and security voters, since these break silently more often than controllers.

Work on a dedicated branch and merge only when tests pass at each major boundary. Step one: set extra.symfony.require to 5.4., run composer update symfony/ --with-all-dependencies, then cache:clear and debug:container --deprecations. Step two: raise PHP to 8.1 minimum, set the constraint to 6.4., and update again. Step three: confirm PHP 8.2+, set the constraint to 7.4. or your approved 7.x patch, update, and run composer outdated symfony/*. Repeat linting, deprecation checks, and your full test suite at Symfony 5, 6, and 7 before merging to main.

Security and authentication cause the most production incidents. Symfony 4 apps using fos/user-bundle or guard authenticators must move to the authenticator system with LoginFormAuthenticator or custom authenticators implementing AuthenticatorInterface. Password hashers moved to password_hashers in security config. Forms and serialization also bite: ChoiceType with choices_as_values needs rewriting, annotation constraints should become PHP attributes, and the serializer ignores null fields differently unless skip_null_values is configured. Caching defaults shifted toward cache.system and Redis adapters over APCu-only setups. Console command default name changes can break cron entries that still call old command names.

Doctrine ORM 2.x does not run on Symfony 7, so plan the move to Doctrine ORM 3.x during the Symfony 6 step, not at the final 7.x jump. Replace annotations with PHP 8 attributes unless you temporarily keep doctrine/annotations. Run Doctrine migrations in a transaction on staging before production. Review the official Doctrine ORM upgrade guide for removed annotations and mapping changes. After migration, schedule testing and optimisation because Doctrine 3 surfaces N+1 queries under load that Symfony 4 apps often hid behind APCu caching.

On Symfony 4.4, install symfony/phpunit-bridge and run vendor/bin/simple-phpunit with SYMFONY_DEPRECATIONS_HELPER=max[total]=0 only after clearing existing noise. Start with weak mode to collect the full list without failing the build. At each major boundary, run bin/console debug:container --deprecations after composer update. Pair manual fixes with Rector: composer require --dev rector/rector rector/rector-symfony, run vendor/bin/rector process src/ --dry-run first, then process for real. Review every Rector diff because it misses business-specific edge cases. Add PHPStan at level 6 or higher once you reach Symfony 6.

Check Packagist for Symfony 7 compatible releases before you start. Abandoned bundles force replacement: SwiftMailer becomes symfony/mailer and symfony/mime by Symfony 6, SensioFrameworkExtraBundle gives way to native attributes in Symfony 6+, and old admin generators need EasyAdmin 4.x or custom CRUD. When a bundle has no update, extract the code you actually use into a project service. On legal-tech portals I have maintained, a stalled PDF bundle at Symfony 5 was forked into roughly 200 lines of project code, which unblocked the upgrade even though the fork lived longer than expected.

Replace guard authenticators with the authenticator manager and follow the current Symfony security firewall guide for YAML structure. Move services.yml defaults from autowire-only _defaults to explicit autoconfigure: true where needed. Replace SwiftMailer with symfony/mailer and symfony/mime. Update framework.yaml by removing templating and confirming csrf_protection and session blocks. These 5.x tasks remove dependencies that become hard errors in Symfony 6. Fixing them early reduces the volume of simultaneous breaks when you raise PHP to 8.1 and jump to 6.4 LTS with Doctrine ORM 3 and attribute-based routing.

Layer your testing beyond a green PHPUnit run. Unit tests should cover domain services, validators, and custom constraints. Integration tests should hit Doctrine repositories, Messenger handlers, and WebTestCase for forms. Contract tests compare API JSON schemas against mobile or SPA clients. Run k6 load tests on staging at twice expected traffic. Execute bin/console lint:yaml config/ and bin/console lint:twig templates/ because broken Twig usually means deprecated filters from old symfony/twig-bridge extensions. Validate bin/console debug:router output against your sitemap and API docs. Test login, logout, remember-me, and API token auth on staging with real cookies.

Schedule cutover during low traffic. Pull the release tag and run composer install --no-dev --optimize-autoloader with Composer 2.10. Run pending migrations with bin/console doctrine:migrations:migrate --no-interaction. Clear and warmup production cache with APP_ENV=prod APP_DEBUG=0, then reload PHP-FPM so opcache picks up new files. Verify bin/console about reports Symfony 7.x. Hit health-check URLs, login flow, one write operation, and one queued job. Verify cron entries and Messenger queues, not just HTTP 200 responses. Monitor logs for 24 hours after cutover. Keep a Symfony 6.4 rollback tag in git until production proves stable.

The first decision on client projects I maintain is target version. Stop at 6.4 LTS for maximum stability; it is actively supported until November 2027 and fits teams that want a long runway with fewer surprises. Land on 7.x for current features and the stable line most teams ask for in 2026. This article targets Symfony 7 for that reason. Symfony 8.1 is the optional next step after 7.x if you want the newest APIs and can meet PHP 8.4.1 minimum. Either way, you still must pass through 5.4 and 6.4 sequentially from Symfony 4.4.

Keep Symfony 6.4 LTS as a rollback tag in git before cutting over to 7.x. If the deploy fails, restore the previous release directory using your symlink-based deployment workflow, reload PHP-FPM, and investigate before retrying. Do not attempt a forward fix under live traffic unless the failure is trivial. Document the incident because upgrade war stories often shape the support retainer clients sign afterward. Maintaining a known-good 6.4 release alongside the 7.x candidate gives you a tested fallback without re-running the entire migration under pressure.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: