Skip to content

Bug: medusa db:migrate deadlocks (hangs) on aarch64/ARM64 Linux; works on x86_64 #16011

Description

@aigoup

Bug: medusa db:migrate deadlocks (hangs forever) on aarch64/ARM64 Linux; works on x86_64

Summary

npx medusa db:migrate hangs indefinitely at Running migrations... on ARM64 Linux hosts, but the identical project + database completes migrations normally on x86_64 (both x86_64 Linux and x86_64 macOS). The hang is an unresolved promise — the Node event loop goes fully idle with no pending I/O — inside the per-module migration transaction. It reproduces with a fresh create-medusa-app project (no custom modules), across Medusa 2.16.0 and 2.17.2, Node 20 and 22, and multiple base images.

Environment where it hangs

  • Arch: aarch64 (ARM64) — Oracle Cloud Ampere A1, Ubuntu 24.04, kernel 6.8.0-1027-oracle
  • Node: tested 20.18 / 20.20 / 22 (all hang)
  • Medusa: 2.17.2 and 2.16.0 (all hang)
  • Postgres 16, Redis 7 (also reproduces with REDIS_URL unset → "fake redis")
  • Reproduces both in Docker and on the bare host (so not a container/sandbox issue)

Environment where it works

  • x86_64 macOS (local) and x86_64 GitHub Actions ubuntu-latest — same repo, same DB, migrations complete in seconds.

Steps to reproduce

create-medusa-app app --version 2.17.2 --skip-db --no-browser
cd app/apps/backend
DATABASE_URL=postgres://user:pass@host:5432/db REDIS_URL= NODE_ENV=production \
  JWT_SECRET=x COOKIE_SECRET=x STORE_CORS=http://x ADMIN_CORS=http://x AUTH_CORS=http://x \
  npx medusa db:migrate

On ARM64: prints Running migrations... (and, on a fresh DB, Migrations table created successfully) then hangs forever. On x86_64: completes.

Diagnosis

  • A minimal bare knex transaction using the exact same pattern Medusa uses — createPgConnection({ pool: { min:1, max:1 } }) + pg_advisory_xact_lock(hashtext(...)) inside knex.transaction()works fine on the same ARM64 host. So knex/tarn/pg/advisory-locks are not the cause.
  • An async_hooks trace of the hung process shows the never-resolving promise chain is:
    @medusajs/modules-sdk .../medusa-app.js runMigrations
      → applyMigration → @medusajs/utils executeWithConcurrency → executeNext (Promise.allSettled)
        → run (medusa-app.js) → lockKnex.transaction()
          → knex .../execution/transaction.js:79/85 (new Transaction, _evaluateContainer) → Promise.then  ← never resolves
    
  • A Node diagnostic report at hang time: event loop idle (loopIdleTimeSeconds climbing), the only referenced active handle is one idle Postgres TCP socket (readable, no data pending), no timers. On the Postgres side that connection is state=idle, wait_event=ClientRead (server already answered, waiting for the client) — i.e. the client is awaiting a completion that will never come.

What was ruled out (all still hang on ARM64)

base image node:20-slim / node:22-slim / node:20 / node:20-alpine; seccomp=unconfined; UV_USE_IO_URING=0; Docker embedded DNS (tested via direct IP); Redis on/off; MTU; telemetry; --skip-links / --skip-scripts / --concurrency 1; Medusa 2.16.0 and 2.17.2.

Impact

Medusa cannot run its own migrations on ARM64 Linux hosts (e.g. Oracle Ampere, AWS Graviton) — a common/cheap deployment target. Runtime (medusa start) works fine on ARM64; only the migrator deadlocks. Current workaround: run medusa db:migrate from an x86_64 machine against the same DB.

Happy to provide the full async_hooks dump / diagnostic report.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions