There is a new PHP package available for Laravel users called laravel-websockets that allows you to quickly start a websocket server for your applications.
The added benefit is that it’s fully written in PHP, which means it will run on pretty much any system that already runs your Laravel code, without additional tools. Once installed, you can start a websocket server as easily as this:
$ php artisan websocket:serve
That’ll open a locally available websocket server, running on 127.0.0.1:6001
.
This is great for development, but it also performs pretty well in production. To make that more manageable, we’ll run this as a supervisor job with an Nginx proxy in front of it, to handle the SSL part.
Supervisor job for laravel-websockets
The first thing we’ll do is make sure that process keeps running forever. If it were to crash (out of memory, killed by someone, throwing exceptions, …), we want it to automatically restart.
For this, we’ll use supervisor
, a versatile task runner that is ideally suited for this. Technically, systemd
would work equally good for this purpose, as you could quickly add a unit file to run this job.
First, install supervisord
.
# On Debian / Ubuntu apt install supervisor # On Red Hat / CentOS yum install supervisor systemctl enable supervisor
Once installed, add a job for managing this websocket server.
$ cat /etc/supervisord.d/ohdear_websocket_server.ini [ohdear_websocket_server] command=/usr/bin/php /var/www/vhosts/ohdear.app/htdocs/artisan websocket:start numprocs=1 autostart=true autorestart=true user=ohdear_prod
This example is taken from ohdear.app, where it’s running in production.
Once the config has been made, instruct supervisord
to load the configuration and start the job.
$ supervisorctl update $ supervisorctl start websockets
Now you have a running websocket server, but it will still only listen to 127.0.0.1:6001
, not very useful for your public visitors that want to connect to that websocket.
Note: if you are expecting a higher number of users on this websocket server, you’ll need to increase the maximum number of open files supervisord
can open. See this blog post: Increase the number of open files for jobs managed by supervisord.
Add an Nginx proxy to handle the TLS
Let your websocket server run locally and add an Nginx configuration in front of it, to handle the TLS portion. Oh, and while you’re at it, add that domain to Oh Dear! to monitor your certificate expiration dates. ;-)
The configuration looks like this, assuming you already have Nginx installed.
$ cat /etc/nginx/conf.d/socket.ohdear.app.conf server { listen 443 ssl; listen [::]:443 ssl; server_name socket.ohdear.app; access_log /var/log/nginx/socket.ohdear.app/proxy-access.log main; error_log /var/log/nginx/socket.ohdear.app/proxy-error.log error; # Start the SSL configurations ssl on; ssl_certificate /etc/letsencrypt/live/socket.ohdear.app/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/socket.ohdear.app/privkey.pem; ssl_session_timeout 3m; ssl_session_cache shared:SSL:30m; ssl_protocols TLSv1.1 TLSv1.2; # Diffie Hellmann performance improvements ssl_ecdh_curve secp384r1; location / { proxy_pass http://127.0.0.1:6001; 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 https; proxy_set_header X-VerifiedViaNginx yes; proxy_read_timeout 60; proxy_connect_timeout 60; proxy_redirect off; # Specific for websockets: force the use of HTTP/1.1 and set the Upgrade header proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } }
Everything that connects to socket.ohdear.app over TLS will be proxied to a local service on port 6001
, in plain text. This offloads all the TLS (and certificate management) to Nginx, keeping your websocket server configuration as clean and simple as possible.
This also makes automation via Let’s Encrypt a lot easier, as there are already implementations that will manage the certificate configuration in your Nginx and reload them when needed.