You can absolutely Run Databases in WSL2 (Postgres/MySQL/Mongo) Without Performance Regret. WSL2 puts a real Linux kernel on your Windows machine, but without the right configuration, databases can feel slow, flaky, or hard to access from Windows apps. This guide shows you how to install and tune PostgreSQL, MySQL/MariaDB, and MongoDB in WSL2, place data in the right filesystem, expose services cleanly to Windows, and avoid the common performance traps. You’ll walk away with a dev setup that’s fast, stable, and easy to use—without needing a separate VM or dual-boot.
By the end, you’ll know:
- Where to store database files for maximum performance
- How to configure WSL2 resources with a smart .wslconfig
- How to enable systemd to run services reliably
- How to expose ports to Windows, use Docker correctly, and fix DNS/file I/O bottlenecks
- Concrete, copy-paste steps to run Postgres, MySQL/MariaDB, and MongoDB optimally
Overview
WSL2 runs Linux in a lightweight VM and stores each distro’s filesystem inside a virtual disk called an ext4.vhdx. The biggest performance gotcha is crossing the Windows/Linux filesystem boundary: File I/O between /mnt/c (Windows NTFS) and the Linux ext4 filesystem inside WSL2 is much slower than native. Databases write many small files, so placing their data directories on the wrong side of that boundary can tank performance.
Typical scenarios:
- Running PostgreSQL with data on /mnt/c leads to high latency and slow queries.
- Docker bind mounts from C:\ into containers cause slow I/O.
- Windows apps can’t connect to WSL services because of networking misconfig.
- WSL uses too much or too little memory, starving your DB or the OS.
The fix is straightforward: Keep database files on the Linux side (inside the distro), tune WSL2’s resources and networking, and only cross the boundary when necessary (e.g., IDE on Windows editing project source). This guide shows exactly how.
Quick Reference Table
| Command | Purpose | Example Output |
|---|---|---|
| wsl –status | Show WSL version, kernel, and settings | Default Version: 2; Kernel version: 5.15.133; WSL version: 2.2.5 |
| wsl –version | Show detailed WSL info (Store version) | WSL version: 2.2.5.0; Kernel: 5.15.133.1 |
| wsl -l -v | List installed distros and versions | NAME: Ubuntu; STATE: Running; VERSION: 2 |
| wsl –shutdown | Stop all WSL VMs (apply config changes) | (no output) |
| notepad %UserProfile%.wslconfig | Edit global WSL config (Windows) | Opens .wslconfig in Notepad |
| cat /etc/wsl.conf | Show per-distro WSL settings | [boot] systemd=true |
| systemctl status postgresql | Check service status | active (running) |
| psql -h localhost -U postgres | Test Postgres from WSL or Windows | psql (16.3) Type “help” for help. |
| mysql -h 127.0.0.1 -u root -p | Test MySQL/MariaDB connectivity | Welcome to the MySQL monitor… |
| mongosh “mongodb://127.0.0.1:27017” | Test MongoDB connectivity | Current Mongosh Log ID… |
| netsh interface portproxy show v4tov4 | View Windows port proxies | Listen on IPv4:Port 5432… |
| Optimize-VHD -Path “C:\Users\You\AppData\Local\Packages…\ext4.vhdx” -Mode Full | Reclaim space in ext4.vhdx (PowerShell, Hyper-V needed) | (progress output) |
Key Concepts & Prerequisites
-
Windows and WSL requirements
- Windows 11 (recommended) or Windows 10 Version 2004+.
- Latest WSL from Microsoft Store recommended for features like systemd and mirrored networking.
- Check: wsl –version and wsl –status.
-
Virtualization support
- BIOS/UEFI virtualization enabled (Intel VT-x/AMD-V).
- Windows features: “Virtual Machine Platform” enabled. “Windows Subsystem for Linux” installed.
-
Administrative rights
- Needed to install features, edit .wslconfig, and manage services/ports.
-
Linux distro
- Ubuntu 22.04 LTS or similar recommended. Install from Microsoft Store.
-
Tools
- Package manager (apt), editors (nano/vim), curl.
- Optional: Docker Desktop with WSL integration, or Docker Engine inside WSL.
-
Hardware guidance (dev workstation)
- 16 GB RAM or more recommended if you run multiple databases/containers.
- SSD strongly recommended.
Step-by-Step Guide
- Update WSL and your distro
- Update WSL (Windows PowerShell):
wsl –update
wsl –version - Reboot if prompted.
- Update your Linux distro packages (inside WSL):
sudo apt update && sudo apt -y upgrade
Explanation: Up-to-date WSL delivers better networking (localhost mirroring), systemd support, and bug fixes.
- Configure WSL resources and networking with .wslconfig (Windows)
-
Edit/create %UserProfile%.wslconfig on Windows:
notepad %UserProfile%.wslconfig -
Recommended baseline (tune to your machine):
[wsl2]
memory=8GB
processors=4
swap=4GB
localhostForwarding=true
networkingMode=mirrored
dnsTunneling=true
autoProxy=trueContents showOptional: keep VM warm for faster startup
vmIdleTimeout=180000
-
Apply changes:
wsl –shutdown
Explanation: This caps WSL memory to prevent Windows thrashing, enables mirrored networking so services are reachable via localhost, and provides sensible CPU/swap defaults. Increase memory if you run multiple DBs concurrently.
- Enable systemd inside the distro
-
Edit /etc/wsl.conf in your Linux distro:
sudo nano /etc/wsl.conf[boot]
systemd=true -
Restart WSL:
wsl –shutdown -
Verify:
systemctl –version
Explanation: systemd lets you manage databases as services (enable at boot, restart on failure), making WSL feel like a normal Linux server.
- Keep your database data on the Linux filesystem
-
Create data directories inside WSL (not under /mnt/c):
sudo mkdir -p /srv/postgres /srv/mysql /srv/mongo
sudo chown -R $USER:$USER /srv/postgres /srv/mysql /srv/mongo
Explanation: Storing data in the WSL filesystem (inside the distro’s ext4.vhdx) avoids the slow cross-OS file I/O that hurts database performance.
- Install and configure PostgreSQL (fast on WSL2)
-
Install:
sudo apt update
sudo apt -y install postgresql postgresql-contrib -
Verify service:
systemctl status postgresql
-
Configure for localhost access:
sudo sed -i “s/^#listen_addresses =./listen_addresses = ‘127.0.0.1’/g” /etc/postgresql//main/postgresql.conf
sudo systemctl restart postgresql -
Move data directory (optional; default is already inside WSL):
- Check current data dir:
sudo -u postgres psql -c “show data_directory;” - If needed, stop and move:
sudo systemctl stop postgresql
sudo rsync -a /var/lib/postgresql/ /srv/postgres/
sudo sed -i “s|/var/lib/postgresql|/srv/postgres|g” /etc/postgresql/*/main/postgresql.conf
sudo systemctl start postgresql
- Check current data dir:
-
Create a user/db and test from Windows:
sudo -u postgres createuser -s $USER
createdb devdb
psql -h localhost -U $USER -d devdb -c “select version();” -
Optional tuning (edit /etc/postgresql/*/main/postgresql.conf):
- shared_buffers = 25% of memory (e.g., 2GB on 8GB WSL)
- effective_cache_size = 50–75% of memory
- work_mem = 16–64MB per connection (start small)
- checkpoint_timeout = 10-15min; max_wal_size = 1–2GB
-
Enable at boot:
sudo systemctl enable postgresql
- Install and configure MySQL or MariaDB
Note: Ubuntu’s mysql-server may install MariaDB. Either works for dev.
- Install:
sudo apt -y install mysql-server - Set to listen on localhost:
sudo sed -i “s/^bind-address.*/bind-address = 127.0.0.1/” /etc/mysql/mysql.conf.d/mysqld.cnf
sudo systemctl restart mysql - Secure installation:
sudo mysql_secure_installation - Create user/db and test:
sudo mysql -e “CREATE USER ‘dev’@’localhost’ IDENTIFIED BY ‘devpass’; GRANT ALL PRIVILEGES ON . TO ‘dev’@’localhost’; FLUSH PRIVILEGES;”
mysql -h 127.0.0.1 -u dev -p -e “SELECT VERSION();” - Optional tuning (mysqld.cnf under [mysqld]):
- innodb_buffer_pool_size = 2G (on 8GB WSL; adjust)
- innodb_log_file_size = 256M
- innodb_flush_log_at_trx_commit = 2 (dev)
- max_connections = 100 (dev)
- Enable at boot:
sudo systemctl enable mysql
- Install and configure MongoDB (WiredTiger)
-
Add official repo (MongoDB 7.0 example for Ubuntu 22.04):
curl -fsSL https://pgp.mongodb.com/server-7.0.asc | sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg –dearmor
echo “deb [signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse” | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
sudo apt update
sudo apt -y install mongodb-org -
Configure localhost binding and data path:
sudo sed -i “s|^ bindIp:.| bindIp: 127.0.0.1|” /etc/mongod.conf
sudo sed -i “s|^ dbPath:.| dbPath: /srv/mongo|” /etc/mongod.conf
sudo chown -R mongodb:mongodb /srv/mongo
sudo systemctl enable –now mongod -
Optional tuning (in /etc/mongod.conf):
storage:
wiredTiger:
engineConfig:
cacheSizeGB: 2 -
Test:
mongosh “mongodb://127.0.0.1:27017” –eval “db.runCommand({ buildInfo: 1 })”
- Connecting from Windows apps and tools
With mirrored networking, Windows connects to WSL services via localhost.
- PostgreSQL: Host=localhost; Port=5432; User=youruser
- MySQL/MariaDB: Host=127.0.0.1; Port=3306; User=dev
- MongoDB: mongodb://127.0.0.1:27017
If localhost doesn’t work (older WSL), create a port proxy:
- Find WSL IP (inside WSL): ip -4 addr show eth0
- Create proxy (Windows PowerShell as Admin):
netsh interface portproxy add v4tov4 listenport=5432 listenaddress=127.0.0.1 connectaddress=connectport=5432
Repeat for 3306 and 27017 as needed.
- Optional: Run databases with Docker inside WSL2 (fast when volumes are on Linux paths)
-
Ensure Docker Desktop is installed and WSL integration is enabled for your distro. Or install Docker Engine inside WSL.
-
Use Linux-side volumes, not Windows bind mounts:
mkdir -p ~/data/postgres ~/data/mysql ~/data/mongo -
Sample docker-compose.yml (place it inside WSL, e.g., ~/projects/dbs):
services:
postgres:
image: postgres:16
environment:
POSTGRES_PASSWORD: devpass
ports:- “5432:5432”
volumes: - ~/data/postgres:/var/lib/postgresql/data
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: devpass
ports: - “3306:3306”
command: –default-authentication-plugin=mysql_native_password
volumes: - ~/data/mysql:/var/lib/mysql
mongo:
image: mongo:7
ports: - “27017:27017”
volumes: - ~/data/mongo:/data/db
- “5432:5432”
-
Start:
docker compose up -d
Explanation: Avoid bind-mounting C:\ folders into containers; keep data inside WSL for speed.
Troubleshooting
-
DNS in WSL not working (apt fails, names don’t resolve)
- Regenerate resolv.conf and pin a DNS:
sudo rm -f /etc/resolv.conf
echo -e “[network]\ngenerateResolvConf=false” | sudo tee -a /etc/wsl.conf
echo -e “nameserver 1.1.1.1\nnameserver 8.8.8.8” | sudo tee /etc/resolv.conf
sudo chattr +i /etc/resolv.conf
wsl –shutdown - If on corporate VPN, try dnsTunneling=true in .wslconfig and/or autoProxy=true.
- Regenerate resolv.conf and pin a DNS:
-
Windows can’t reach WSL services on localhost
- Ensure mirrored networking in .wslconfig and restart WSL:
networkingMode=mirrored
localhostForwarding=true
wsl –shutdown - Check service is listening:
ss -tulpen | grep -E “5432|3306|27017” - As a fallback, use netsh portproxy (see Step 8).
- Ensure mirrored networking in .wslconfig and restart WSL:
-
Disk space bloat in ext4.vhdx
- Clean package caches in WSL:
sudo apt clean && sudo journalctl –vacuum-size=100M - Compact VHD (requires Hyper-V PowerShell):
wsl –shutdown
Optimize-VHD -Path “C:\Users\You\AppData\Local\Packages\\LocalState\ext4.vhdx” -Mode Full - Alternatively, export/import:
wsl –shutdown
wsl –exportC:\backup.tar
wsl –unregister
wsl –importC:\WSL C:\backup.tar –version 2
- Clean package caches in WSL:
-
Git or build tools are slow in WSL
- Don’t keep repos under /mnt/c if you build inside WSL; store under ~ or /home.
- If you must use Windows paths, enable metadata and sensible masks:
In /etc/wsl.conf:
[automount]
options = “metadata,umask=22,fmask=11”
Then wsl –shutdown. - Git tips:
git config –global core.autocrlf input
git config –global core.fscache true
-
File I/O slowness with Docker
- Avoid Windows bind mounts to containers. Use named volumes or paths under /home.
- Exclude your WSL or Docker data directories in Windows Defender to reduce scanning impact on bind mounts:
Windows Security > Virus & threat protection > Manage settings > Exclusions.
-
Services don’t start automatically
- Ensure systemd is enabled:
cat /etc/wsl.conf should show [boot] systemd=true
systemctl enable postgresql mysql mongod - Restart WSL:
wsl –shutdown
- Ensure systemd is enabled:
-
Time drift or clock issues
- Restart WSL and restart services:
wsl –shutdown
sudo systemctl restart postgresql mysql mongod
- Restart WSL and restart services:
Optimization & Best Practices
-
Filesystem placement
- Keep database data on Linux (inside WSL’s ext4.vhdx). Never place DB data files under /mnt/c.
- Keep source code wherever your workflow benefits (Windows for IDEs; Linux if building inside WSL).
-
Memory and CPU tuning
- Use .wslconfig to cap memory and assign CPU cores to avoid thrashing.
- Size DB caches to fit within the WSL memory cap.
- Prefer moderate swap (2–4 GB). Excessive swap may harm performance.
-
Networking
- Use networkingMode=mirrored and localhostForwarding=true for simple Windows connectivity.
- Avoid binding services to 0.0.0.0 unless you need external access; 127.0.0.1 is safer for dev.
-
Docker volumes
- Place volumes in Linux paths for speed. For Windows IDE access to code, use bind mounts for code only, not for DB data.
-
Logging and journaling
- Limit excessive logs: journalctl –vacuum-time=7d or size limits.
- For dev, relaxed durability settings (e.g., PostgreSQL synchronous_commit=off for local dev) can speed up tests/builds. Don’t use in production.
-
Compaction and housekeeping
- Periodically compact ext4.vhdx if space grows.
- Clean apt caches and old kernels.
-
Security and exposure
- Keep databases bound to 127.0.0.1 unless you explicitly need network access.
- Use strong passwords even for dev; consider local-only accounts.
-
Backups and resets
- Use pg_dump/mysqldump/mongodump or docker volume backups for quick resets.
- Export/import WSL distros for snapshot-like backups.
-
Tooling integration
- For IDEs (VS Code, JetBrains), consider the WSL extension for faster file operations on Linux-side paths.
Project-Based Example: Node.js + PostgreSQL on WSL2 with Fast Local DB
Goal: Run a typical Node.js app with PostgreSQL in WSL2, fast builds, and easy Windows access.
- Prepare WSL2 for performance
- Ensure .wslconfig with 8GB memory, mirrored networking, and systemd enabled in /etc/wsl.conf.
- Restart WSL:
wsl –shutdown
- Create a project on Windows for IDE convenience
- On Windows, create C:\projects\nodeapp and open in your IDE.
- In WSL, access it at /mnt/c/projects/nodeapp. For performance-critical build steps, consider running the build in WSL but keep DB data in Linux.
- Install PostgreSQL and create a DB
- Inside WSL:
sudo apt -y install postgresql
sudo systemctl enable –now postgresql
createdb nodeapp_dev
psql -d nodeapp_dev -c “create table healthcheck(id serial primary key, ts timestamptz default now());”
- App configuration
- In your Node.js app, set DATABASE_URL:
DATABASE_URL=postgres://$USER@localhost:5432/nodeapp_dev - Test a query:
node -e “const {Client}=require(‘pg’);(async()=>{const c=new Client({connectionString:process.env.DATABASE_URL});await c.connect();const r=await c.query(‘select now()’);console.log(r.rows[0]);await c.end();})();”
- Performance notes
- DB data lives inside WSL (fast).
- Node.js source on Windows is okay for editing; for fastest builds, run npm install and builds inside WSL:
cd /mnt/c/projects/nodeapp
npm ci
npm run build - If builds feel slow due to Windows path, clone repo under ~ in WSL and use the VS Code WSL extension to edit directly on Linux.
- Optional: Docker Compose for DB only
- In WSL:
mkdir -p ~/data/postgres
Create docker-compose.yml with a postgres service and volume under ~/data/postgres.
docker compose up -d - Update app DATABASE_URL accordingly.
- Keep volume in Linux paths for performance.
Result: Fast local dev with Postgres reachable from Windows tools (localhost:5432), quick builds when executed in WSL, and durable, service-managed DB.
Conclusion
You can Run Databases in WSL2 (Postgres/MySQL/Mongo) Without Performance Regret by following three core principles: Keep data on the Linux side, give WSL the right amount of RAM/CPU via .wslconfig, and use modern WSL features (systemd, mirrored networking) so services run reliably and are reachable via localhost. Add a few sensible DB tunings and you’ll get native-like performance, simple connectivity, and a smooth workflow across Windows and Linux tools—without juggling full VMs or dual-booting.
FAQ
Should I store my database on C:\ or inside WSL’s filesystem?
Store it inside WSL’s Linux filesystem (the distro’s ext4.vhdx), not under /mnt/c. Database workloads suffer on NTFS via the WSL 9P layer. Keep data under /var/lib/… or /srv/… inside WSL.
Do I need Docker Desktop, or can I run Docker directly in WSL?
Both work. Docker Desktop with WSL integration is easiest for most. If you run Docker Engine directly in WSL, keep volumes in Linux paths and expose ports normally. Avoid Windows bind mounts for database data.
Why can’t Windows connect to my WSL database on localhost?
You may be using older networking. Enable networkingMode=mirrored and localhostForwarding=true in .wslconfig and wsl –shutdown. Alternatively, use netsh portproxy to forward ports from Windows to the WSL IP.
How much memory should I allocate to WSL2?
For a single DB, 4–8 GB is comfortable. For multiple DBs/containers, 8–16 GB. Set memory in .wslconfig and tune DB caches to fit within that cap. Avoid huge swap files in dev.
How do I shrink the ext4.vhdx when it grows?
Clean caches (apt clean; journalctl vacuum), shut down WSL (wsl –shutdown), then run Optimize-VHD -Mode Full on the ext4.vhdx (requires Hyper-V PowerShell). Export/import via wsl –export / –import also re-compacts.
You’ve got this—set it up once, and WSL2 becomes a fast, reliable home for your dev databases.
