-
Notifications
You must be signed in to change notification settings - Fork 1
Supabase Migrations CI
This guide explains how to set up automated Supabase migrations using GitHub Actions.
The supabase-migrations.yml workflow automatically applies database migrations to your Supabase project when migration files change. It supports:
- ✅ Automatic migration on push to
main(production) orstagingbranches - ✅ Migration validation on pull requests (dry-run)
- ✅ Manual workflow dispatch with environment selection
- ✅ Destructive operation detection
- ✅ SQL syntax validation
- ✅ Migration rollback guidance
- ✅ Multi-environment support (production + staging)
The workflow runs automatically when:
-
Push to main/staging with changes to
supabase/migrations/*.sql - Pull request to main/staging with migration file changes (validation only)
- Manual dispatch from GitHub Actions UI (can choose environment)
You need to configure the following secrets in your GitHub repository settings:
| Secret Name | Description | How to Get It |
|---|---|---|
SUPABASE_ACCESS_TOKEN |
Supabase CLI access token | Generate from Dashboard |
SUPABASE_URL |
Project URL | Dashboard > Settings > API > Project URL |
SUPABASE_SERVICE_ROLE_KEY |
Service role key (anon key) | Dashboard > Settings > API > service_role key |
SUPABASE_PROJECT_REF |
Project reference ID | From your project URL: https://ynleroehyrmaafkgjgmr.supabase.co → ynleroehyrmaafkgjgmr
|
SUPABASE_DB_PASSWORD |
Database password | Dashboard > Settings > Database > Database password |
If you have a separate staging Supabase project, add these:
| Secret Name | Description |
|---|---|
SUPABASE_STAGING_URL |
Staging project URL |
SUPABASE_STAGING_SERVICE_ROLE_KEY |
Staging service role key |
SUPABASE_STAGING_PROJECT_REF |
Staging project reference |
SUPABASE_STAGING_DB_PASSWORD |
Staging database password |
- Go to https://supabase.com/dashboard/account/tokens
- Click "Generate new token"
- Give it a name like "GitHub Actions"
- Copy the token (you won't be able to see it again!)
- Add it to GitHub as
SUPABASE_ACCESS_TOKEN
-
Open your Supabase project dashboard
-
Go to Settings > API
-
Copy:
-
Project URL →
SUPABASE_URL -
service_role key →
SUPABASE_SERVICE_ROLE_KEY -
Project Ref (from URL) →
SUPABASE_PROJECT_REF
-
Project URL →
-
Go to Settings > Database
-
Reset database password if needed and copy it →
SUPABASE_DB_PASSWORD
- Go to your GitHub repository
- Click Settings > Secrets and variables > Actions
- Click "New repository secret"
- Add each secret with the exact names shown above
- Create or modify migration files in
supabase/migrations/ - Commit and push to
mainorstagingbranch - The workflow automatically:
- Validates SQL syntax
- Checks for destructive operations
- Applies migrations to the appropriate environment
- Notifies on success/failure
- Go to Actions tab in GitHub
- Select "Supabase Migrations" workflow
- Click "Run workflow"
- Choose:
-
Environment:
productionorstaging -
Dry run:
trueto validate only,falseto apply
-
Environment:
- Click "Run workflow"
Migrations should follow this pattern:
supabase/migrations/YYYYMMDDHHMMSS_descriptive_name.sql
Example:
supabase/migrations/20251126120000_add_user_preferences_table.sql
-
Always make migrations idempotent:
-- ✅ Good CREATE TABLE IF NOT EXISTS users (...); DROP POLICY IF EXISTS "Users can read own data" ON users; CREATE POLICY "Users can read own data" ON users ...; -- ❌ Bad CREATE TABLE users (...); CREATE POLICY "Users can read own data" ON users ...;
-
Use transactions for multi-step migrations:
BEGIN; ALTER TABLE users ADD COLUMN IF NOT EXISTS new_field TEXT; UPDATE users SET new_field = 'default' WHERE new_field IS NULL; COMMIT;
-
Add comments to explain complex logic:
-- Add RLS policy for user data access -- Users can only read their own data via privy_user_id CREATE POLICY "Users read own data" ON users FOR SELECT USING ( privy_user_id::text = auth.uid()::text );
-
Avoid destructive operations in production: The workflow blocks these in production:
DROP DATABASEDROP SCHEMA publicTRUNCATE ... CASCADEDELETE FROM ... WHERE 1=1
- Determines target environment based on branch/input
- Sets appropriate credentials for the environment
- Checks SQL syntax
- Detects dangerous operations
- Lists new migrations
- Authenticates with Supabase
- Links to the correct project
- Applies pending migrations
- Verifies application
- Provides rollback instructions
- Links to backup restoration
- Posts summary to PR (if applicable)
- Shows migration status
Cause: Invalid SUPABASE_ACCESS_TOKEN or SUPABASE_DB_PASSWORD
Solution:
- Regenerate access token from Supabase dashboard
- Update GitHub secret
- Verify database password is correct
Cause: Invalid SUPABASE_PROJECT_REF or network issues
Solution:
- Verify project reference matches your project URL
- Check that the project exists and is accessible
- Ensure you have correct permissions
Cause: Migration file was already run on the database
Solution:
# Locally, repair the migration status
supabase migration repair --status applied <timestamp>Cause: Migration is not idempotent
Solution: Update migration to use DROP POLICY IF EXISTS before creating:
DROP POLICY IF EXISTS "policy_name" ON table_name;
CREATE POLICY "policy_name" ON table_name ...;Cause: Sequence referenced before creation
Solution: Create sequences before tables that use them:
-- Create sequences first
CREATE SEQUENCE IF NOT EXISTS table_id_seq;
-- Then create table
CREATE TABLE table_name (
id bigint DEFAULT nextval('table_id_seq'::regclass),
...
);
-- Set ownership
ALTER SEQUENCE table_id_seq OWNED BY table_name.id;If migrations fail and you need to rollback:
# Mark migration as reverted
supabase migration repair --status reverted <timestamp>
# Then fix the migration file and reapply
supabase db push- Go to Supabase Dashboard
- Navigate to Database > Backups
- Select a backup from before the migration
- Click "Restore"
Before pushing, test migrations locally:
# Start local Supabase
supabase start
# Apply migrations
supabase db push
# If successful, commit and push
git add supabase/migrations/
git commit -m "Add new migration"
git push| Branch | Environment | Auto-deploy |
|---|---|---|
main |
Production | ✅ Yes |
staging |
Staging | ✅ Yes |
develop |
Manual only | ❌ No |
| PR to main/staging | Validation only | ❌ No |
When you open a PR with migration changes:
- ✅ Migrations are validated (syntax, dangerous ops)
- ❌ Migrations are NOT applied automatically
- ℹ️ Comment shows validation results
- ✅ Safe to merge after validation passes
- Never commit secrets: Use GitHub Secrets, never hardcode credentials
- Use service_role key carefully: It bypasses RLS, only use in CI/CD
- Limit access token scope: Use project-specific tokens when possible
- Review destructive operations: Manual review required for DROP/TRUNCATE
- Test in staging first: Always test migrations in staging before production
graph LR
A[Create Migration] --> B[Test Locally]
B --> C[Push to Staging Branch]
C --> D[CI Validates & Applies to Staging]
D --> E{Tests Pass?}
E -->|Yes| F[Create PR to Main]
E -->|No| B
F --> G[CI Validates Migration]
G --> H[Merge PR]
H --> I[CI Applies to Production]
After migration:
- Check workflow logs: GitHub Actions > Supabase Migrations
- Verify in Supabase Dashboard: Database > Migrations
- Test application: Ensure API still works
- Monitor errors: Check error logs for any issues
If you encounter issues:
- Check workflow logs in GitHub Actions
- Review this documentation
- Check Supabase CLI docs: https://supabase.com/docs/guides/cli
- Open an issue in the repository
Last Updated: 2025-11-26 Workflow Version: 1.0
Reading Path (start here, in order)
- Conceptual Model
- Stability Definition
- Conceptual Model Features
- Features
- Delta Report
- Features-Acceptance-Criteria
Testing
Security & Access
Billing
Monitoring
Features
Providers
Operations
Data References