Using Doctrine Migrations in Symfony to Manage Schema Changes Safely
Doctrine Migrations lets you version‑control database changes in Symfony, apply them safely, and roll back if needed. This guide covers the desired outcome, prerequisites, step‑by‑step procedure, checks, and recovery options.
15 Sept 2026, 19:05 UTC

Desired Outcome
In a Symfony application, you want a repeatable, auditable way to evolve the database schema without manual SQL or risking data loss. Doctrine Migrations provides a version table that tracks every applied migration, ensuring that the same changes can be applied across dev, staging, and production environments in the same order.
Prerequisites
- Symfony 6.2 or later with the DoctrineBundle installed.
- Doctrine ORM 3.x and Doctrine Migrations 3.x (installed via Composer).
- PHP 8.1+ with the
pdo_mysqlor appropriate PDO driver for your database. - Read/write access to the database, and filesystem permissions to create migration files in
src/Migrations. - A working database connection configured in
config/packages/doctrine.yaml.
Configuration
Doctrine Migrations uses a YAML configuration file. Create or edit config/packages/doctrine_migrations.yaml with the following minimal setup:
doctrine_migrations:
migrations_paths:
# Path where migration classes will be generated
'Doctrine\\Migrations': '%kernel.project_dir%/src/Migrations'
storage:
table_storage:
table_name: doctrine_migration_versions
# Optional: set the namespace for generated migrations
# namespace: Doctrine\\Migrations
# Optional: set the directory for generated migrations
# directory: src/Migrations
After editing, run php bin/console doctrine:migrations:diff --dry-run to verify that the configuration is parsed correctly. The command should output "No differences detected" if the database schema matches the mapping.
Step‑by‑Step Procedure
1. Generate a Migration
When you change an entity (add a field, rename a column, etc.), generate a migration that captures the diff:
# Run from project root, requires superuser or DB write access
php bin/console doctrine:migrations:diff
This creates a new PHP class in src/Migrations named something like Version20260921123456.php. The class contains up() and down() methods with the SQL needed to apply and revert the change.
2. Review the Generated Migration
Open the file and confirm that the statements match your intent. For example:
addSql('ALTER TABLE user ADD email VARCHAR(180) NOT NULL');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE user DROP email');
}
}
Do not edit this file after it has been applied; doing so breaks the version history.
3. Apply the Migration
Run the migrations in the target environment:
php bin/console doctrine:migrations:migrate
The console will list pending migrations and prompt for confirmation. Confirm to proceed. After successful execution, Doctrine inserts a row into doctrine_migration_versions with the migration name.
4. Verify Success
- Check the version table:
SELECT * FROM doctrine_migration_versions;should list the new migration. - Run
php bin/console doctrine:migrations:status. It should report "No pending migrations" and list the migration as applied. - Inspect the database schema (e.g.,
SHOW CREATE TABLE user;) to confirm the new column exists.
5. Rollback (Recovery Option)
If a migration caused a problem, you can revert the last applied migration:
php bin/console doctrine:migrations:rollback
This command runs the down() method of the most recent migration and removes its entry from the version table. After rollback, re‑run doctrine:migrations:status to confirm that the migration is now pending again.
6. Force Marking a Migration (Optional)
Sometimes you need to mark a migration as applied without executing it (e.g., after manually running SQL). Use:
php bin/console doctrine:migrations:execute --up 20260921123456
Replace the migration name with your actual one. This updates the version table accordingly.
Practical Checks and Limitations
- Version Table Integrity: If the
doctrine_migration_versionstable is missing or corrupted, migrations will fail. Re‑create it withphp bin/console doctrine:migrations:installif necessary. - Data Loss Risk: Always backup your database before running migrations in production. Use
mysqldumpor your RDBMS’s backup tool. - Manual Edits: Editing a migration after it has been applied invalidates the audit trail. Keep migrations immutable.
- Large Schema Changes: For bulk changes, consider splitting migrations into smaller steps to reduce rollback complexity.
- Testing: Run migrations in a staging environment that mirrors production. Verify that
doctrine:migrations:statusshows no pending migrations after a full cycle.
Conclusion
Doctrine Migrations gives Symfony developers a robust, versioned mechanism to evolve database schemas. By following the steps above—generating, reviewing, applying, and, if necessary, rolling back—you can maintain consistency across environments, reduce deployment risk, and preserve data integrity.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.