dify - 💡(How to fix) Fix PostgreSQL bind mount causes silent data loss on Docker Desktop WSL2 due to 9p stale file handles

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

Docker Desktop on Windows accesses the WSL2 Linux filesystem via the 9p (Plan 9) network filesystem protocol. Unlike a native Linux mount, 9p file handles held by long-running processes can become stale — the kernel dentry cache and the 9p server state drift apart over time. When PostgreSQL's shared buffer pool and file descriptors age past a certain threshold, reads from the 9p-backed bind mount return empty/inconsistent data without surfacing I/O errors. The process (PostgreSQL) sees no reason to invalidate its cache or reopen files, so the condition persists until restart.

This is a known class of issue with 9p-based filesystems — they were designed for low-throughput, short-lived access patterns, not for database workloads with long-held file descriptors and mmap'd pages.

Code Example

volumes:
     - ./volumes/db/data:/var/lib/postgresql/data

---

SELECT COUNT(*) FROM apps;  -- returns 0, even though data exists on disk

---

# Before (bind mount — affected by 9p staleness)
volumes:
  - ./volumes/db/data:/var/lib/postgresql/data

# After (named volume — Docker manages the filesystem directly)
volumes:
  - dify_db_data:/var/lib/postgresql/data

# Top-level volumes section
volumes:
  dify_db_data:
RAW_BUFFERClick to expand / collapse

Self Checks

  • I have read Dify's Contributing Guide
  • I have searched to make sure this is not a discussion question
  • I have searched both open and closed issues
  • I have confirmed this bug has not been submitted before
  • I am submitting this bug report in English
  • I have not modified this template

Dify version

1.14.1 (also affects earlier versions using the same docker-compose template)

Cloud or Self Hosted

Self Hosted (Docker)

Steps to reproduce

  1. Deploy Dify via docker compose on Docker Desktop for Windows with WSL2 backend
  2. The default docker-compose.yaml mounts PostgreSQL data via bind mount:
    volumes:
      - ./volumes/db/data:/var/lib/postgresql/data
  3. Let the deployment run continuously for 2-3 days with active usage (apps, datasets, conversations created)
  4. After extended uptime, query the database:
    SELECT COUNT(*) FROM apps;  -- returns 0, even though data exists on disk
  5. Check the host filesystem — volumes/db/data/pgdata/base/16384/ still contains all data files on disk
  6. Restart the PostgreSQL container — all data reappears immediately

✔️ Expected Behavior

PostgreSQL should consistently read and return data stored in volumes/db/data/ regardless of container uptime. Bind mounts should provide reliable, persistent storage indistinguishable from native filesystem access.

❌ Actual Behavior

After ~2-3 days of uptime, PostgreSQL returns empty query results for all tables (0 apps, 0 datasets, 0 workflows, 0 tenants, 0 accounts). The Dify web UI shows an empty workspace with no apps, redirecting to /install as if it were a fresh deployment. All data files remain intact and uncorrupted on disk. A PostgreSQL restart fixes the issue immediately, confirming the data was never actually lost.

PostgreSQL logs show no errors, crashes, or recovery events during the affected period — the database believes it is operating normally.

Root Cause

Docker Desktop on Windows accesses the WSL2 Linux filesystem via the 9p (Plan 9) network filesystem protocol. Unlike a native Linux mount, 9p file handles held by long-running processes can become stale — the kernel dentry cache and the 9p server state drift apart over time. When PostgreSQL's shared buffer pool and file descriptors age past a certain threshold, reads from the 9p-backed bind mount return empty/inconsistent data without surfacing I/O errors. The process (PostgreSQL) sees no reason to invalidate its cache or reopen files, so the condition persists until restart.

This is a known class of issue with 9p-based filesystems — they were designed for low-throughput, short-lived access patterns, not for database workloads with long-held file descriptors and mmap'd pages.

Proposed Fix

Change the db_postgres volume mount in docker-compose-template.yaml from a bind mount to a Docker named volume:

# Before (bind mount — affected by 9p staleness)
volumes:
  - ./volumes/db/data:/var/lib/postgresql/data

# After (named volume — Docker manages the filesystem directly)
volumes:
  - dify_db_data:/var/lib/postgresql/data

# Top-level volumes section
volumes:
  dify_db_data:

Why this fixes it: Docker named volumes on Docker Desktop WSL2 use Docker's internal volume driver, which stores data inside the WSL2 VM's native ext4 filesystem. This bypasses the 9p translation layer entirely — PostgreSQL interacts with a real Linux filesystem, not a network filesystem proxy. File handles, fsync, and mmap all work correctly regardless of uptime.

Migration path for existing users: A one-time data copy from bind mount to named volume (via docker compose run) preserves all existing data. No downtime beyond a normal restart.

Other affected services (for consideration): The same 9p staleness risk applies to other bind-mounted stateful volumes in the template:

  • redis./volumes/redis/data
  • weaviate./volumes/weaviate
  • plugin_daemon./volumes/plugin_daemon

These may experience similar issues and could be migrated in a follow-up.

Additional Context

Reproduced with Docker Desktop v5.1.1 on Windows 11 + WSL2 (Ubuntu), PostgreSQL 15-alpine. After migrating to a named volume, the problem has not recurred.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

dify - 💡(How to fix) Fix PostgreSQL bind mount causes silent data loss on Docker Desktop WSL2 due to 9p stale file handles