Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mohsenmojadam2019/d7a748cdcc470f66cc62bebc318b8b69 to your computer and use it in GitHub Desktop.
Save mohsenmojadam2019/d7a748cdcc470f66cc62bebc318b8b69 to your computer and use it in GitHub Desktop.
Laravel-Websockets Deploy Nginx Virtualhost with SSL

Laravel-websockets, SSL Certificate, Let's Encrypt, Certbot, Supervisor, Digitalocean Ubuntu

I did two days work to run laravel-websockets on my server. It will be nice to share with you.

Steps

Step1 - Laravel Websockets Installation with composer

Laravel WebSockets can be installed via composer:

composer require beyondcode/laravel-websockets

The package will automatically register a service provider.
This package comes with a migration to store statistic information while running your WebSocket server. You can publish the migration file using:

php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="migrations"

Run the migrations with:

php artisan migrate

Next, you need to publish the WebSocket configuration file:

php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config"

This is the default content of the config file that will be published as config/websockets.php:

return [

    /*
     * This package comes with multi tenancy out of the box. Here you can
     * configure the different apps that can use the webSockets server.
     *
     * Optionally you can disable client events so clients cannot send
     * messages to each other via the webSockets.
     */
    'apps' => [
        [
            'id' => env('PUSHER_APP_ID'),
            'name' => env('APP_NAME'),
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'enable_client_messages' => false,
            'enable_statistics' => true,
        ],
    ],

    /*
     * This class is responsible for finding the apps. The default provider
     * will use the apps defined in this config file.
     *
     * You can create a custom provider by implementing the
     * `AppProvider` interface.
     */
    'app_provider' => BeyondCode\LaravelWebSockets\Apps\ConfigAppProvider::class,

    /*
     * This array contains the hosts of which you want to allow incoming requests.
     * Leave this empty if you want to accept requests from all hosts.
     */
    'allowed_origins' => [
        //
    ],

    /*
     * The maximum request size in kilobytes that is allowed for an incoming WebSocket request.
     */
    'max_request_size_in_kb' => 250,

    /*
     * This path will be used to register the necessary routes for the package.
     */
    'path' => 'laravel-websockets',

    'statistics' => [
        /*
         * This model will be used to store the statistics of the WebSocketsServer.
         * The only requirement is that the model should extend
         * `WebSocketsStatisticsEntry` provided by this package.
         */
        'model' => \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry::class,

        /*
         * Here you can specify the interval in seconds at which statistics should be logged.
         */
        'interval_in_seconds' => 60,

        /*
         * When the clean-command is executed, all recorded statistics older than
         * the number of days specified here will be deleted.
         */
        'delete_statistics_older_than_days' => 60,
        
        /*
         * Use an DNS resolver to make the requests to the statistics logger
         * default is to resolve everything to 127.0.0.1.
         */
        'perform_dns_lookup' => false,
    ],

    /*
     * Define the optional SSL context for your WebSocket connections.
     * You can see all available options at: http://php.net/manual/en/context.ssl.php
     */
    'ssl' => [
        /*
         * Path to local certificate file on filesystem. It must be a PEM encoded file which
         * contains your certificate and private key. It can optionally contain the
         * certificate chain of issuers. The private key also may be contained
         * in a separate file specified by local_pk.
         */
        'local_cert' => null,

        /*
         * Path to local private key file on filesystem in case of separate files for
         * certificate (local_cert) and private key.
         */
        'local_pk' => null,

        /*
         * Passphrase for your local_cert file.
         */
        'passphrase' => null
    ],
];

Step2 - Pusher Replacement with NPM

The easiest way to get started with Laravel WebSockets is by using it as a Pusher replacement. The integrated WebSocket and HTTP Server has complete feature parity with the Pusher WebSocket and HTTP API. In addition to that, this package also ships with an easy to use debugging dashboard to see all incoming and outgoing WebSocket requests.

To make use of the Laravel WebSockets package in combination with Pusher, you first need to install the official Pusher PHP SDK.

If you are not yet familiar with the concept of Broadcasting in Laravel, please take a look at the Laravel documentation.

composer require pusher/pusher-php-server "~3.0"

Next, you should make sure to use Pusher as your broadcasting driver. This can be achieved by setting the BROADCAST_DRIVER environment variable in your .env file:

BROADCAST_DRIVER=pusher

PUSHER_APP_ID=1
PUSHER_APP_KEY=1
PUSHER_APP_SECRET=1
PUSHER_APP_CLUSTER=mt1

Step3 - Pusher Configuration

When broadcasting events from your Laravel application to your WebSocket server, the default behavior is to send the event information to the official Pusher server. But since the Laravel WebSockets package comes with its own Pusher API implementation, we need to tell Laravel to send the events to our own server.

To do this, you should add the host and port configuration key to your config/broadcasting.php and add it to the pusher section. The default port of the Laravel WebSocket server is 6001 .

'pusher' => [
    'driver' => 'pusher',
    'key' => env('PUSHER_APP_KEY'),
    'secret' => env('PUSHER_APP_SECRET'),
    'app_id' => env('PUSHER_APP_ID'),
    'options' => [
        'cluster' => env('PUSHER_APP_CLUSTER'),
        'encrypted' => true,
        'host' => '127.0.0.1',
        'port' => 6001,
        'scheme' => 'http'
    ],
],

You may add additional apps in your config/websockets.php file.

'apps' => [
    [
        'id' => env('PUSHER_APP_ID'),
        'name' => env('APP_NAME'),
        'key' => env('PUSHER_APP_KEY'),
        'secret' => env('PUSHER_APP_SECRET'),
        'enable_client_messages' => false,
        'enable_statistics' => true,
    ],
],

Step4 - Usage with Laravel Echo

Locate the resources/js/bootstrap.js file and add the following lines.

import Echo from 'laravel-echo';

window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: '1',
    cluster: 'mt1',
    wsHost: window.location.hostname,
    wsPort: 6001,
    wssPort: 443
});

Open terminal window and navigate to our laravel project directory and then run this command.

npm run prod

Step5 - Installing Certbot

1. SSH into the server

SSH into the server running your HTTP website as a user with sudo privileges.

2. Add Certbot PPA

You'll need to add the Certbot PPA to your list of repositories. To do so, run the following commands on the command line on the machine:

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update

3. Install Certbot

Run this command on the command line on the machine to install Certbot.

sudo apt-get install certbot python-certbot-nginx

5. Get a SSL Certificate

Let’s Encrypt do a strong Domain Validation automatically with multiple challenges to verify the ownership of the domain. Once the Certificate Authority (CA) verified the authenticity of your domain, SSL certificate will be issued.

sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Step6 - Nginx Virtual Host Example

open your etc/nginx/sites-available/yourdomain.com: file.

map $http_upgrade $type {
  default "web";
  websocket "ws";
}

server {

    listen 80;
    listen [::]:80;

    root /var/www/yourdomain.com/laravel/public;
    index index.php index.html index.htm index.nginx-debian.html;

    charset utf-8;
    
    server_name yourdomain.com www.yourdomain.com;

    location ~ \.php$ {
        try_files $uri /index.php =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php7.1-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
    
    location / {
        try_files /nonexistent @$type;
    }
  
    location @web {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location @ws {
        proxy_pass             http://127.0.0.1:6001;
        proxy_set_header Host  $host;
        proxy_read_timeout     60;
        proxy_connect_timeout  60;
        proxy_redirect         off;

        # Allow the use of websockets
        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;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    
    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    # Redirect non-https traffic to https
    # if ($scheme != "https") {
    #     return 301 https://$host$request_uri;
    # } # managed by Certbot
}

Step7 - Keeping the socket server running with supervisord

The php artisan websockets:serve --host=127.0.0.1 daemon needs to always be running in order to accept connections. This is a prime use case for supervisor, a task runner on Linux.

First, make sure supervisor is installed.

# On Debian / Ubuntu
apt install supervisor

Once installed, add a new process that supervisor needs to keep running. You place your configurations in the /etc/supervisor/conf.d (Debian/Ubuntu) directory.

Within that directory, create a new file called websockets.conf.

[program:websockets]
command=/usr/bin/php /var/www/yourdomain.com/laravel/artisan websockets:serve --host=127.0.0.1
numprocs=1
autostart=true
autorestart=true
user=root

Once created, instruct supervisor to reload its configuration files (without impacting the already running supervisor jobs).

supervisorctl update
supervisorctl start websockets
supervisorctl status

CURRENTLY WRITING...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment