Automating Laravel Migration Squashing with GitHub Actions
Keep your migration history clean with automated monthly schema dumps.
Laravel’s schema:dump command is a great way to keep your migration history manageable. Instead of running dozens (or hundreds) of migrations during tests and deployments, Laravel can load a single SQL dump and only run migrations created after the dump. The catch? Someone has to remember to run it. Here’s how we automated the entire process with GitHub Actions.
The Problem
Over time, Laravel applications accumulate migrations. Each new feature, refactor, or bug fix adds to the pile. This creates several issues:
- Slower test runs as migrations execute sequentially
- Longer deployment times for fresh database setups
- Cognitive overhead when scrolling through years of migration history
Laravel’s solution is php artisan schema:dump, which captures the current database state as a SQL file. When migrations run, Laravel loads this dump first, then only executes migrations created after the dump.
The problem? It’s easy to forget. Months pass, migrations pile up, and the schema dump becomes stale.
The Solution: Monthly Automation
We run a GitHub Actions workflow on the first of every month that:
- Sets up a fresh database and runs all migrations
- Generates a new schema dump
- Creates a pull request if the schema changed
name: Squash migrations
on:
workflow_dispatch:
schedule:
- cron: '0 13 1 * *' # 8am EST/9am EDT on the 1st of every month
jobs:
squash:
name: Squash migrations
runs-on: ubuntu-latest
env:
DB_CONNECTION: mysql
DB_DATABASE: app_test
DB_USERNAME: root
DB_PASSWORD: root
APP_ENV: testing
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- name: Install dependencies
run: composer install --no-scripts --no-progress --prefer-dist
- name: Prepare environment
run: |
cp .env.example .env
php artisan key:generate
- name: Start MySQL and run migrations
run: |
sudo /etc/init.d/mysql start
php artisan migrate --env=testing --force
- name: Generate schema dump
run: php artisan schema:dump --env=testing
- name: Create PR if schema changed
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add database/schema/mysql-schema.sql
if git diff --staged --quiet; then
echo "No changes to commit"
else
BRANCH_NAME="chore/schema-dump-$(date '+%Y%m%d')"
git checkout -b "$BRANCH_NAME"
git commit -m "Update schema dump"
git push origin "$BRANCH_NAME"
gh pr create \
--title "Update schema dump - $(date +%Y-%m-%d)" \
--body "Automated schema dump update from GitHub Actions" \
--head "$BRANCH_NAME" \
--base main
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
How It Works
Scheduled execution: The cron expression 0 13 1 * * runs at 1pm UTC on the first of every month. We also include workflow_dispatch to allow manual triggers when needed.
Database setup: GitHub Actions runners include MySQL by default. We start the service, run migrations to build the current schema, then dump it.
Conditional PR creation: The workflow checks git diff --staged --quiet to determine if the schema actually changed. No changes means no PR—keeping your repository clean.
Automatic PR: When changes exist, the workflow creates a timestamped branch, commits the schema dump, and opens a pull request for review.
Why a Pull Request?
We create a PR rather than committing directly to main for a few reasons:
- Review opportunity: Schema changes are visible before merging
- CI validation: The PR triggers your normal test suite to catch any issues
- Audit trail: PRs provide context and history for schema changes
Adapting for PostgreSQL
If you’re using PostgreSQL instead of MySQL, adjust the workflow:
env:
DB_CONNECTION: pgsql
DB_DATABASE: app_test
DB_USERNAME: postgres
DB_PASSWORD: postgres
# ...
- name: Start PostgreSQL and run migrations
run: |
sudo systemctl start postgresql
sudo -u postgres createdb app_test
php artisan migrate --env=testing --force
- name: Generate schema dump
run: php artisan schema:dump --env=testing
# Update the git add path
- name: Create PR if schema changed
run: |
git add database/schema/pgsql-schema.sql
# ... rest of the script
Benefits
This automation provides:
- Consistent schema dumps without relying on human memory
- Faster CI runs as migrations stay minimal between dumps
- Clean git history with predictable monthly updates
- Zero maintenance once configured
The workflow runs quietly in the background. Once a month, if your schema has changed, a PR appears for review. If nothing changed, nothing happens. Set it and forget it.