Skip to main content

Solving WSL Database Permission Errors with Configuration Profiles

· 7 min read
thresh Team
thresh Development Team

Problem: PostgreSQL, MySQL, and Redis fail to start in WSL with cryptic permission errors
Solution: thresh WSL configuration profiles (specifically the database profile)

If you've ever tried running a database in WSL and encountered errors like:

FATAL: data directory "/var/lib/postgresql/data" has wrong ownership
chmod: changing permissions of '/var/lib/mysql': Operation not permitted
Error: setuid failed for redis user

You've hit the Plan9 filesystem permission problem. Let's fix it permanently.

The Problem: Plan9 Filesystem​

WSL uses the Plan9 filesystem protocol to mount Windows drives (like /mnt/c). While this enables seamless file sharing between Windows and Linux, it has a critical limitation:

Plan9 doesn't support chmod operations.

This breaks database servers that require precise file permissions:

  • PostgreSQL needs 0700 permissions on data directories
  • MySQL requires specific ownership for /var/lib/mysql
  • Redis needs 0755 permissions and specific user ownership

When these databases try to set permissions, they fail silently or with cryptic errors.

Traditional "Solutions" (That Don't Work)​

❌ chmod Commands​

chmod 700 /var/lib/postgresql/data
# Works on standard Linux, fails silently on Plan9

❌ Moving Data Outside /mnt/c​

# Still uses Plan9 if Windows interop is enabled
# Performance issues persist

❌ Disabling Metadata​

# /etc/wsl.conf
[automount]
options = "metadata"
# Doesn't solve the core issue

The Real Solution: Database WSL Profile​

thresh provides WSL configuration profiles that solve this at the system level.

What The Database Profile Does​

The database profile completely disables Windows interop and automount:

# Automatically configured by thresh
[boot]
systemd=true

[interop]
enabled=false # No Windows .exe access
appendWindowsPath=false # No Windows PATH pollution

[automount]
enabled=false # No /mnt/c, no /mnt/d

[network]
generateResolvConf=true # Maintain DNS resolution

Result: Pure Linux filesystem behavior, no Plan9 anywhere. Databases work perfectly.

Using WSL Configuration Profiles​

Basic Usage​

Just add one property to your blueprint:

{
"name": "postgres-dev",
"base": "ubuntu-22.04",
"wslConfig": "database",
"packages": ["postgresql"]
}

That's it! No manual configuration, no complex setup.

Complete Production Example​

{
"name": "postgres-production",
"description": "Production PostgreSQL with persistent storage",
"base": "ubuntu-22.04",
"ports": ["5432:5432"],
"volumes": [
{
"name": "pgdata",
"mountPath": "/var/lib/postgresql/data"
},
{
"name": "backups",
"mountPath": "/backups"
}
],
"wslConfig": "database",
"packages": [
"postgresql",
"postgresql-contrib"
],
"postInstall": "systemctl enable postgresql"
}
thresh up postgres-production

# PostgreSQL starts without any permission errors!
wsl -d thresh-postgres-production -- psql -U postgres

How It Works​

1. Profile Selection​

thresh includes 6 built-in profiles:

thresh wslconf list
Available WSL Configuration Profiles:

1. systemd (88 bytes)
Basic systemd enablement

2. docker (188 bytes)
Docker daemon auto-start

3. database (355 bytes)
PostgreSQL, MySQL, MongoDB, Redis optimization

4. web-server (250 bytes)
Nginx/Apache auto-start

5. minimal (238 bytes)
Disabled interop for isolation

6. development (350 bytes)
Full development features

2. Automatic Configuration​

During thresh up:

  1. Loads the specified profile
  2. Validates configuration syntax
  3. Writes to /etc/wsl.conf
  4. Automatically restarts the distro
  5. Settings applied on next boot

3. Verification​

Check what's configured:

wsl -d thresh-postgres-production cat /etc/wsl.conf
# Database Profile
# Optimized for database servers (PostgreSQL, MySQL, MongoDB, Redis)
# Disables Windows interop and drive mounting for performance
# Uses native Linux filesystem only

[boot]
systemd=true

[interop]
enabled=false
appendWindowsPath=false

[automount]
enabled=false

[network]
generateHosts=true
generateResolvConf=true

Database-Specific Examples​

PostgreSQL​

{
"name": "postgres-14",
"base": "ubuntu-22.04",
"wslConfig": "database",
"packages": ["postgresql-14"],
"postInstall": [
"systemctl enable postgresql",
"sudo -u postgres psql -c \"ALTER USER postgres PASSWORD 'dev123';\""
]
}

MySQL​

{
"name": "mysql-8",
"base": "ubuntu-22.04",
"wslConfig": "database",
"packages": ["mysql-server"],
"postInstall": "systemctl enable mysql"
}

Redis​

{
"name": "redis-persistent",
"base": "ubuntu-22.04",
"wslConfig": "database",
"volumes": [
{
"name": "redis-data",
"mountPath": "/data"
}
],
"packages": ["redis-server"],
"postInstall": [
"sed -i 's/^dir .*/dir \\/data/' /etc/redis/redis.conf",
"systemctl enable redis"
]
}

MongoDB​

{
"name": "mongodb-replica",
"base": "ubuntu-22.04",
"wslConfig": "database",
"volumes": [
{
"name": "mongo-data",
"mountPath": "/data/db"
}
],
"packages": ["mongodb"],
"postInstall": "systemctl enable mongodb"
}

Other WSL Profiles​

Docker Profile​

Auto-starts Docker daemon:

{
"name": "docker-dev",
"base": "ubuntu-22.04",
"wslConfig": "docker",
"packages": ["docker.io", "docker-compose"]
}

Web Server Profile​

Auto-starts Nginx:

{
"name": "nginx-server",
"base": "ubuntu-22.04",
"wslConfig": "web-server",
"ports": ["8080:80"],
"packages": ["nginx"]
}

Minimal Profile​

Maximum isolation (no Windows access):

{
"name": "isolated-build",
"base": "alpine-3.19",
"wslConfig": "minimal",
"packages": ["build-base", "git"]
}

Custom WSL Configuration​

From File​

{
"name": "custom-env",
"wslConfigFile": "C:\\config\\custom-wsl.conf",
"base": "ubuntu-22.04"
}

Inline Configuration​

{
"name": "custom-env",
"wslConfigCustom": "[boot]\\nsystemd=true\\n[user]\\ndefault=developer",
"base": "ubuntu-22.04"
}

Priority Order​

thresh applies configurations in this order:

  1. Custom inline (highest priority)
  2. Custom file
  3. Built-in profile (lowest priority)

CLI Commands​

List Profiles​

thresh wslconf list

Show Profile Content​

thresh wslconf show database
# Database Profile
# Optimized for database servers (PostgreSQL, MySQL, MongoDB, Redis)
...

Validate Configuration​

thresh wslconf validate myconfig.conf

Get Configuration Options​

thresh wslconf options

Lists all valid WSL configuration sections and keys.

Troubleshooting​

Profile Not Applied​

Issue: Changes not taking effect

# 1. Verify wsl.conf exists
wsl -d thresh-postgres-dev cat /etc/wsl.conf

# 2. Manually restart distro
thresh stop postgres-dev
thresh start postgres-dev

# 3. Check systemd status
wsl -d thresh-postgres-dev systemctl status

Database Still Has Permission Errors​

Issue: Errors persist after applying database profile

# 1. Verify interop is disabled
wsl -d thresh-postgres-dev which notepad.exe
# Should return empty (no Windows access)

# 2. Check automount is disabled
wsl -d thresh-postgres-dev ls /mnt
# Should be empty or show error

# 3. Recreate environment
thresh destroy postgres-dev -y
thresh up postgres-dev

Need Windows Access with Database​

Issue: Need both database optimization and Windows tools

Solution: Use separate environments:

# Database environment (isolated)
thresh up postgres-production # wslConfig: database

# Development environment (Windows access)
thresh up ubuntu-dev # wslConfig: development

Access database from dev environment via network:

# From ubuntu-dev environment
psql -h 172.24.123.45 -p 5432 -U postgres

Performance Benefits​

Beyond fixing permissions, the database profile improves performance:

Without Database Profile​

  • ✗ Plan9 filesystem overhead
  • ✗ Windows PATH scanning
  • ✗ Cross-filesystem operations
  • ✗ Interop overhead

With Database Profile​

  • ✅ Native Linux filesystem (ext4)
  • ✅ No PATH pollution
  • ✅ Direct filesystem access
  • ✅ Minimal overhead

Result: 2-3x faster database operations in some workloads.

Best Practices​

✅ Always Use Database Profile​

For any database server:

"wslConfig": "database"

✅ Combine with Persistent Volumes​

{
"wslConfig": "database",
"volumes": [
{"name": "pgdata", "mountPath": "/var/lib/postgresql/data"}
]
}

✅ Separate Environments by Purpose​

Don't mix database servers with development tools:

thresh-postgres-prod   → wslConfig: database
thresh-dev-workspace → wslConfig: development

❌ Don't Use for Desktop Applications​

Database profile disables Windows access:

// ❌ Bad: Need Windows GUI apps
{
"name": "vscode-server",
"wslConfig": "database" // Can't launch Windows apps!
}

// ✅ Good: Use development or systemd
{
"name": "vscode-server",
"wslConfig": "development"
}

Learn More​

Summary​

WSL database permission errors are caused by the Plan9 filesystem protocol. The solution:

  1. Use thresh WSL configuration profiles
  2. Apply the database profile to any database environment
  3. Enjoy native Linux filesystem behavior
  4. No more permission errors, ever
{
"wslConfig": "database"
}

That's it. One line. Done. 🚀


Try it now:

# Download thresh
irm https://thresh.sh/install.ps1 | iex

# Create PostgreSQL environment
thresh blueprint generate "PostgreSQL 14 with persistent storage"

# Edit blueprint to add:
"wslConfig": "database"

# Provision
thresh up postgres-14

# No permission errors! 🎉