Use this guide with CHANGELOG.md: the changelog names release history, and this guide explains how existing apps should move safely. Fresh installs should stay on README and Getting Started.
CI validates the documented upgrade paths from generated Phoenix apps before each Hex publish. Keep future entries newest-first, action-oriented, and focused on adopter work rather than duplicating the changelog.
Version index
0.4.0 schema isolation
Applies to
Fresh installs and existing apps moving to Rindle 0.4.0 or later, which includes
the versioned Rindle.Migration module. Existing apps that already applied
Rindle's legacy packaged migrations can keep those migrations in place.
What changed
Rindle now exposes a host-migration API for Rindle-owned tables:
Rindle.Migration.up(version: 1) and
Rindle.Migration.down(version: 1). Fresh installs default to the rindle
schema. The only compatibility pairing is explicit prefix: "public" with a
release compiled for the public schema.
Host apps own Oban.Migration, the shared oban_jobs table, and their
schema_migrations ledger. Rindle does not create, move, or own that host
infrastructure.
Upgrade steps
Fresh installs
Create normal host-app migration files. Install Oban first:
defmodule MyApp.Repo.Migrations.AddObanJobs do
use Ecto.Migration
def up, do: Oban.Migration.up()
def down, do: Oban.Migration.down(version: 1)
endThen install Rindle's tables with the pinned version:
defmodule MyApp.Repo.Migrations.InstallRindle do
use Ecto.Migration
def up, do: Rindle.Migration.up(version: 1)
def down, do: Rindle.Migration.down(version: 1)
endRun the host app's normal migration workflow:
mix ecto.migrate
Rollback:
Rindle.Migration.down/1is destructive. Back up the database before runningRindle.Migration.down(version: 1); it removes Rindle-owned tables only and does not manageoban_jobsorschema_migrations.
Existing populated public installs
Prepare a maintenance window: back up the database, then stop or drain Rindle HTTP
writers and Oban workers that invoke Rindle. Ecto's migrator lock
serializes migrators; it does not quiesce application traffic. PostgreSQL
ALTER TABLE can require an ACCESS EXCLUSIVE lock. Run the host migration as
the database owner, or as a role that owns the seven Rindle relations and has
the database CREATE privilege needed to provision rindle. A lock-timeout
refusal is bounded: keep traffic stopped, investigate the competing lock, and
run the host migration again only in the maintenance window.
Create a host-owned migration. Do not create rindle yourself. The forward
helper first classifies the complete public-only Rindle state and required
privileges, then creates an absent rindle destination inside the same host
transaction immediately before it moves the fixed six Rindle tables plus the
rindle_migration_versions marker. If creation or usability privileges are
insufficient, it fails boundedly; transaction rollback leaves no partial
destination or moved relation.
defmodule MyApp.Repo.Migrations.MoveRindleToSchema do
use Ecto.Migration
def up do
execute("SET LOCAL lock_timeout = '5s'")
Rindle.Migration.move_public_to_rindle(version: 1)
end
def down do
execute("SET LOCAL lock_timeout = '5s'")
Rindle.Migration.move_rindle_to_public(version: 1)
end
endRun the host migration while writers and workers remain drained:
mix ecto.migrate
Then deploy the build compiled for rindle that matches this migration.
Verify the deployment in this order:
mix rindle.doctor
mix rindle.runtime_status
Doctor confirms the Rindle and host-owned Oban setup; runtime status confirms
that the deployed application can report its operational state. Verify all
seven Rindle relations and normal reads and writes after those commands.
Rindle does not touch oban_jobs or schema_migrations; keep Oban
configuration and the host migration ledger unchanged.
Use the guarded reverse only while the application remains quiesced and state is
exactly reversible: there have been no post-move writes or later migrations, and
you can redeploy the previous public-compiled release. It does not drop the
rindle schema. Otherwise, restore the backup. Rindle.Migration.down/1 is
destructive teardown, not a populated-upgrade rollback.
Existing legacy installs
If your app already applied Rindle's legacy packaged migrations, leave that
history in place. Do not delete, replay, or rewrite already-applied legacy
migration files just to adopt this release. Use the new Rindle.Migration
module for fresh installs and future versioned migration work.
Verification
Run the doctor after migrations:
mix rindle.doctor
mix rindle.doctor should show the Rindle-owned schema as ready and should
treat oban_jobs as host-owned Oban setup.
0.1.3 and earlier -> current AV-aware runtime
Applies to
Apps that already ship Rindle from the pre-0.1.4 image-only shape and need to move onto the current AV-aware runtime contract.
What changed
The runtime now supports AV-aware assets and variants. Existing adopters need to confirm runtime ownership, keep explicit host plus packaged migrations, validate the upgraded environment, and use the bounded repair verb that matches the observed state.
Upgrade steps
1. Confirm runtime ownership and AV prerequisites
Before you touch migrations, make sure the host app still owns the same runtime boundaries:
- Rindle persists through your adopter-owned Repo.
- Oban stays on the default
Obaninstance and the host app owns that supervision tree. - Install
FFmpeg >= 6.0before you enable AV variants or diagnose AV work.
If you are bumping the package version as part of the upgrade, fetch the new dependency first:
mix deps.get
If you only need the greenfield setup details again, return to Getting Started. This guide assumes the app already owns its Repo, Oban config, and storage configuration.
2. Run explicit host and packaged migrations
Run your host migrations and the packaged Rindle migrations explicitly. The
canonical upgrade path stays on Application.app_dir(:rindle, "priv/repo/migrations"):
Application.ensure_all_started(:rindle)
{:ok, _pid} = MyApp.Repo.start_link()
host_path = Path.join([File.cwd!(), "priv", "repo", "migrations"])
rindle_path = Application.app_dir(:rindle, "priv/repo/migrations")
unless File.dir?(rindle_path) do
raise "Rindle migration path missing: #{rindle_path}"
end
{:ok, _, _} =
Ecto.Migrator.with_repo(MyApp.Repo, fn repo ->
for path <- [host_path, rindle_path] do
Ecto.Migrator.run(repo, path, :up, all: true)
end
end)Rindle still does not hide this behind a public install task. The host app owns the migration handoff.
3. Validate the upgraded runtime
Run the read-only environment check immediately after migrations:
mix rindle.doctor
mix rindle.doctor validates setup and drift. If it reports FFmpeg, Oban, or
migration issues, fix those before you attempt any repair command.
4. Inspect degraded upgraded work when needed
If a specific upgraded asset or variant looks wrong after the migration, inspect the bounded runtime report before you mutate anything:
mix rindle.runtime_status --format json
mix rindle.runtime_status is optional in the happy path. Use it when you need
to confirm whether the problem is failed asset-scoped work,
stale/missing drift, or broader runtime residue. Deep diagnostics and error
maps stay in Operations and
Troubleshooting.
5. Repair one upgraded asset through the public facade
For one failed upgraded asset, use the asset-scoped repair surface:
asset_id = "..."
{:ok, report} =
Rindle.requeue_variants(asset_id, variant_names: ["web_720p"])Rindle.requeue_variants/2 is the sharp lane for one asset. It re-enqueues the
named failed variants without pulling ready, queued,
processing, stale, or missing siblings into the run.
6. Reserve broad drift repair for stale or missing variants
Do not use asset-scoped requeue as a surrogate for profile drift or missing
storage objects. For broader derivative drift, stay on:
mix rindle.regenerate_variants
That command is the broad maintenance lane for stale or missing variants
after recipe, preset, or storage drift.
Verification
Run the same checks that CI uses for this path:
- Confirm the explicit host plus packaged migrations completed.
- Run
mix rindle.doctor. - If needed, inspect degraded work with
mix rindle.runtime_status --format json. - Repair one failed upgraded asset with
Rindle.requeue_variants(asset_id, variant_names: ["web_720p"]). - Reserve broad drift repair for stale or missing variants with
mix rindle.regenerate_variants.
Next Reads
- Operations for the day-2 verb map and task boundaries
- Troubleshooting for error-state recovery guidance
- Getting Started for the greenfield install path