Skip to content

Upgrades & migrations

Filearr upgrades are designed to be low-drama: the database bootstrap is idempotent, the search index is disposable and rebuildable, and migrations are managed by Alembic.

The golden rule: back up Postgres first

Before any upgrade, take a Postgres dump. It is the only irreplaceable store.

cd /opt/filearr
docker compose exec -T postgres pg_dump -U filearr -Fc filearr > filearr-$(date -u +%Y%m%dT%H%M%SZ).dump

See Backup & restore for the full procedure and how to verify a dump.

Docker Compose upgrade

cd /opt/filearr
git pull                                        # or fetch the new source
docker compose build --pull                     # rebuild the app/worker image
docker compose up -d                            # app auto-runs the idempotent migrate on start

The app container runs scripts/init_db.py itself on start, so up -d is the whole migration step. To watch migration output explicitly (or if you set FILEARR_AUTO_INIT_DB=false), run it by hand first — it is idempotent either way:

docker compose run --rm app python scripts/init_db.py

Proxmox LXC upgrade

Just re-run the deploy script — it pushes the new source, rebuilds, and runs the idempotent bootstrap for you:

bash proxmox/deploy-proxmox.sh

The script verifies (via a build stamp) that the running image was actually built from the source it just pushed, and runs a functional smoke test. If the stamp mismatches, retry with a forced clean build: FORCE_REBUILD=1 bash proxmox/deploy-proxmox.sh.

Migration behavior (Alembic)

The bootstrap, scripts/init_db.py, is idempotent and handles every state:

  • Brand-new database — creates the schema, stamps the Alembic baseline, runs migrations to head.
  • Existing database, no alembic_version table (a restore from an unstamped dump) — stamps the baseline, then upgrades to head.
  • Up-to-date database — a no-op beyond confirming head.

It then applies the Procrastinate job-queue schema (guarded — Procrastinate's own schema step is not idempotent) and ensures the Meilisearch index exists.

Inspect state directly if you need to:

docker compose exec app alembic current   # revision the DB is at
docker compose exec app alembic heads      # revision the code expects

Postgres 18 is required

The Alembic baseline uses uuidv7() server defaults, so Postgres 18+ is required and the migration fails fast on older Postgres.

Rebuild the search index after field changes

Meilisearch is a disposable projection. Any upgrade that changes indexed fields requires a rebuild so the projection matches the new schema:

curl -X POST http://localhost:8484/api/v1/system/rebuild-index

The rebuild uses a shadow-index swap, so live search stays up during it. A notable case: enabling path-scoped RBAC search adds a scope attribute to the index; until the rebuild completes, scoped non-admin users fail closed to empty results (admins and API keys are unaffected).

Downgrades

Alembic downgrades exist but are best-effort and can lose recycle-bin data. Always take a fresh dump before downgrading, and rebuild the search index afterward.