Repository navigation
troubleshooting
Comprehensive guide to diagnosing and resolving common issues with soketi.rs WebSocket server
- Connection Issues
- Authentication Problems
- Channel Subscription Errors
- Performance Issues
- Deployment Problems
- Configuration Errors
- Debug Techniques
- Monitoring and Logging
- Common Error Messages
- Getting Help
Symptom:
Error: WebSocket connection to 'ws://localhost:6001/app/...' failed
Possible Causes:
- soketi.rs server is not running
- Incorrect host or port configuration
- Firewall blocking WebSocket connections
- SSL/TLS configuration mismatch
Solutions:
1. Verify Server is Running:
# Check if soketi.rs is running
curl http://localhost:6001/health
# Check Docker container status
docker ps | grep soketi
# Check process
ps aux | grep soketi2. Verify Configuration:
// Check your Pusher client configuration
const pusher = new Pusher('app-key', {
wsHost: 'localhost', // Should match your server host
wsPort: 6001, // Should match your server port
forceTLS: false, // Set to true if using HTTPS
encrypted: false, // Set to true if using HTTPS
enabledTransports: ['ws', 'wss'],
});3. Check Firewall Rules:
# Linux - Check if port is open
sudo netstat -tulpn | grep 6001
# Allow port through firewall (Ubuntu/Debian)
sudo ufw allow 6001/tcp
# Check if port is accessible
telnet localhost 60014. SSL/TLS Mismatch:
// If your server uses HTTPS, ensure client matches
const pusher = new Pusher('app-key', {
wsHost: 'your-domain.com',
wsPort: 443,
wssPort: 443,
forceTLS: true, // Must be true for HTTPS
encrypted: true, // Must be true for HTTPS
enabledTransports: ['wss'], // Use 'wss' for secure connections
});Symptom: Client disconnects and reconnects repeatedly.
Possible Causes:
- Network instability
- Timeout settings too aggressive
- Load balancer timeout issues
- Server resource constraints
Solutions:
1. Adjust Timeout Settings:
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
wsPort: 6001,
activityTimeout: 120000, // Increase to 120 seconds
pongTimeout: 30000, // Increase to 30 seconds
});2. Configure Load Balancer Timeouts:
# Nginx configuration
location /app/ {
proxy_pass http://soketi_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Increase timeouts
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
}3. Monitor Server Resources:
# Check CPU and memory usage
top
# Check soketi.rs resource usage
docker stats soketi
# Check system logs
journalctl -u soketi -fSymptom: Connection works from server/backend but fails from browser.
Possible Causes:
- CORS configuration issues
- Mixed content (HTTP page trying to connect to WS)
- Browser security policies
Solutions:
1. Configure CORS:
# Environment variable
SOKETI_CORS_ALLOWED_ORIGINS=https://your-domain.com,https://app.your-domain.com
# Or in config.json
{
"cors": {
"credentials": true,
"origin": ["https://your-domain.com"],
"methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
}
}2. Fix Mixed Content:
// If your page is HTTPS, use WSS
const pusher = new Pusher('app-key', {
wsHost: 'your-domain.com',
forceTLS: true, // Force secure connection
encrypted: true,
});3. Check Browser Console:
// Enable Pusher debug logging
Pusher.logToConsole = true;
const pusher = new Pusher('app-key', {
// ... config
});Symptom:
Error: Unable to retrieve auth string from auth endpoint
Possible Causes:
- Auth endpoint not configured or incorrect
- Auth endpoint returning wrong format
- Session/token not valid
- CORS issues with auth endpoint
Solutions:
1. Verify Auth Endpoint Configuration:
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
wsPort: 6001,
authEndpoint: '/api/pusher/auth', // Must be correct path
auth: {
headers: {
'Authorization': `Bearer ${token}` // Include auth token
}
}
});2. Check Auth Endpoint Response:
// Correct auth endpoint response format
export async function POST(req: Request) {
const body = await req.text();
const params = new URLSearchParams(body);
const socketId = params.get('socket_id');
const channelName = params.get('channel_name');
// For private channels
const authSignature = pusher.authorizeChannel(socketId, channelName);
// Must return this exact format
return Response.json({
auth: authSignature.auth
});
}3. Test Auth Endpoint Manually:
# Test your auth endpoint
curl -X POST http://localhost:3000/api/pusher/auth \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Bearer your-token" \
-d "socket_id=123.456&channel_name=private-test"
# Expected response:
# {"auth":"app-key:signature"}4. Enable CORS for Auth Endpoint:
// app/api/pusher/auth/route.ts
export async function OPTIONS(req: Request) {
return new Response(null, {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
}Symptom: User successfully subscribes to presence channel but user info is missing.
Possible Causes:
- Auth endpoint not returning user_info
- Incorrect presence data format
Solutions:
1. Return Correct Presence Data:
// app/api/pusher/auth/route.ts
export async function POST(req: Request) {
const body = await req.text();
const params = new URLSearchParams(body);
const socketId = params.get('socket_id');
const channelName = params.get('channel_name');
if (channelName?.startsWith('presence-')) {
// Must include presenceData for presence channels
const presenceData = {
user_id: session.user.id,
user_info: {
name: session.user.name,
email: session.user.email,
avatar: session.user.avatar,
}
};
const authResponse = pusher.authorizeChannel(
socketId,
channelName,
presenceData // This is required!
);
return Response.json(authResponse);
}
}2. Verify Client-Side Handling:
const channel = pusher.subscribe('presence-chat');
channel.bind('pusher:subscription_succeeded', (members: any) => {
console.log('Members:', members.count);
members.each((member: any) => {
console.log('Member ID:', member.id);
console.log('Member Info:', member.info); // Should contain user_info
});
});Symptom:
Error: Invalid signature
Possible Causes:
- App secret mismatch between client and server
- Incorrect signature generation
- Clock skew between servers
Solutions:
1. Verify App Credentials:
# Server-side (soketi.rs)
SOKETI_DEFAULT_APP_ID=app-id
SOKETI_DEFAULT_APP_KEY=app-key
SOKETI_DEFAULT_APP_SECRET=app-secret
# Client-side
NEXT_PUBLIC_PUSHER_KEY=app-key # Must match server
# Server SDK (for auth endpoint)
PUSHER_APP_ID=app-id
PUSHER_SECRET=app-secret # Must match server2. Check Pusher Server Configuration:
// Ensure server SDK uses correct credentials
const pusher = new Pusher({
appId: process.env.PUSHER_APP_ID!,
key: process.env.NEXT_PUBLIC_PUSHER_KEY!,
secret: process.env.PUSHER_SECRET!, // Must match soketi.rs secret
host: process.env.PUSHER_HOST,
port: process.env.PUSHER_PORT,
useTLS: true,
});3. Synchronize Server Clocks:
# Install NTP
sudo apt-get install ntp
# Sync time
sudo ntpdate -s time.nist.gov
# Enable NTP service
sudo systemctl enable ntp
sudo systemctl start ntpSymptom:
Error: Subscription timeout
Possible Causes:
- Server not responding to subscription request
- Network latency too high
- Server overloaded
Solutions:
1. Increase Subscription Timeout:
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
wsPort: 6001,
activityTimeout: 120000, // 120 seconds
});2. Check Server Logs:
# Docker logs
docker logs soketi -f
# Check for subscription errors
docker logs soketi 2>&1 | grep -i "subscription"3. Monitor Server Load:
# Check if server is overloaded
docker stats soketi
# Scale up if needed
docker-compose up --scale soketi=3Symptom: Client events (client-*) are not being received by other clients.
Possible Causes:
- Client events not enabled in app configuration
- Using client events on public channels (not allowed)
- Event name doesn't start with "client-"
Solutions:
1. Enable Client Events:
// config.json
{
"app_manager": {
"driver": "array",
"array": {
"apps": [
{
"id": "app-id",
"key": "app-key",
"secret": "app-secret",
"enable_client_messages": true // Must be true
}
]
}
}
}2. Use Private or Presence Channels:
// Client events only work on private/presence channels
const channel = pusher.subscribe('private-chat'); // ✅ Works
// const channel = pusher.subscribe('public-chat'); // ❌ Won't work
channel.bind('pusher:subscription_succeeded', () => {
// Trigger client event (must start with 'client-')
channel.trigger('client-typing', { user: 'John', isTyping: true });
});
// Listen for client events
channel.bind('client-typing', (data) => {
console.log('User typing:', data);
});3. Check Event Name Format:
// ✅ Correct - starts with 'client-'
channel.trigger('client-message', data);
channel.trigger('client-typing', data);
// ❌ Wrong - doesn't start with 'client-'
channel.trigger('message', data); // Won't work as client eventSymptom:
Error: Connection limit exceeded
Possible Causes:
- Too many concurrent connections
- Connection limit set too low
- Connections not being properly closed
Solutions:
1. Increase Connection Limit:
// config.json
{
"app_manager": {
"driver": "array",
"array": {
"apps": [
{
"id": "app-id",
"key": "app-key",
"secret": "app-secret",
"max_connections": 1000 // Increase limit
}
]
}
}
}2. Properly Disconnect Clients:
// Disconnect when component unmounts
useEffect(() => {
const pusher = new Pusher('app-key', { /* config */ });
const channel = pusher.subscribe('my-channel');
return () => {
channel.unbind_all();
pusher.unsubscribe('my-channel');
pusher.disconnect(); // Important!
};
}, []);3. Monitor Active Connections:
# Check metrics endpoint
curl http://localhost:9601/metrics
# Look for connection count
# soketi_connections_totalSymptom: Messages take several seconds to arrive.
Possible Causes:
- Network latency
- Server overloaded
- Inefficient event handling
- Geographic distance
Solutions:
1. Deploy Closer to Users:
# Use CDN or deploy in multiple regions
# Choose region closest to your users2. Optimize Event Handling:
// ❌ Bad - Processing in event handler
channel.bind('message', (data) => {
// Heavy processing here blocks other events
processHeavyData(data);
});
// ✅ Good - Offload to worker
channel.bind('message', (data) => {
// Queue for background processing
queueForProcessing(data);
});3. Use Connection Pooling:
// Reuse single Pusher instance
const pusher = new Pusher('app-key', { /* config */ });
// Don't create new instance for each component
// ❌ Bad
function Component1() {
const pusher = new Pusher('app-key', { /* config */ });
}
// ✅ Good - Import shared instance
import { pusher } from '@/lib/pusher';4. Enable Compression:
# Nginx configuration
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain application/json;Symptom: soketi.rs consuming excessive memory.
Possible Causes:
- Too many concurrent connections
- Large message payloads
- Memory leak
- Insufficient garbage collection
Solutions:
1. Limit Message Size:
// config.json
{
"app_manager": {
"driver": "array",
"array": {
"apps": [
{
"max_event_payload_in_kb": 100 // Limit to 100KB
}
]
}
}
}2. Monitor Memory Usage:
# Check memory usage
docker stats soketi
# Set memory limits
docker run -m 512m soketi3. Restart Periodically:
# Use Docker restart policy
docker run --restart=unless-stopped soketi
# Or use systemd timer for periodic restarts4. Scale Horizontally:
# docker-compose.yml
version: '3.8'
services:
soketi:
image: quay.io/soketi/soketi:latest-16-alpine
deploy:
replicas: 3 # Run multiple instances
resources:
limits:
memory: 512MSymptom: Events take long time to broadcast to all subscribers.
Possible Causes:
- Single-threaded bottleneck
- No horizontal scaling
- Inefficient adapter
Solutions:
1. Use Redis Adapter:
# Enable Redis for horizontal scaling
SOKETI_ADAPTER_DRIVER=redis
SOKETI_REDIS_HOST=redis
SOKETI_REDIS_PORT=6379
SOKETI_REDIS_DB=02. Deploy Multiple Instances:
# docker-compose.yml
version: '3.8'
services:
redis:
image: redis:alpine
soketi-1:
image: quay.io/soketi/soketi:latest-16-alpine
environment:
- SOKETI_ADAPTER_DRIVER=redis
- SOKETI_REDIS_HOST=redis
soketi-2:
image: quay.io/soketi/soketi:latest-16-alpine
environment:
- SOKETI_ADAPTER_DRIVER=redis
- SOKETI_REDIS_HOST=redis
nginx:
image: nginx:alpine
ports:
- "6001:6001"
depends_on:
- soketi-1
- soketi-23. Optimize Event Payload:
// ❌ Bad - Large payload
channel.trigger('update', {
fullDocument: largeObject,
metadata: moreData,
history: evenMoreData
});
// ✅ Good - Minimal payload
channel.trigger('update', {
id: documentId,
type: 'modified'
});
// Client fetches full data if neededSymptom:
Error: Container exits immediately after starting
Possible Causes:
- Configuration error
- Port already in use
- Missing environment variables
- Invalid credentials
Solutions:
1. Check Container Logs:
# View logs
docker logs soketi
# Follow logs in real-time
docker logs -f soketi2. Check Port Availability:
# Check if port is already in use
sudo lsof -i :6001
# Kill process using the port
sudo kill -9 <PID>
# Or use different port
docker run -p 6002:6001 soketi3. Verify Environment Variables:
# Check required variables are set
docker run --rm soketi env | grep SOKETI
# Run with explicit variables
docker run \
-e SOKETI_DEFAULT_APP_ID=app-id \
-e SOKETI_DEFAULT_APP_KEY=app-key \
-e SOKETI_DEFAULT_APP_SECRET=app-secret \
-p 6001:6001 \
quay.io/soketi/soketi:latest-16-alpine4. Test Configuration:
# Test with minimal config
docker run -p 6001:6001 \
-e SOKETI_DEFAULT_APP_ID=test \
-e SOKETI_DEFAULT_APP_KEY=test \
-e SOKETI_DEFAULT_APP_SECRET=test \
quay.io/soketi/soketi:latest-16-alpineSymptom: Cannot connect through Nginx/Caddy reverse proxy.
Possible Causes:
- WebSocket upgrade headers missing
- Incorrect proxy configuration
- SSL/TLS issues
- Timeout too short
Solutions:
1. Verify Nginx Configuration:
location /app/ {
proxy_pass http://soketi:6001;
proxy_http_version 1.1;
# Required for WebSocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Standard headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
}2. Test Nginx Configuration:
# Test configuration syntax
nginx -t
# Reload configuration
nginx -s reload
# Check error logs
tail -f /var/log/nginx/error.log3. Verify Caddy Configuration:
your-domain.com {
reverse_proxy /app/* {
to soketi:6001
# WebSocket headers
header_up Host {host}
header_up Connection {>Connection}
header_up Upgrade {>Upgrade}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Proto {scheme}
}
}4. Test Reverse Proxy:
# Test WebSocket connection through proxy
wscat -c wss://your-domain.com/app/app-key
# Check proxy logs
docker logs nginx -f
docker logs caddy -fSymptom:
Error: SSL certificate problem: unable to get local issuer certificate
Possible Causes:
- Self-signed certificate
- Certificate chain incomplete
- Certificate expired
- Wrong certificate path
Solutions:
1. Use Let's Encrypt:
# With Caddy (automatic)
# Caddy handles SSL automatically
# With Certbot (Nginx)
sudo certbot --nginx -d your-domain.com2. Verify Certificate:
# Check certificate expiration
openssl x509 -in /path/to/cert.pem -noout -dates
# Check certificate chain
openssl s_client -connect your-domain.com:443 -showcerts
# Test SSL configuration
curl -vI https://your-domain.com3. Configure Certificate Path:
# soketi.rs with SSL
SOKETI_SSL_CERT=/etc/ssl/certs/cert.pem
SOKETI_SSL_KEY=/etc/ssl/private/key.pem
SOKETI_SSL_PASSPHRASE=your-passphrase # If encrypted4. Allow Self-Signed Certificates (Development Only):
// ⚠️ Only for development!
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
forceTLS: true,
// Node.js only
// process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
});Symptom:
Error: Failed to parse configuration file
Possible Causes:
- Invalid JSON syntax
- Missing required fields
- Wrong data types
Solutions:
1. Validate JSON:
# Validate JSON syntax
cat config.json | jq .
# Or use online validator
# https://jsonlint.com/2. Check Required Fields:
{
"app_manager": {
"driver": "array",
"array": {
"apps": [
{
"id": "app-id", // Required
"key": "app-key", // Required
"secret": "app-secret", // Required
"enabled": true // Required
}
]
}
}
}3. Use Environment Variables Instead:
# Simpler alternative to config file
docker run \
-e SOKETI_DEFAULT_APP_ID=app-id \
-e SOKETI_DEFAULT_APP_KEY=app-key \
-e SOKETI_DEFAULT_APP_SECRET=app-secret \
-p 6001:6001 \
quay.io/soketi/soketi:latest-16-alpineSymptom:
Error: Invalid app credentials
Possible Causes:
- Mismatched credentials between client and server
- Typo in credentials
- Using wrong app ID
Solutions:
1. Verify All Credentials Match:
# Server (soketi.rs)
SOKETI_DEFAULT_APP_ID=my-app
SOKETI_DEFAULT_APP_KEY=my-key
SOKETI_DEFAULT_APP_SECRET=my-secret
# Client
NEXT_PUBLIC_PUSHER_KEY=my-key # Must match SOKETI_DEFAULT_APP_KEY
# Server SDK (auth endpoint)
PUSHER_APP_ID=my-app # Must match SOKETI_DEFAULT_APP_ID
PUSHER_SECRET=my-secret # Must match SOKETI_DEFAULT_APP_SECRET2. Test Credentials:
# Test connection with credentials
curl -X POST http://localhost:6001/apps/my-app/events \
-H "Content-Type: application/json" \
-d '{
"name": "test-event",
"channel": "test-channel",
"data": "{\"message\": \"test\"}"
}'3. Check for Whitespace:
# Trim whitespace from credentials
export SOKETI_DEFAULT_APP_KEY=$(echo "my-key" | tr -d '[:space:]')Client-Side:
// Enable Pusher debug logging
Pusher.logToConsole = true;
const pusher = new Pusher('app-key', {
wsHost: 'localhost',
wsPort: 6001,
// ... other config
});
// You'll see detailed logs in browser console:
// Pusher : State changed : connecting -> connected
// Pusher : Event sent : {"event":"pusher:subscribe","data":{"channel":"my-channel"}}Server-Side:
# Enable debug mode
SOKETI_DEBUG=true
SOKETI_LOG_LEVEL=debug
# Or in config.json
{
"debug": true,
"log_level": "debug"
}Using Browser DevTools:
1. Open Chrome DevTools (F12)
2. Go to Network tab
3. Filter by "WS" (WebSocket)
4. Click on connection to see frames
5. View sent/received messages
Using wscat:
# Install wscat
npm install -g wscat
# Connect to soketi.rs
wscat -c ws://localhost:6001/app/app-key
# Send test message
> {"event":"pusher:subscribe","data":{"channel":"test"}}
# View responses
< {"event":"pusher:connection_established","data":"..."}Using tcpdump:
# Capture WebSocket traffic
sudo tcpdump -i any -A 'tcp port 6001'
# Save to file for analysis
sudo tcpdump -i any -w websocket.pcap 'tcp port 6001'1. Test Server to Client:
# Trigger event via HTTP API
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\"}"
}'
# Client should receive the event2. Test Client to Server:
// Subscribe to channel
const channel = pusher.subscribe('test-channel');
// Bind to event
channel.bind('test-event', (data) => {
console.log('Received:', data);
});
// Verify subscription succeeded
channel.bind('pusher:subscription_succeeded', () => {
console.log('✅ Subscribed successfully');
});
channel.bind('pusher:subscription_error', (error) => {
console.error('❌ Subscription failed:', error);
});3. Test Client Events:
// On private/presence channel
const channel = pusher.subscribe('private-test');
channel.bind('pusher:subscription_succeeded', () => {
// Trigger client event
channel.trigger('client-test', { message: 'Hello' });
});
// Listen for client event
channel.bind('client-test', (data) => {
console.log('Client event received:', data);
});// Monitor all connection state changes
pusher.connection.bind('state_change', (states) => {
console.log(`State: ${states.previous} → ${states.current}`);
});
// Possible states:
// - initialized
// - connecting
// - connected
// - unavailable
// - failed
// - disconnected
// Check current state
console.log('Current state:', pusher.connection.state);
// Get socket ID (available when connected)
console.log('Socket ID:', pusher.connection.socket_id);// Log all events on a channel
const channel = pusher.subscribe('my-channel');
// Bind to all events
channel.bind_global((eventName, data) => {
console.log(`Event: ${eventName}`, data);
});
// List all bound events
console.log('Bound events:', channel.callbacks._callbacks);soketi.rs provides a Prometheus-compatible metrics endpoint:
# Access metrics
curl http://localhost:9601/metrics
# Key metrics to monitor:
# - soketi_connections_total: Total active connections
# - soketi_messages_sent_total: Total messages sent
# - soketi_messages_received_total: Total messages received
# - soketi_channels_total: Total active channels# Check server health
curl http://localhost:6001/health
# Expected response:
# {"status":"ok"}
# Use in monitoring systems
# Kubernetes liveness probe:
livenessProbe:
httpGet:
path: /health
port: 6001
initialDelaySeconds: 10
periodSeconds: 30Configure Log Format:
# JSON logging for easier parsing
SOKETI_LOG_FORMAT=json
# Log to file
SOKETI_LOG_FILE=/var/log/soketi/soketi.logParse Logs:
# Filter error logs
cat soketi.log | jq 'select(.level == "error")'
# Count events by type
cat soketi.log | jq -r '.event' | sort | uniq -c
# Monitor in real-time
tail -f soketi.log | jq .Integrate with Monitoring Services:
1. Datadog:
# docker-compose.yml
services:
soketi:
image: quay.io/soketi/soketi:latest-16-alpine
labels:
com.datadoghq.ad.logs: '[{"source": "soketi", "service": "websocket"}]'2. Prometheus:
# prometheus.yml
scrape_configs:
- job_name: 'soketi'
static_configs:
- targets: ['soketi:9601']3. Grafana Dashboard:
# Import soketi.rs dashboard
# Use metrics from Prometheus
# Visualize connections, messages, latency4. Sentry (Error Tracking):
// In your Next.js app
import * as Sentry from '@sentry/nextjs';
pusher.connection.bind('error', (err) => {
Sentry.captureException(err);
});Meaning: Cannot connect to soketi.rs server.
Solutions:
- Verify server is running:
curl http://localhost:6001/health - Check host and port configuration
- Verify firewall rules
Meaning: Client using wrong app key.
Solutions:
- Verify
NEXT_PUBLIC_PUSHER_KEYmatchesSOKETI_DEFAULT_APP_KEY - Check for typos or whitespace
- Ensure environment variables are loaded
Meaning: Authorization failed for private/presence channel.
Solutions:
- Check auth endpoint is configured correctly
- Verify auth endpoint returns correct format
- Ensure user is authenticated
- Check CORS configuration
Meaning: Too many concurrent connections.
Solutions:
- Increase
max_connectionsin config - Scale horizontally with multiple instances
- Implement connection pooling
Meaning: Message exceeds size limit.
Solutions:
- Reduce message size
- Increase
max_event_payload_in_kbin config - Split large messages into smaller chunks
Meaning: Trying to trigger event on non-existent channel.
Solutions:
- Ensure channel is subscribed before triggering
- Check channel name spelling
- Verify channel exists in metrics
Meaning: Too many requests in short time.
Solutions:
- Implement client-side rate limiting
- Increase rate limits in config:
{ "max_backend_events_per_second": 1000, "max_client_events_per_second": 100 } - Use batching for multiple events
Meaning: Server cannot upgrade HTTP to WebSocket.
Solutions:
- Check reverse proxy WebSocket configuration
- Verify
UpgradeandConnectionheaders - Ensure HTTP/1.1 is used
- Check this troubleshooting guide for your specific issue
- Enable debug logging and review logs
- Test with minimal configuration to isolate the problem
- Verify credentials are correct and consistent
- Check server status and resource usage
When asking for help, provide:
-
soketi.rs version:
docker run quay.io/soketi/soketi:latest-16-alpine --version
-
Configuration:
# Sanitize secrets before sharing! cat config.json | jq 'del(.app_manager.array.apps[].secret)'
-
Error messages:
# Full error logs docker logs soketi 2>&1 | tail -50
-
Client configuration:
// Share your Pusher client setup (remove secrets!) const pusher = new Pusher('app-key', { wsHost: 'localhost', wsPort: 6001, // ... });
-
Environment:
- Operating system
- Docker version (if using Docker)
- Node.js version (for client)
- Browser version (if browser issue)
GitHub Issues:
- Search existing issues: soketi.rs Issues
- Create new issue with template
- Include all relevant information
Discord/Slack:
- Join community chat
- Ask questions in appropriate channels
- Help others with similar issues
Stack Overflow:
- Tag questions with
soketiandwebsocket - Search existing questions first
- Provide minimal reproducible example
For production deployments and enterprise support:
- Contact soketi.rs team
- Consider managed hosting solutions
- Hire WebSocket consultants
# Check server health
curl http://localhost:6001/health
# View metrics
curl http://localhost:9601/metrics
# Test WebSocket connection
wscat -c ws://localhost:6001/app/app-key
# Trigger test event
curl -X POST http://localhost:6001/apps/app-id/events \
-H "Content-Type: application/json" \
-d '{"name":"test","channel":"test","data":"{}"}'
# View Docker logs
docker logs soketi -f
# Check container stats
docker stats soketi
# Restart container
docker restart soketi- Server is running (
curl http://localhost:6001/health) - Credentials match between client and server
- Port is accessible (check firewall)
- WebSocket upgrade headers configured (if using proxy)
- SSL/TLS configuration matches (forceTLS, encrypted)
- Auth endpoint configured correctly (for private/presence)
- CORS configured for browser clients
- Debug logging enabled
- Metrics show expected values
- No errors in server logs
- Using Redis adapter for horizontal scaling
- Multiple soketi.rs instances deployed
- Load balancer configured with proper timeouts
- Connection pooling implemented
- Event payloads optimized (< 100KB)
- Rate limits configured appropriately
- Compression enabled
- Monitoring and alerting set up
- Resource limits configured
- Geographic distribution considered
- Getting Started Guide
- Configuration Reference
- API Reference
- Deployment Guide
- Vercel Deployment
- Netlify Deployment
- Examples
Need more help? Check the soketi.rs documentation or open an issue on GitHub.
Symptom: Server starts but no apps are loaded, or "App not found" errors.
Possible Causes:
- Config file not specified with
--config-fileparameter - Wrong parameter name used (
--configinstead of--config-file) - Config file path incorrect
Solutions:
1. Use Correct Parameter:
# ✅ Correct
cargo run -- --config-file config.json
./soketi --config-file config.json
# ❌ Wrong
cargo run -- --config config.json # Will fail
./soketi --config config.json # Will fail2. Verify Config File Path:
# Check file exists
ls -la config.json
# Use absolute path if needed
./soketi --config-file /path/to/config.json
# Check file permissions
chmod 644 config.json3. Enable Debug Mode to See Config Loading:
# Run with debug to see if apps are loaded
./soketi --config-file config.json --debug true
# Look for log line:
# DEBUG soketi_rs::server: Loading ArrayAppManager with X apps4. Verify Config File Format:
{
"app_manager": {
"driver": "Array",
"array": {
"apps": [
{
"id": "app-id",
"key": "app-key",
"secret": "app-secret",
"enabled": true
}
]
}
}
}Symptom: HTTP API accepts messages but clients don't receive them. Logs show "No sockets found in channel".
Possible Causes:
- Sockets not being added to adapter
- Channel subscription not completing
- Socket ID mismatch
Solutions:
1. Verify Socket Registration:
# Enable debug mode to see socket operations
./soketi --config-file config.json --debug true
# Look for these log lines:
# DEBUG soketi_rs::ws: Socket added to adapter: <socket_id>
# DEBUG soketi_rs::ws: Successfully subscribed to channel: <channel_name>2. Check Subscription Flow:
// Client-side - verify subscription succeeds
const channel = pusher.subscribe('my-channel');
channel.bind('pusher:subscription_succeeded', () => {
console.log('✅ Subscription successful');
});
channel.bind('pusher:subscription_error', (error) => {
console.error('❌ Subscription failed:', error);
});3. Test End-to-End:
# 1. Start server with debug
./soketi --config-file config.json --debug true
# 2. Connect client and subscribe to channel
# 3. Send test message
curl -X POST http://localhost:6001/apps/app-id/events \
-H "Content-Type: application/json" \
-d '{
"name": "test",
"channel": "my-channel",
"data": "{\"message\": \"test\"}"
}'
# 4. Check debug logs for:
# DEBUG soketi_rs::adapters::local: Found X sockets in channel: my-channel
# DEBUG soketi_rs::adapters::local: Sending to socket: <socket_id>Symptom: Presence channels authenticate successfully but private channels fail with "Invalid signature".
Possible Causes:
- Backend sending
channel_datafor private channels - Signature calculation includes wrong data
- Auth endpoint not differentiating channel types
Solutions:
1. Fix Auth Endpoint to Handle Channel Types:
// Backend auth endpoint
export async function POST(req: Request) {
const body = await req.text();
const params = new URLSearchParams(body);
const socketId = params.get('socket_id');
const channelName = params.get('channel_name');
// ✅ Correct - Different handling for different channel types
if (channelName?.startsWith('presence-')) {
// Presence channel - include user data
const authResponse = pusher.authenticate(socketId, channelName, {
user_id: userId,
user_info: { name: userName }
});
return Response.json(authResponse);
} else if (channelName?.startsWith('private-')) {
// Private channel - NO user data
const authResponse = pusher.authenticate(socketId, channelName);
return Response.json(authResponse);
}
// ❌ Wrong - Same handling for all channels
// const authResponse = pusher.authenticate(socketId, channelName, {
// user_id: userId, // Don't include for private channels!
// user_info: { name: userName }
// });
}2. Verify Auth Response Format:
# Test private channel auth
curl -X POST http://localhost:4000/pusher/auth \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "socket_id=123.456&channel_name=private-test"
# Expected response for private channel:
# {"auth":"app-key:signature"}
# Should NOT include channel_data
# Test presence channel auth
curl -X POST http://localhost:4000/pusher/auth \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "socket_id=123.456&channel_name=presence-test"
# Expected response for presence channel:
# {"auth":"app-key:signature","channel_data":"{\"user_id\":\"...\",\"user_info\":{...}}"}3. Check Pusher.js Library Version:
# Ensure using compatible version
npm list pusher-js
# Update if needed
npm install pusher-js@latestSymptom: Debug mode enabled but no debug logs appear.
Possible Causes:
- Wrong debug flag format
- Log level not set correctly
- Logs being filtered
Solutions:
1. Use Correct Debug Flag:
# ✅ Correct
./soketi --config-file config.json --debug true
# ❌ Wrong
./soketi --config-file config.json --debug # Missing value
./soketi --config-file config.json -d # Wrong flag2. Check Log Output:
# Ensure not redirecting stderr
./soketi --config-file config.json --debug true 2>&1
# Or save to file
./soketi --config-file config.json --debug true 2>&1 | tee soketi.log3. Verify Debug Logs Format:
# Debug logs should look like:
2026-01-25T16:25:19.341777Z DEBUG soketi_rs::server: src/server.rs:191: Loading ArrayAppManager with 1 apps
2026-01-25T16:25:23.740581Z DEBUG soketi_rs::ws: src/ws.rs:34: Found app by key 'app-key': app_id = 'app-id'
1. Socket Registration Issue (v0.1.0)
- Problem: Sockets were added to channels but not to the adapter's socket map
- Symptom: Messages broadcast but not received by clients
-
Fix: Added
adapter.add_socket()call after socket establishment - Commit: Added in ws.rs socket handler
2. Config File Loading (v0.1.0)
- Problem: Config file not loaded without explicit parameter
- Symptom: Server starts with no apps configured
-
Fix: Must use
--config-fileparameter - Documentation: Updated getting-started.md
3. Private Channel Auth (v0.1.0)
- Problem: Backend sending channel_data for all channel types
- Symptom: Private channels fail auth, presence channels work
- Fix: Differentiate channel types in auth endpoint
- Example: See demo-chat/backend/server.js
1. Client Events
- Only work on private and presence channels
- Must start with "client-" prefix
- Require
enable_client_messages: truein app config
2. Presence Channels
- Member list not automatically synced on reconnect
- Large member lists may impact performance
- Consider pagination for channels with many members
3. Rate Limiting
- Per-app rate limits only
- No per-user rate limiting yet
- Consider implementing in your application layer
When reporting issues:
- Check if already fixed in latest version
- Search existing issues on GitHub
- Provide minimal reproduction case
-
Include debug logs with
--debug true - Specify versions of all components
Issue Template:
## Description
Brief description of the issue
## Steps to Reproduce
1. Start server with: `./soketi --config-file config.json`
2. Connect client with: `pusher.subscribe('channel')`
3. Trigger event: `curl -X POST ...`
## Expected Behavior
What should happen
## Actual Behavior
What actually happens
## Environment
- soketi.rs version: 0.1.0
- OS: macOS 14.0
- Client: pusher-js 8.4.0
- Browser: Chrome 120
## Debug LogsPaste relevant debug logs here
## Configuration
```json
{
"app_manager": {
// Sanitized config
}
}