backend/scripts/data_migrations/. They handle one-off corrections, backfills, or clean-ups that cannot be expressed as a zero-downtime Alembic migration.
This page covers how to run the scripts. The rationale for each individual migration — the problem it fixes and what it changes, plus any relevant details such as conflict handling or idempotency — lives in the module docstring at the top of the corresponding script in
backend/scripts/data_migrations/. Read that before running one.How they run
Recent migrations are wired into the container startup script (backend/scripts/start/app.sh) and run automatically on every deploy, after Alembic migrations are applied. They are idempotent (no-op once the data is corrected) and non-fatal - a failure logs a warning and retries on the next startup.
This automatic execution is temporary. Each entry in app.sh is gated by a TODO: Remove this after ~<date> comment and is deleted once existing deployments have had time to upgrade. After that point the script is no longer invoked on startup.
The practical consequence: if you upgrade Open Wearables long after a migration shipped, the corresponding script may already have been removed from app.sh and will not run automatically. In that case, run it manually (see below). Scripts that were never added to app.sh must always be run manually.
Prerequisites
The scripts import from theapp package and connect to the database via the environment variables in backend/config/.env. You need:
- Docker Compose services running (
docker compose up -d) - The
appcontainer healthy (check withdocker compose ps) - A database with the latest Alembic migrations applied (
docker compose exec app uv run alembic upgrade head)
Running a script
All scripts are run inside theapp container using uv run:
Dry run first
Every script supports a--dry-run flag. Always run it first — it prints the rows that would be affected without making any changes:

