Deploying file changes is the straightforward part. The harder question is: what happens when your code update requires a database schema change? A new column, a renamed table, or a modified index can't be deployed the same way you upload files — and getting the timing wrong can take your application offline.
Database migrations solve this by versioning your schema changes alongside your code. Each migration is a file that describes a specific change and can be applied (or rolled back) in order. When paired with DeployHQ's SSH commands, migrations run automatically as part of every deployment.
What are database migrations?
A database migration is a version-controlled script that modifies your database schema. Instead of manually running SQL statements against production, you write migration files that define the changes declaratively. A migration tool tracks which migrations have already been applied and runs only the new ones.
flowchart LR
A[Push code + migration files] --> B[DeployHQ uploads files]
B --> C[SSH command runs migrations]
C --> D[Database updated]
D --> E[Application serves new code]
This approach gives you:
- Version control for your schema: Every change is tracked in Git, with a clear history of who changed what and when
- Reproducible environments: Run the same migrations against development, staging, and production to keep them in sync
- Safe rollbacks: Most migration tools generate reverse migrations, so you can undo changes if something goes wrong
- Team coordination: Multiple developers can create migrations independently, and the tool handles ordering and conflict detection
Migration tools by framework
Most web frameworks include migration support. Here's how the most common ones work:
Laravel (PHP)
Laravel's Artisan CLI is one of the most widely used migration tools. To create a migration:
php artisan make:migration create_orders_table
This generates a timestamped file in database/migrations/:
class CreateOrdersTable extends Migration
{
public function up()
{
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->decimal('total', 10, 2);
$table->string('status')->default('pending');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('orders');
}
}
Run migrations with:
php artisan migrate
For a complete guide to deploying Laravel applications, see our Laravel deployment guide.
Ruby on Rails
Rails uses Active Record migrations. Create one with:
rails generate migration CreateOrders user:references total:decimal status:string
Run migrations with:
rails db:migrate
Django (Python)
Django auto-generates migrations from model changes:
python manage.py makemigrations
python manage.py migrate
Prisma (Node.js / TypeScript)
Prisma uses a schema-first approach. Define your models in schema.prisma, then generate and apply migrations:
npx prisma migrate dev --name add_orders_table
npx prisma migrate deploy # For production
.NET Entity Framework
EF Core generates migrations from your C# model classes:
dotnet ef migrations add CreateOrders
dotnet ef database update
For a detailed walkthrough, see our guide on automating database migrations in .NET with DbUp.
Flyway (Java / any language)
Flyway uses plain SQL files with a naming convention:
V1__create_users.sql
V2__create_orders.sql
V3__add_status_to_orders.sql
Run with:
flyway migrate
Setting up migrations in DeployHQ
Once your migration files are committed to your repository, configure DeployHQ to run them automatically during deployment using SSH commands.
Navigate to SSH Commands in your project sidebar and add a command set to run after files are transferred:

For a Laravel project, the command would be:
cd /var/www/your-app/current && php artisan migrate --force
The --force flag is required in production to skip the confirmation prompt. For Rails:
cd /var/www/your-app/current && RAILS_ENV=production bundle exec rails db:migrate
The migration command should be idempotent — if there are no pending migrations, it completes immediately without making changes.
Rollback strategies
Things go wrong. A migration might fail partway through, or the new code might not work as expected with the schema changes. Here's how to handle it:
Automatic rollback with atomic deployments
When using DeployHQ's zero downtime deployments, a failed deployment leaves the previous release in place. However, if the migration has already run, your database schema won't match the old code.
To handle this safely:
- Make migrations backwards-compatible when possible — add new columns as nullable, don't rename columns in the same deployment as the code change
- Use a two-phase approach for breaking changes: deploy the migration first (adding the new column), then deploy the code that uses it in a separate release
- Test migrations against a staging database before running them in production
Manual rollback
Most migration tools support rolling back the last batch of migrations:
# Laravel
php artisan migrate:rollback
# Rails
rails db:rollback
# Django
python manage.py migrate app_name 0003_previous_migration
# Prisma
# Prisma doesn't support automatic rollback — revert manually or use a previous migration
For a deeper look at deployment safety, see our guide on zero downtime database migration strategies.
Best practices
- Never edit a migration that's already been applied in production. Create a new migration instead.
- Keep migrations small and focused. One migration per logical change makes rollbacks simpler.
- Always include a
downmethod (or equivalent) so migrations can be reversed. - Test migrations on a copy of production data before deploying. Schema changes that work on an empty database may fail on one with millions of rows.
- Monitor migration duration. Long-running migrations (like adding an index to a large table) may need to run outside the deployment process using online DDL techniques.
- Use config files for database credentials rather than hardcoding them in your repository.
Ready to automate your deployments, including database migrations? Sign up for DeployHQ and run your first deployment in minutes.
If you have any questions about database migrations or any other aspect of DeployHQ, contact us at support@deployhq.com or reach out on X (Twitter).