Fixing Planka WebSocket Issues with Tailscale
Fixing Planka WebSocket Issues with Tailscale Integration
Fixing Planka WebSocket Issues with Tailscale Integration
This writeup documents the complete solution for resolving WebSocket connection failures in Planka when accessed through Tailscale, including CORS configuration and proper proxy setup.
Problem
Planka was experiencing WebSocket connection failures with errors like:
WebSocket connection to 'wss://TAILSCALE_URL/socket.io/?__sails_io_sdk_version=1.2.1&__sails_io_sdk_platform=node&__sails_io_sdk_language=javascript&EIO=3&transport=websocket' failed
The application would load but remain stuck in a loading state due to failed WebSocket connections when accessed through Tailscale.
Root Cause
The issue was caused by:
Incorrect BASE_URL configuration - pointing to wrong port
CORS restrictions - Sails.js only allowing localhost origins
Missing proxy trust configuration for Tailscale
Solution Overview
The fix involves configuring Planka's CORS settings to accept connections from any origin and setting up proper proxy trust for Tailscale, along with ensuring the BASE_URL matches the Tailscale endpoint.
Step-by-Step Solution
1. Configure Docker Compose Environment Variables
Edit /opt/planka/docker-compose.yml
and ensure the environment section includes:
environment:
- BASE_URL=https://TAILSCALE_URL
- DATABASE_URL=postgresql://postgres@postgres/planka
- SECRET_KEY=LONG_SECRET_KEY
- TRUST_PROXY=true
- SAILS_SECURITY_CORS_ALLOW_ORIGINS=*
- SAILS_SECURITY_CORS_ALLOW_ANY_ORIGIN_WITH_CREDENTIALS_UNSAFE=true
Key changes:
BASE_URL
must match your Tailscale hostnameTRUST_PROXY=true
enables proxy supportSAILS_SECURITY_CORS_ALLOW_ORIGINS=*
allows all originsSAILS_SECURITY_CORS_ALLOW_ANY_ORIGIN_WITH_CREDENTIALS_UNSAFE=true
enables credentials with wildcard origin
2. Apply Configuration Changes
# Update BASE_URL to match Tailscale URL
sudo sed -i '' 's|BASE_URL=http://localhost:3000|BASE_URL=https://TAILSCALE_URL|g' /opt/planka/docker-compose.yml
# Enable proxy trust
sudo sed -i '' 's|# - TRUST_PROXY=true|- TRUST_PROXY=true|g' /opt/planka/docker-compose.yml
# Add CORS configuration
sudo sed -i '' '/TRUST_PROXY=true/a\
- SAILS_SECURITY_CORS_ALLOW_ORIGINS=*\
- SAILS_SECURITY_CORS_ALLOW_ANY_ORIGIN_WITH_CREDENTIALS_UNSAFE=true' /opt/planka/docker-compose.yml
3. Configure Tailscale Serve
Set up Tailscale to serve Planka on HTTPS in background:
# Configure Tailscale serve to proxy to local Planka instance
nohup tailscale serve --https=443 http://localhost:3900 >/dev/null 2>&1 & disown
# Alternative: Use background flag (if supported)
tailscale serve --bg --https=443 http://localhost:3900
4. Restart Planka
Apply the configuration changes by restarting the containers:
cd /opt/planka
docker compose down
docker compose up -d
5. Verify Setup
Check that everything is running correctly:
# Check container status
docker ps | grep planka
# Verify local connectivity
curl -I http://localhost:3900
# Check Tailscale serve status
tailscale serve status
# Verify port binding
lsof -i :3900
Final Configuration
Docker Compose Environment Variables
environment:
- BASE_URL=https://TAILSCALE_URL
- DATABASE_URL=postgresql://postgres@postgres/planka
- SECRET_KEY=LONG_SECRET_KEY
- TRUST_PROXY=true
- SAILS_SECURITY_CORS_ALLOW_ORIGINS=*
- SAILS_SECURITY_CORS_ALLOW_ANY_ORIGIN_WITH_CREDENTIALS_UNSAFE=true
Tailscale Configuration
https://TAILSCALE_URL (tailnet only)
|-- / proxy http://localhost:3900
Port Configuration
Container: 0.0.0.0:3900->1337/tcp
Local Access: http://localhost:3900
Remote Access: https://TAILSCALE_URL
Access URLs
Local Access
URL:
http://localhost:3900
Use case: Development and local testing
Remote Access (Tailscale)
URL:
https://TAILSCALE_URL
Requirements:
Tailscale installed on client machine
Connected to same tailnet
HTTPS enabled automatically
Key Technical Details
CORS Configuration
The critical fix was enabling CORS for all origins in Sails.js:
// Equivalent to setting in config/security.js:
cors: {
allRoutes: true,
allowOrigins: '*',
allowCredentials: true,
allowAnyOriginWithCredentialsUnsafe: true,
}
WebSocket Support
Sails.js requires specific CORS configuration to allow WebSocket connections from external domains. The environment variables SAILS_SECURITY_CORS_ALLOW_ORIGINS=*
and SAILS_SECURITY_CORS_ALLOW_ANY_ORIGIN_WITH_CREDENTIALS_UNSAFE=true
override the default localhost-only restriction.
Tailscale Proxy
Tailscale serve acts as an HTTPS proxy, forwarding requests from the public Tailscale URL to the local Planka instance. The TRUST_PROXY=true
setting tells Planka to trust the proxy headers from Tailscale.
Security Considerations
⚠️ Important: The configuration allowOrigins: '*'
and allowAnyOriginWithCredentialsUnsafe: true
allows connections from any domain. This is acceptable for Tailscale since:
Tailscale provides network-level security through its VPN
Access is restricted to your tailnet members
The service is not exposed to the public internet
For production deployments on public networks, consider restricting allowOrigins
to specific trusted domains.
Troubleshooting
Common Issues
WebSocket still failing after configuration:
# Restart Planka to ensure configuration is loaded
docker restart planka-planka-1
# Check that BASE_URL matches access URL
docker exec planka-planka-1 printenv BASE_URL
Tailscale serve not working:
# Kill existing serve processes
killall tailscale
# Restart serve with background flag
tailscale serve --bg --https=443 http://localhost:3900
# Verify status
tailscale serve status
Container unhealthy:
# Check detailed container status
docker inspect planka-planka-1 | grep -A 10 Health
# View detailed logs
docker logs planka-planka-1
Verification Commands
Use these commands to verify the complete setup:
# Container status
docker ps | grep planka
# Local connectivity
curl -s http://localhost:3900 | head -5
# Tailscale status
tailscale serve status
# Port verification
lsof -i :3900
# Environment check
docker exec planka-planka-1 printenv | grep -E "(BASE_URL|TRUST_PROXY|SAILS_SECURITY)"
Alternative Solutions
If the above solution doesn't work, consider these alternatives:
1. File-based Configuration Override
Create custom config/sockets.js
and config/security.js
files and mount them as volumes (requires Docker file sharing configuration on macOS).
2. Environment Variable Method
Use Sails.js environment variable overrides for more granular control:
- SAILS_SOCKETS_ONLYALLOW_ORIGINS=false
- SAILS_SECURITY_CORS_ALL_ROUTES=true
3. Reverse Proxy Headers
If using nginx or another reverse proxy, add WebSocket upgrade headers:
Upgrade: $http_upgrade
Connection: $connection_upgrade
Summary
The solution successfully resolves Planka WebSocket issues by:
Configuring proper CORS settings via environment variables
Setting correct BASE_URL to match Tailscale endpoint
Enabling proxy trust for Tailscale forwarding
Running Tailscale serve in background for persistent access
Final Result:
✅ Local access:
http://localhost:3900
✅ Remote access:
https://TAILSCALE_URL
✅ WebSocket connections working correctly
✅ Full functionality available from any device in tailnet
Environment: macOS + Docker + Tailscale Planka Version: 2.0.0-rc.3
Last updated