We have just finished our transition from a websocket server based on laravel-echo-server to one that is fully driven by PHP: laravel-websockets. In this post, we'll highlight why and how we made that move.

Simplifying our stack #

As we're built on Laravel, we already run a fair bit of nodejs during our build phase. Our frontend JavaScript & CSS already get compiled via webpack. So in a way, our stack already includes node to make this all happen.

Part of our smooth user experience (if we say so ourselves ;-)) comes from the use of websockets, that allows us to give instant feedback to our users in their dashboard & our homepage. To make that work, we've always used laravel-echo-server, a node implementation of a websocket server.

To make that websocket server work, you can use 2 methods: use a Redis queue or publish messages directly through HTTP. We used a Redis queue, which means the following events took place for us:

Laravel publishes a message to a Redis channel The echo-server listens to new events being stored there The echo-server relays those to its subscribers/clients

This has worked very well for us, without any issues.

But we found ourselves in the unique spot to test an even simpler approach: run a websocket server fully in PHP without the need for node.

Moving to PHP #

Our initial reaction was "but surely PHP can't handle the load a Node process could, right?".

Well, to get started we benchmarked the laravel-websockets package using Artillery. What we found was that the websocket implementation in PHP could handle our load with great ease and keep under 50MB memory consumption.

Performance, during our testing, appeared on-par with the node implementation.

Since we weren't losing anything, we dediced to remove our node dependency for our production machines and run the entire websocket stack in PHP.

Adding TLS and supervisor #

Our setup is already using Nginx as a TLS proxy as well as Supervisor to keep all our workers running, so we already had the building blocks in place to add some configuration for our new websocket server.

We configured both Nginx and Supervisor to handle the TLS part and the job-running pretty quickly.

Transitioning from laravel-echo-server to laravel-websockets #

Code-wise, the change was a piece of cake. Unless we're forgetting something, it consisted of:

Remove all references to /js/socket.io.js in our frontend (we'll no longer be needing socket.io for our websockets)

in our frontend (we'll no longer be needing socket.io for our websockets) Install laravel-websockets and follow its install instructions

Change our frontend-code from using socket.io to pusher

window .Echo = new Echo({ - broadcaster: 'socket.io' , + broadcaster: 'pusher' , key : window .pusherKey, - host: window .pusherHost, + wsHost: window .pusherHost, + wsPort: window .pusherPort, + disableStats: true , });

Change our broadcast driver from Redis to using Pusher in .env

- BROADCAST_DRIVER=redis + BROADCAST_DRIVER=pusher + PUSHER_ENDPOINT=socket.ohdear.app + PUSHER_ENDPOINT_SCHEME=https

That was enough for us to move to laravel-websockets.

What did we gain? #

One of its biggest gains is in our development process: we now just need to run php artisan websocket:serve to get a local websocket server going and not have to deal with the (rather confusing) configuration of laravel-echo-server.

Additionally, we simplified our server setup and now fully rely on PHP without Node for running websockets. Managing less software is always a win, especially from a security angle (keeping track of the node ecosystem and the dependencies of the echo-server).

For us, it was a no-brainer to make the switch.