Deploy a WebSocket Application with Socket.IO
Socket.IO is the most widely used library for real-time bidirectional communication in JavaScript applications. It handles WebSocket connections with automatic fallback to HTTP long-polling, built-in reconnection, and room-based message routing.
This guide covers how to deploy a Socket.IO application on Railway, handle the 15-minute connection limit, and scale across multiple instances.
What you will deploy
- A Socket.IO server running on Express.
- A frontend client that connects to the server.
- Both services deployed to the same Railway project.
Server setup
Create a new Node.js project and install dependencies:
Create server.js:
Key configuration:
- CORS: set
originto your frontend's domain. Use a reference variable likeFRONTEND_URL=${{Frontend.RAILWAY_PUBLIC_DOMAIN}}to keep it in sync. - Transports:
['polling', 'websocket']starts with HTTP long-polling and upgrades to WebSocket. This is Socket.IO's default and works reliably through Railway's proxy. - Health check: the
/healthendpoint lets Railway monitor the service. Configure it in Health Checks.
Client setup
Install the Socket.IO client in your frontend:
Connect to your Railway-hosted server:
Replace https://your-socketio-server.railway.app with your service's public domain.
Deploy to Railway
- Create a new project on Railway.
- Deploy the Socket.IO server from your GitHub repository or using the CLI.
- Generate a domain for the server.
- Set the
FRONTEND_URLvariable to your frontend's domain for CORS. - Configure a health check pointing to
/health.
Ensure the server binds to 0.0.0.0 and reads the port from the PORT environment variable.
Handle the 15-minute connection limit
Railway has a maximum request duration of 15 minutes. WebSocket connections open longer than 15 minutes are terminated.
Socket.IO handles this automatically. When the connection drops, the client reconnects and receives a new socket.id. The reconnection settings in the client configuration above ensure this happens with exponential backoff.
To preserve state across reconnections:
- Use rooms for group membership. When a client reconnects, re-join the rooms it was in:
- Use a last-seen timestamp. On reconnection, the client sends the timestamp of the last message it received. The server sends any messages the client missed.
Scale with a Redis adapter
By default, Socket.IO routes messages through the server's in-process memory. If you scale to multiple instances, clients connected to different instances cannot communicate.
The Redis adapter solves this by routing messages through Redis pub/sub.
- Add a Redis database to your Railway project.
- Install the adapter:
- Configure the server:
Set REDIS_URL using a reference variable pointing to your Redis service's connection string.
With the Redis adapter in place, messages broadcast on one instance reach clients connected to all instances.
Common pitfalls
WebSocket upgrade fails, connection stays on polling. This can happen if your client or a proxy between the client and Railway does not support WebSocket upgrades. Socket.IO falls back to polling automatically, which works but is less efficient. To debug, check the transport property:
CORS errors on connection. The Socket.IO server must explicitly allow your frontend's origin. Set the origin in the cors option. Using '*' works for development but should be restricted in production.
Sticky sessions not configured for multiple instances. Without the Redis adapter, clients that reconnect may hit a different instance that does not have their session. The Redis adapter eliminates this issue. If you cannot use Redis, enable sticky sessions by routing based on the Socket.IO session ID, though this is more complex to configure.
Next steps
- Choose between SSE and WebSockets - Decide which protocol fits your use case.
- Redis on Railway - Set up Redis for the Socket.IO adapter.
- Private Networking - Connect services within your project.
- Scaling - Scale your Socket.IO server based on connection count.