-
Notifications
You must be signed in to change notification settings - Fork 73
Description
Problem
When using Remnawave with an external managed database through pgbouncer (Supabase, Neon, etc.), prisma migrate deploy hangs indefinitely during container startup.
This is a known Prisma issue — advisory locks used by prisma migrate are incompatible with pgbouncer's transaction pooling mode:
There is currently no documentation or built-in support for running Remnawave against an external database with connection pooling, which is the standard setup for Supabase, Neon, and most managed PostgreSQL providers.
Proposed Fix: docker-entrypoint.sh
Add an optional MIGRATION_TIMEOUT environment variable that wraps the migration command with a timeout. Without it set, behavior is identical to current — fully backward-compatible.
#!/bin/sh
echo "Starting entrypoint script..."
echo "Migrating database..."
if [ -n "$MIGRATION_TIMEOUT" ]; then
echo "MIGRATION_TIMEOUT=${MIGRATION_TIMEOUT}s — running migration with timeout..."
if ! timeout "${MIGRATION_TIMEOUT}" npm run migrate:deploy; then
EXIT_CODE=$?
if [ "$EXIT_CODE" -eq 143 ]; then
echo "⚠️ Migration timed out after ${MIGRATION_TIMEOUT}s (advisory lock issue with pgbouncer)."
echo "If schema is already up-to-date this is safe to ignore. Otherwise run migrations via a direct connection."
else
echo "Database migration failed! Exiting container..."
exit 1
fi
fi
else
if ! npm run migrate:deploy; then
echo "Database migration failed! Exiting container..."
exit 1
fi
fi
echo "Migrations deployed successfully!"
echo "Seeding database..."
if ! npm run migrate:seed; then
echo "Database seeding failed! Exiting container..."
exit 1
fi
echo "Entrypoint script completed."
exec "$@"Key behavior:
MIGRATION_TIMEOUT |
Behavior |
|---|---|
| Not set | Identical to current — waits indefinitely for migration |
Set (e.g. 30) |
Kills hung migration after N seconds. Exit code 143 (timeout) is treated as non-fatal so the container still starts |
The timeout approach works because in practice, if the schema is already current, the migration command hangs on the advisory lock rather than failing — so timing out and proceeding is safe for day-to-day container restarts. For actual schema changes, users run migrations separately via a direct (non-pooled) connection.
Configuration Guide for External Database with pgbouncer
Required DATABASE_URL parameters
DATABASE_URL=postgresql://user:password@host:6543/postgres?pgbouncer=true&connection_limit=13&pool_timeout=10&statement_cache_size=0
| Parameter | Why |
|---|---|
pgbouncer=true |
Tells Prisma to use pgbouncer-compatible mode |
connection_limit=13 |
Avoids exhausting pooler connections (adjust to your plan) |
pool_timeout=10 |
Prevents indefinite waits for a connection |
statement_cache_size=0 |
Required for pgbouncer — prepared statements don't work across pooled connections |
.env additions
# Timeout for prisma migrate deploy (seconds). Set when using pgbouncer.
# Recommended: 30
MIGRATION_TIMEOUT=30docker-compose adjustments for external DB
When using an external database, users should:
- Remove or comment out the
remnawave-dbservice - Remove
depends_on: remnawave-dbfrom theremnawavebackend service - Increase the health check
start_periodto120s(first migration through a remote pooler takes longer) - Add
MIGRATION_TIMEOUTto the environment
Suggested .env.sample additions
### External database (Supabase / Neon / managed PostgreSQL) ###
# If using pgbouncer, append: ?pgbouncer=true&connection_limit=13&pool_timeout=10&statement_cache_size=0
# DATABASE_URL=postgresql://user:password@pooler-host:6543/postgres?pgbouncer=true&connection_limit=13&pool_timeout=10&statement_cache_size=0
# Timeout for migration through pgbouncer (seconds). Unset = no timeout.
# MIGRATION_TIMEOUT=30Testing
- Production-tested with Supabase pgbouncer (transaction pooling, port 6543) — running stable
- Local PostgreSQL without
MIGRATION_TIMEOUTset: no behavior change, identical to current entrypoint - Timeout value of
30works reliably — migrations either complete in <5s or hang indefinitely (advisory lock), no false positives observed