Skip to content
Latchkey

Ecto Migration Lock Timeout in CI

Ecto acquires a database advisory lock so two migrators cannot run at once. If a previous migrator died holding it, or another run is migrating concurrently, ecto.migrate waits - and can time out. The stale-lock case is transient.

What this error means

mix ecto.migrate stalls while acquiring the migration lock and then fails with a connection/lock timeout. It commonly follows a cancelled or OOM-killed CI job that held the advisory lock when it died.

Elixir output
** (DBConnection.ConnectionError) tcp recv: closed / timed out while
waiting for the Ecto migration advisory lock to be released

Common causes

Stale advisory lock from a killed run

A prior migrator was cancelled or OOM-killed before releasing the advisory lock. The next run waits on it. Advisory locks are session-scoped, so a truly dead session’s lock clears once that connection is gone.

Concurrent migrations

Two pipelines migrating the same database at once contend for the lock; one waits while the other runs.

How to fix it

Retry once the stale session clears

A bounded retry usually succeeds once the dead migrator’s connection is reaped and its advisory lock releases.

Terminal
for i in 1 2 3; do
  mix ecto.migrate && break
  echo "migration lock retry $i"; sleep 5
done

Serialize migrations to avoid real contention

  1. Use a CI concurrency group so only one migration job runs against a database at a time.
  2. Avoid two pipelines targeting the same database concurrently.
  3. Set a step timeout so a stuck lock surfaces fast instead of hanging.

Disable the lock only when safe

For a single-writer ephemeral CI database you can opt out of the migration lock, but never on a shared database.

config/test.exs
# config/test.exs
config :my_app, MyApp.Repo, migration_lock: false

How to prevent it

  • Serialize migration jobs with a CI concurrency group.
  • Set step timeouts so a stuck lock fails fast.
  • Keep one migrator per database; rely on the advisory lock for safety.

Frequently asked questions

What causes "Ecto migration lock"?
A prior migrator was cancelled or OOM-killed before releasing the advisory lock. The next run waits on it. Advisory locks are session-scoped, so a truly dead session’s lock clears once that connection is gone.
How do I fix Ecto migration lock?
A bounded retry usually succeeds once the dead migrator’s connection is reaped and its advisory lock releases.
Can Latchkey fix this automatically?
Yes. Latchkey runs your GitHub Actions on managed runners that detect this failure, apply the fix, and retry the job automatically - self-healing is on by default.

Related guides

References

Latchkey auto-heals failures like this one - detected, fixed, and retried without you. Start free → 30-day trial · No credit card