Migrations
AEGIS uses Alembic for database schema migrations, managed per-service.
Approach
Each service that owns database tables manages its own Alembic migration history. The base schema is defined in SQL files mounted into the PostgreSQL container and executed at startup.
The SQL init files create the foundational schema including Apache AGE setup, pgvector extension, and core tables. Alembic migrations handle incremental changes after initial setup.
Initial Schema
The Docker PostgreSQL container automatically runs the following SQL files on first boot:
| File | Purpose |
|---|---|
00-create-extension-age.sql | Create the AGE extension |
infrastructure/docker/postgres/init.sql | Core tables: audit logs, users, episodic memories, agents, skills, approvals, conversations, budget |
infrastructure/docker/postgres/002_checklist_compliance_tables.sql | Checklist templates, filing checklists, compliance status, rule versions, filing rule snapshots |
infrastructure/docker/postgres/007_entity_type_definitions.sql | Entity type system: type definitions, field definitions, relationship rules, RRC mappings |
These scripts collectively:
- Create the
aegisdatabase and user - Enable extensions:
age,pgvector,uuid-ossp - Create the
oilgasgraph in Apache AGE - Create all base tables with indexes and constraints
- Set up audit trail triggers (prevent UPDATE/DELETE on
audit_logs)
Standalone Deploy Migrations
For databases that already exist (and therefore won’t re-run init.sql), standalone idempotent SQL migrations live under infrastructure/deploy/migrations/ and are applied directly with psql:
| File | Purpose |
|---|---|
infrastructure/deploy/migrations/001_users.sql | Create the users table (auth accounts) with the idx_users_email_lower case-insensitive unique index. Idempotent — safe to re-run. |
psql "$DATABASE_URL" -f infrastructure/deploy/migrations/001_users.sqlFresh databases get the users table from init.sql, so this migration is only needed to bring existing databases up to date.
Running Migrations
For services with Alembic configured:
# Navigate to the service directory
cd services/{service-name}
# Create a new migration
poetry run alembic revision --autogenerate -m "description of change"
# Run pending migrations
poetry run alembic upgrade head
# Check current migration status
poetry run alembic current
# Roll back one migration
poetry run alembic downgrade -1
# View migration history
poetry run alembic historyMigration Workflow
Creating a New Migration
- Make your model or schema changes in the service code.
- Run the autogenerate command to create a migration file:
cd services/{service-name}
poetry run alembic revision --autogenerate -m "add compliance_status table"- Review the generated migration in
alembic/versions/. Autogenerate is not perfect — verify that theupgrade()anddowngrade()functions are correct. - Test the migration in both directions:
# Apply
poetry run alembic upgrade head
# Verify
poetry run alembic current
# Roll back
poetry run alembic downgrade -1
# Re-apply
poetry run alembic upgrade head- Commit the migration file alongside the code changes.
Checking Migration Status
# Show current revision
poetry run alembic current
# Show all pending migrations
poetry run alembic history --indicate-current
# Show the SQL that would be run (without executing)
poetry run alembic upgrade head --sqlConventions
- One migration per logical change — don’t bundle unrelated schema changes into a single revision
- Descriptive revision messages — e.g.,
add compliance_status tablenotupdate schema - Always test migrations — run
upgradeanddowngradebefore committing - Never modify existing migrations — if a migration has been applied by other developers, create a new corrective migration instead
- Schema ownership — only the owning service should create migrations for its tables
- Idempotent where possible — use
IF NOT EXISTSguards in raw SQL within migrations
Adding Alembic to a New Service
If a service needs to manage its own tables, set up Alembic:
cd services/{service-name}
poetry add alembic asyncpg sqlalchemy
poetry run alembic init alembicConfigure alembic.ini with the database URL:
sqlalchemy.url = postgresql://aegis:aegis_local@localhost:5432/aegisUpdate alembic/env.py to import your models for autogenerate support:
from your_service.models import Base
target_metadata = Base.metadataIf using async database connections (which all AEGIS services do), you will need to configure Alembic’s env.py to use an async engine. See the SQLAlchemy docs on async migrations .
Resetting the Database
During development, you may need to start fresh. The most reliable way is to remove the Docker volume and recreate:
# Stop containers and remove volumes
docker compose down -v
# Recreate containers (init.sql runs automatically)
docker compose up -d
# Re-run seed scripts
curl -X POST http://localhost:8003/seed
cd services/orchestration-engine
poetry run python -m orchestration.seed_skills
poetry run python -m orchestration.seed_checklists
poetry run python -m orchestration.seed_rules
poetry run python -m orchestration.seed_demo_dataRunning docker compose down -v destroys all data in PostgreSQL and Redis. Only use this in local development. Never run this against a shared or production database.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
alembic.util.exc.CommandError: Can't locate revision | Migration file deleted or corrupted | Reset Alembic version table: DELETE FROM alembic_version then re-run |
relation already exists | Schema was created by init.sql before Alembic | Mark migration as applied: poetry run alembic stamp head |
target database is not up to date | Pending migrations exist | Run poetry run alembic upgrade head first |
No changes detected during autogenerate | Models not imported in env.py | Verify target_metadata is set correctly |