2.8 KiB
Database migrations policy
This document describes how database schema changes are handled for Backupchecks .
Overview
- The baseline schema is defined in
backend/app/models.py. - On application startup, the following is executed inside
create_app():db.create_all()– creates any missing tables and columns defined in the models.run_migrations()– executes in-image SQL migrations frombackend/app/migrations.py.
This approach allows:
- Clean databases to be created automatically.
- Existing databases to be upgraded in-place without manual SQL.
- Safe repeated restarts: migrations are idempotent and can be run multiple times.
Adding migrations
When you change the schema in a way that is not automatically covered by db.create_all() (for example,
altering column nullability, adding constraints, backfilling data), follow these steps:
-
Add or adjust the corresponding model(s) in
models.py. -
In
migrations.py:-
Add a new function, for example:
def migrate_xyz():- Perform the required SQL using
db.get_engine()andsqlalchemy.text. - Always check the current state first (e.g. whether a column or constraint already exists).
-
Call this function from
run_migrations()in the correct order.
-
-
Do NOT remove older migration functions. They must remain so that:
- Existing databases can still be upgraded from older versions.
- New installations run all migrations but older ones become no-ops because their checks see that the changes are already applied.
-
Each migration must be idempotent:
- It should detect whether its change is already in place and then exit without error.
- This allows
run_migrations()to be executed on every startup.
Current migrations (initial set)
Implemented in backend/app/migrations.py:
-
migrate_add_username_to_users()- Adds a
usernamecolumn to theuserstable if it does not exist. - Backfills
usernamefromemailwhere possible. - Sets
usernametoNOT NULL. - Adds a UNIQUE constraint on
users.username.
- Adds a
-
migrate_make_email_nullable()- Ensures the
emailcolumn onusersis nullable. - If the column is currently
NOT NULL, the migration executes:
ALTER TABLE "users" ALTER COLUMN email DROP NOT NULL.
- Ensures the
-
run_migrations()- Calls the above migrations in order.
- Logs progress to stdout so changes are visible in container / Portainer logs.
Future changes
-
Every time you introduce a non-trivial schema change, update:
backend/app/models.pybackend/app/migrations.py- This document (
docs/migrations.md) – add a short description of the new migration.
-
When sharing the repository state (for example in a ZIP), always include the current
migrations.pyand this document so the migration history and policy are clear.