Scani schema migrator. Applies the app and queue schemas. github.com/MGrin/scani-oss
8.1K
Schema migration runner for Scani — the self-hostable, open-source portfolio tracker for crypto and traditional assets.
A one-shot container that applies pending schema migrations against your
Postgres and exits. It applies both schemas Scani needs: the application
tables (Drizzle) and the job-queue schema (BullMQ). Run it before the first
boot — the api and worker do not create either one, so an unmigrated database
returns schema "bullmq" is not initialized on the first background job. Migrations live OUTSIDE the
scani/api,
scani/worker, and
scani/data-provider runtime
images by design — schema changes are an operator concern, not something
the app silently does on its own.
latest — highest semver release tag1.2.3 / 1.2 / 1 — semver release tagsAlways use the same tag you're upgrading your app images to. Mixing
e.g. scani/migrate:1.2.0 with scani/api:1.3.0 is unsupported.
The recommended way to run this image is via the reference
docker-compose.prod.yml,
which exposes it as a profile-gated one-shot service:
# Step 1 — apply migrations
docker compose -f docker-compose.prod.yml --profile migrate run --rm migrate
# Step 2 — bring the rest of the stack up
docker compose -f docker-compose.prod.yml up -d
Re-run step 1 on every upgrade (after docker compose pull) before
restarting the long-running services. The migrator is idempotent —
already-applied migrations are skipped.
For Kubernetes Jobs, CI deploy steps, or any orchestrator that isn't docker-compose:
docker run --rm \
-e DATABASE_URL='postgres://user:pass@your-postgres-host:5432/scani?sslmode=require' \
scani/migrate:latest /app/migrate --allow-remote your-postgres-host
--allow-remote <host> is required for any database that is not on
loopback, and the host you name has to match the one in DATABASE_URL.
That is deliberate: it is the one guard against a stale connection string
rewriting the wrong database unnoticed. Without it the container refuses
and exits 1 having changed nothing.
Successful output:
🎯 Target: postgres://your-postgres-host:5432/scani
🔓 Non-local target named on the command line (--allow-remote your-postgres-host)
🔄 Starting database migrations...
📍 PostgreSQL connection (ssl=require)
📂 Migrations folder: /app/migrations
✅ Migrations completed successfully — 63 applied, 0 already present
📦 BullMQ schema migrated | schema=bullmq
migrate: application + queue schemas applied
Exit code is 0 on success, 1 on any failure. A failing application
migration stops before the queue schema is touched.
| Variable | Purpose |
|---|---|
DATABASE_URL | Postgres 16+ connection string. SSL mode is auto-detected from the URL (?sslmode=disable for local, require for hosted). |
That's the only variable the migrator reads. It does NOT need the
ENCRYPTION_KEY, BETTER_AUTH_SECRET, or any other app-level secret.
bun build --compile) on
debian:bookworm-slim — no bun runtime and no node_modules treescripts/migrate.ts,
the same entrypoint bun run db:migrate uses from sourcepackages/infra/db/src/migrations/Image size is well under 100 MB. The container exits as soon as migrations complete; nothing long-running.
Full source, architecture, and contribution guidelines: https://github.com/MGrin/scani-oss
MIT licensed.
Content type
Image
Digest
sha256:2e9437a18…
Size
66.3 MB
Last updated
2 days ago
docker pull scani/migrate