Documentation Index
Fetch the complete documentation index at: https://mintlify.com/sockudo/sockudo/llms.txt
Use this file to discover all available pages before exploring further.
Sockudo is designed as a drop-in replacement for Pusher, making migration straightforward. This guide walks you through the migration process.
Why Migrate to Sockudo?
- Cost Savings: Self-hosted solution eliminates per-connection/per-message fees
- Data Sovereignty: Keep all WebSocket traffic within your infrastructure
- Performance: Optimized Rust implementation with lower latency
- Scalability: Handle 100K+ concurrent connections per node
- Advanced Features: Delta compression, tag filtering, Unix socket support
Migration Steps
Deploy Sockudo
First, deploy Sockudo alongside your existing Pusher setup. This allows gradual migration without downtime.Docker Deployment (Recommended)
git clone https://github.com/sockudo/sockudo.git
cd sockudo
# Configure environment
cp .env.example .env
# Edit .env with your app credentials
# Start Sockudo
docker-compose up -d
From Source
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Build and run
git clone https://github.com/sockudo/sockudo.git
cd sockudo
cargo run --release --features full
Verify Sockudo is running:curl http://localhost:6001/up/app-id
# Should return: {"status":"ok"}
Configure Client Connections
Update your client-side code to point to Sockudo. You can do this gradually by feature flag or environment variable.JavaScript/TypeScript
Before (Pusher):const pusher = new Pusher('your-pusher-key', {
cluster: 'us2'
});
After (Sockudo):const pusher = new Pusher('your-app-key', {
wsHost: 'sockudo.yourdomain.com',
wsPort: 443,
cluster: '',
forceTLS: true
});
Environment-Based Configuration
const pusherConfig = process.env.USE_SOCKUDO === 'true' ? {
wsHost: process.env.SOCKUDO_HOST,
wsPort: parseInt(process.env.SOCKUDO_PORT),
cluster: '',
forceTLS: process.env.SOCKUDO_TLS === 'true'
} : {
cluster: process.env.PUSHER_CLUSTER
};
const pusher = new Pusher(process.env.PUSHER_KEY, pusherConfig);
React/Vue/Angular
// config/pusher.js
export const pusherConfig = {
key: import.meta.env.VITE_PUSHER_KEY,
wsHost: import.meta.env.VITE_SOCKUDO_HOST || undefined,
wsPort: import.meta.env.VITE_SOCKUDO_PORT || undefined,
cluster: import.meta.env.VITE_PUSHER_CLUSTER || '',
forceTLS: import.meta.env.VITE_SOCKUDO_TLS === 'true'
};
Update Server-Side Events
Update your backend to trigger events through Sockudo instead of Pusher.PHP
Before:$pusher = new Pusher\Pusher(
'pusher-key',
'pusher-secret',
'pusher-app-id',
['cluster' => 'us2']
);
After:$pusher = new Pusher\Pusher(
'sockudo-key',
'sockudo-secret',
'sockudo-app-id',
[
'host' => 'sockudo.yourdomain.com',
'port' => 443,
'scheme' => 'https',
'useTLS' => true
]
);
Python
Before:pusher_client = pusher.Pusher(
app_id='pusher-app-id',
key='pusher-key',
secret='pusher-secret',
cluster='us2'
)
After:pusher_client = pusher.Pusher(
app_id='sockudo-app-id',
key='sockudo-key',
secret='sockudo-secret',
host='sockudo.yourdomain.com',
port=443,
ssl=True
)
Node.js
Before:const pusher = new Pusher({
appId: process.env.PUSHER_APP_ID,
key: process.env.PUSHER_KEY,
secret: process.env.PUSHER_SECRET,
cluster: 'us2'
});
After:const pusher = new Pusher({
appId: process.env.SOCKUDO_APP_ID,
key: process.env.SOCKUDO_KEY,
secret: process.env.SOCKUDO_SECRET,
host: process.env.SOCKUDO_HOST,
port: 443,
useTLS: true
});
Test the Migration
Thoroughly test your WebSocket functionality before switching production traffic.Connection Testing
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
wsPort: 6001,
cluster: '',
forceTLS: false
});
pusher.connection.bind('connected', () => {
console.log('✓ Connected to Sockudo');
});
pusher.connection.bind('error', (err) => {
console.error('✗ Connection error:', err);
});
Channel Subscription
// Public channel
const publicChannel = pusher.subscribe('test-channel');
publicChannel.bind('test-event', (data) => {
console.log('✓ Public channel working:', data);
});
// Private channel
const privateChannel = pusher.subscribe('private-test');
privateChannel.bind('pusher:subscription_succeeded', () => {
console.log('✓ Private channel authenticated');
});
// Presence channel
const presenceChannel = pusher.subscribe('presence-test');
presenceChannel.bind('pusher:subscription_succeeded', (members) => {
console.log('✓ Presence channel working, members:', members.count);
});
Event Triggering
Test server-side event triggering:curl -X POST http://localhost:6001/apps/app-id/events \
-H "Content-Type: application/json" \
-d '{
"name": "test-event",
"channel": "test-channel",
"data": "{\"message\":\"Hello from Sockudo\"}"
}'
Feature Compatibility
Fully Compatible Features
- ✅ Public channels
- ✅ Private channels with HMAC authentication
- ✅ Presence channels with member info
- ✅ Client events on private channels
- ✅ Channel existence queries
- ✅ User authentication
- ✅ Webhooks (channel occupied, vacated, member added/removed)
- ✅ Connection state events
- ✅ Rate limiting
Sockudo Extensions
- 🚀 Delta compression for bandwidth optimization
- 🚀 Tag-based publication filtering
- 🚀 Unix socket support for reverse proxies
- 🚀 Redis Sentinel support
- 🚀 Prometheus metrics
- 🚀 Advanced WebSocket buffer controls
Not Supported
- ❌ Pusher Beams (mobile push notifications)
- ❌ Pusher Channels encrypted private channels (use private-encrypted-* with own encryption)
Configuration Mapping
App Credentials
| Pusher | Sockudo |
|---|
| App ID | SOCKUDO_DEFAULT_APP_ID or config file |
| App Key | SOCKUDO_DEFAULT_APP_KEY or config file |
| App Secret | SOCKUDO_DEFAULT_APP_SECRET or config file |
| Cluster | Not needed (self-hosted) |
Environment Variables
# Sockudo equivalent of Pusher settings
SOCKUDO_DEFAULT_APP_ID=your-app-id
SOCKUDO_DEFAULT_APP_KEY=your-app-key
SOCKUDO_DEFAULT_APP_SECRET=your-app-secret
# Server settings
PORT=6001
HOST=0.0.0.0
# For production
SSL_ENABLED=true
SSL_CERT_PATH=/path/to/cert.pem
SSL_KEY_PATH=/path/to/key.pem
Gradual Migration Strategy
1. Canary Deployment
Route a small percentage of traffic to Sockudo:
function getPusherConfig() {
const userId = getCurrentUserId();
const useSockudo = (userId % 100) < 10; // 10% of users
if (useSockudo) {
return {
wsHost: 'sockudo.yourdomain.com',
wsPort: 443,
cluster: '',
forceTLS: true
};
} else {
return {
cluster: 'us2'
};
}
}
2. Feature Flag
Use a feature flag service (LaunchDarkly, Unleash, etc.):
import { useFeatureFlag } from '@your-org/feature-flags';
function WebSocketProvider({ children }) {
const useSockudo = useFeatureFlag('use-sockudo');
const pusherConfig = useSockudo ? {
wsHost: process.env.SOCKUDO_HOST,
wsPort: 443,
cluster: '',
forceTLS: true
} : {
cluster: process.env.PUSHER_CLUSTER
};
const pusher = new Pusher(process.env.PUSHER_KEY, pusherConfig);
return <PusherContext.Provider value={pusher}>{children}</PusherContext.Provider>;
}
3. Environment-Based
Migrate environments one at a time:
const environments = {
development: {
wsHost: 'localhost',
wsPort: 6001,
cluster: '',
forceTLS: false
},
staging: {
wsHost: 'sockudo-staging.yourdomain.com',
wsPort: 443,
cluster: '',
forceTLS: true
},
production: {
// Still using Pusher during gradual migration
cluster: 'us2'
}
};
const pusherConfig = environments[process.env.NODE_ENV];
Monitoring the Migration
Connection Metrics
Monitor Sockudo’s Prometheus metrics:
curl http://localhost:9601/metrics
Key metrics:
sockudo_connections_total - Total active connections
sockudo_messages_received_total - Messages from clients
sockudo_messages_sent_total - Messages to clients
sockudo_subscription_successes_total - Successful subscriptions
sockudo_subscription_errors_total - Failed subscriptions
Error Tracking
Log connection errors for analysis:
pusher.connection.bind('error', (err) => {
// Send to your error tracking service
trackError('sockudo_connection_error', {
code: err.error?.data?.code,
message: err.error?.data?.message,
userId: getCurrentUserId()
});
});
Troubleshooting Common Issues
Authentication Fails After Migration
Problem: Private/presence channels fail with 401 errors.
Solution: Verify authentication endpoint generates signatures using Sockudo credentials:
// Update your auth endpoint
$pusher = new Pusher\Pusher(
getenv('SOCKUDO_KEY'), // Not PUSHER_KEY
getenv('SOCKUDO_SECRET'), // Not PUSHER_SECRET
getenv('SOCKUDO_APP_ID'), // Not PUSHER_APP_ID
['host' => getenv('SOCKUDO_HOST'), 'port' => 443, 'scheme' => 'https']
);
$auth = $pusher->socket_auth($_POST['channel_name'], $_POST['socket_id']);
return response()->json(json_decode($auth));
High Latency
Problem: Messages take longer to arrive than with Pusher.
Solution:
- Check network latency to Sockudo server
- Enable delta compression for bandwidth optimization
- Verify Redis/NATS adapter is properly configured for horizontal scaling
- Check Nginx/proxy configuration has proper WebSocket settings
Connection Drops
Problem: WebSocket connections disconnect unexpectedly.
Solution:
- Increase WebSocket timeout in Nginx:
proxy_read_timeout 300s;
- Check Sockudo buffer settings:
WEBSOCKET_MAX_MESSAGES, WEBSOCKET_MAX_BYTES
- Verify firewall/load balancer preserves long-lived connections
- Enable auto-ping:
WEBSOCKET_AUTO_PING=true, WEBSOCKET_PING_INTERVAL=30
Rollback Plan
If issues arise, you can quickly rollback to Pusher:
Client-Side
const pusherConfig = process.env.USE_SOCKUDO === 'true' ? {
wsHost: process.env.SOCKUDO_HOST,
// ... Sockudo config
} : {
cluster: process.env.PUSHER_CLUSTER // Rollback to Pusher
};
Set USE_SOCKUDO=false to rollback.
Server-Side
const pusher = new Pusher({
appId: process.env.USE_SOCKUDO === 'true'
? process.env.SOCKUDO_APP_ID
: process.env.PUSHER_APP_ID,
key: process.env.USE_SOCKUDO === 'true'
? process.env.SOCKUDO_KEY
: process.env.PUSHER_KEY,
secret: process.env.USE_SOCKUDO === 'true'
? process.env.SOCKUDO_SECRET
: process.env.PUSHER_SECRET,
...(process.env.USE_SOCKUDO === 'true' ? {
host: process.env.SOCKUDO_HOST,
port: 443,
useTLS: true
} : {
cluster: 'us2'
})
});
Next Steps