This lab is intended to be a guide/reference. There are no deliverables.
The Jarvis migrator service lives at backend/migrator/ and uses Alembic to manage database schema migrations. Since all backend microservices share a single Postgres database, the migrator is a standalone service that handles migrations for every table.
If you look at past versions of Jarvis, you’ll notice that in the main.py of each service, we had this line of code:
@app.on_event("startup")
async def startup_event():
"""Create database tables on startup"""
create_tables()
This would effectively attempt to create all tables on startup. However, if the schemas changed, this command would actually fail since it cannot determine what fields it needs to add.
Now, with alembic, we have a more robust migration system.
Project Structure
| Path | Description |
|---|---|
alembic.ini |
Alembic configuration file |
config.py |
Reads DATABASE_URL from .env.local |
migrations/env.py |
Alembic environment; imports all SQLAlchemy models |
migrations/versions/ |
Individual migration scripts (ordered by down_revision chain) |
migrations/script.py.mako |
Template for generating new migration files |
Prerequisites
Make sure your Docker Compose environment is running so the migrator can reach the database:
docker compose up --build
Create your .env.local
The migrator reads its database URL from backend/migrator/.env.local, which is gitignored. You need to create this file yourself, for our docker compose setup:
DATABASE_URL=postgresql://jarvis:password@localhost:5432/jarvis
Applying Existing Migrations
When you first clone the repo (or after pulling new migration files), you’ll need to apply all pending migrations to bring your database up to date:
# from backend/migrator
uv run alembic upgrade head
This applies every migration that hasn’t been run yet, in order. Alembic tracks which migrations have been applied via the alembic_version table in the database.
Creating a New Migration
When you change a SQLAlchemy model (e.g. add a column, create a new table), you need to generate a migration:
-
Make sure the model is imported in
migrations/env.py. Existing imports cover theUser,Note,Chat, andMessagemodels. If you add a new model, add its import there as well. -
Generate the migration:
uv run alembic revision --autogenerate -m "Short description of change"
-
Review the generated file in
migrations/versions/. Alembic auto-detects differences between your models and the current database state, but you should always verify theupgrade()anddowngrade()functions look correct. -
Apply the migration:
uv run alembic upgrade head
Common Commands
| Command | Description |
|---|---|
uv run alembic upgrade head |
Apply all pending migrations |
uv run alembic downgrade -1 |
Roll back the last migration |
uv run alembic downgrade <revision> |
Roll back to a specific revision |
uv run alembic current |
Show the current migration revision |
uv run alembic history |
Show the full migration history |
uv run alembic revision --autogenerate -m "msg" |
Generate a new migration from model changes |
Inspecting the Database
You can open a Postgres shell on the Docker Compose database to verify schema changes:
docker compose exec db psql -U jarvis -d jarvis
Then run \d to list all tables, or \d <table_name> to inspect a specific table’s columns.
Resetting the Database
If you need a clean slate (e.g. your local database is in a bad state), remove the Docker volume and re-apply migrations:
docker compose down
docker volume rm jarvis-monorepo_db_data
docker compose up --build
# Then from backend/migrator/:
uv run alembic upgrade head