Database Migrations
Manage database schema changes with Alembic in SerpentStack.
Prerequisites
- Postgres running:
docker compose up -d postgres - Backend dependencies installed:
cd backend && uv sync
Creating a New Migration
After modifying or adding a SQLAlchemy model:
cd backend && uv run alembic revision --autogenerate -m "describe the change"
The migration description should be concise and specific, e.g., "add projects table", "add email column to users", "create index on tasks.status".
Review the generated file in backend/migrations/versions/. Verify:
- The
upgrade()function contains the expectedop.create_table,op.add_column, orop.create_indexcalls. - The
downgrade()function is the inverse of upgrade. - No unintended changes are included (Alembic sometimes detects phantom diffs).
Running Migrations
Apply all pending migrations:
cd backend && uv run alembic upgrade head
Apply only the next migration:
cd backend && uv run alembic upgrade +1
Checking Migration Status
See the current revision:
cd backend && uv run alembic current
See full migration history:
cd backend && uv run alembic history --verbose
Downgrading
Roll back the last migration:
cd backend && uv run alembic downgrade -1
Roll back to a specific revision:
cd backend && uv run alembic downgrade <revision_id>
Adding a New Table
Full workflow:
- Create the model file at
backend/app/models/{name}.py— inherit fromBase(providesid,created_at,updated_at). - Import the model in
backend/app/models/__init__.pyso Alembic detects it. - Generate the migration:
cd backend && uv run alembic revision --autogenerate -m "add {name}s table". - Review the generated migration file.
- Apply:
cd backend && uv run alembic upgrade head. - Verify: connect to the database and confirm the table exists.
Adding a Column to an Existing Table
- Edit the model in
backend/app/models/{name}.py-- add the new column. - Generate:
cd backend && uv run alembic revision --autogenerate -m "add {column} to {name}s". - Review: ensure only the expected
op.add_columnis present. - Apply:
cd backend && uv run alembic upgrade head.
Troubleshooting
| Problem | Solution |
|---|---|
Target database is not up to date |
Run uv run alembic upgrade head first |
Can't locate revision |
Check alembic.ini for correct script_location |
| Phantom diffs in autogenerate | Compare model against DB schema manually; add to Alembic's exclude list if needed |
relation already exists |
The migration was partially applied. Check alembic_version table and fix manually |
| Migration conflicts (multiple heads) | Run uv run alembic merge heads -m "merge migrations" |
ModuleNotFoundError on model import |
Ensure model is imported in backend/app/models/__init__.py |
| Docker not running | Testcontainers and local Postgres both require Docker Desktop to be running |
Seed Data
Populate the database with sample development data:
make seed
This runs the async seed CLI command at backend/app/cli/seed.py. The seed script is idempotent — running it multiple times will not create duplicates (it checks for existing rows first).