DbConnectionMonitor recovery reconnects without configured database name, falling back to Postgres username (FATAL: database "<user>" does not exist, looping every ~10s)
Environment
- n8n version: 2.40.5 (also reproduced on 2.29.10 before upgrading, in an attempt to rule out the version as the cause)
- Deployment: self-hosted via Docker Compose,
n8nio/n8n:latestimage - Database: external PostgreSQL 15.19, standard Debian-based image, reached over the Docker bridge network (not localhost, not a managed/cloud Postgres)
- Execution mode: regular (not queue mode)
- Relevant env vars:
DB_TYPE=postgresdb,DB_POSTGRESDB_HOST,DB_POSTGRESDB_PORT=5432,DB_POSTGRESDB_DATABASE(set to a valid, existing database),DB_POSTGRESDB_USER,DB_POSTGRESDB_PASSWORD
Summary
Postgres logs a continuous FATAL: database "<value of DB_POSTGRESDB_USER>" does not exist roughly every 10 seconds, indefinitely. The database name in the error is not our configured DB_POSTGRESDB_DATABASE, and not n8n's hardcoded default ('n8n') — it is an exact match for the configured DB_POSTGRESDB_USER value. That database does not exist and was never meant to.
This matches node-postgres (pg)'s documented fallback behavior: a connection opened without an explicit database field defaults to using the connecting username as the database name. Something in n8n's reconnect path appears to open a new connection without carrying the configured database name forward.
Reproduction / persistence across changes
The loop has proven deterministic and unaffected by every mitigation we tried:
- Restarting only the n8n container — error recurs within ~34 seconds of n8n startup, same cadence.
- Upgrading n8n 2.29.10 → 2.40.5 — no change in behavior.
- Full host reboot (both Postgres and n8n cold-started) — Postgres finished its own crash-recovery and logged
database system is ready to accept connections; the very next event, 0.4 seconds later, was the firstFATAL: database "<user>" does not exist. The loop then continued at the same steady ~10-second interval indefinitely.
We have ruled out: disk space (host and container), memory/CPU pressure, any config file change at the time the errors started, and any container restart coinciding with error onset (errors began mid-uptime, not around a restart).
First occurrence in our case was ~18 days before we noticed it in the logs (it produced no functional symptoms, so it went unnoticed — see below).
Functional impact
None observed, consistently, across ~3 weeks of the loop running:
- Workflow executions succeed normally.
- Webhook endpoints respond with expected status codes at normal latency.
- Active workflow count is unaffected.
- One single anomalous
HTTP 500from a webhook was observed once, plausibly a race against this reconnect loop, but not reproduced as a pattern.
Source-level observation
Reading the compiled JS shipped in the official Docker image (sourcemap paths point to @n8n/db):
DbConnectionMonitor(db-connection-monitor.js) wrapsdriver.obtainMasterConnectionfor the entire Postgres driver — both its own health-check ping and all regular queries go through this wrapper.database.config.jsconfirmsPostgresConfig.databaseis sourced only fromDB_POSTGRESDB_DATABASE; there is no separate database configuration for a "monitor" or "wait tracker" connection — everything is expected to share one config.- Our working theory:
DbConnectionMonitor's recovery mechanism (recoverDataSource(), which doesdataSource.destroy()+dataSource.initialize()after repeated ping failures) re-creates a connection where thedatabasefield does not carry over correctly, causingpgto fall back to the username. We have not pinned this down to an exact line — we don't have a way to reproduce the initial trigger (whatever caused the very first ping failure 18+ days ago), only the steady-state loop that follows.
Possibly related upstream issues (none match exactly)
We searched the DbConnectionMonitor recovery subsystem specifically and found a cluster of recent, actively-patched bugs in the same area, though none describe this exact symptom (reconnect using username-as-database):
- #32878 — recovery can hang indefinitely against an unreachable backend
- #34113 (fixes #34115) — ping loop can die silently during recovery
- #35752 — same ping-loop issue, more recent fix
- #36013 — pool teardown can hang and block recovery permanently
None of these have a milestone/version tag via the API, so it's unclear whether any land in 2.40.5 — the symptom is unchanged on that version regardless.
Log excerpt (sanitized)
<DB_USER> below stands in for our actual DB_POSTGRESDB_USER value (a plain alphanumeric string, redacted here — not a placeholder n8n itself would produce):
2026-09-23 16:38:31.579 UTC [1] LOG: database system is ready to accept connections
2026-09-23 16:38:31.950 UTC [223] FATAL: database "<DB_USER>" does not exist
2026-09-23 16:38:42.017 UTC [238] FATAL: database "<DB_USER>" does not exist
2026-09-23 16:38:52.246 UTC [246] FATAL: database "<DB_USER>" does not exist
2026-09-23 16:39:02.309 UTC [254] FATAL: database "<DB_USER>" does not exist
2026-09-23 16:39:12.372 UTC [266] FATAL: database "<DB_USER>" does not exist(pattern continues unchanged, ~10s apart, indefinitely — over 152,000 occurrences accumulated over ~18 days before the fix/restart attempts described above)
What would help us narrow this down further
- Any pointer to where in
DbConnectionMonitor/recoverDataSource()the reconnect options are assembled, so we can confirm whetherdatabaseis dropped there. - Confirmation of whether #34113/#35752/#36013 change the reconnect options, or only the retry/teardown timing — our read of the diffs suggests the latter, which would explain why upgrading didn't help.
Source: n8n-io/n8n