Heroku: PHP Laravel, Nginx and Socket.IO in same Dyno

Tram Ho

Another Heroku tutorial ??

Most of the instructions for deploying Laravel on Heroku use Apache as a web server. By default Laravel comes with a .htaccess file used by Apache to rewrite all dynamic urls to the public/index.php file to handle routing with Laravel. Apache on Heroku also supports override config with .htaccess file, so setup with Apache is very easy.

https://devcenter.heroku.com/articles/custom-php-settings#apache-defaults

Apache uses a Virtual Host that responds to all hostnames. The document root is set up as a <Directory> reachable without access limitations and AllowOverride All set to enable the use of .htaccess files. Any request to a URL ending on .php will be rewritten to PHP-FPM using a proxy endpoint named fcgi://heroku-fcgi via mod_proxy_fcgi . The DirectoryIndex directive is set to index.php index.html index.html .

But what if you want to use Nginx? And a Laravel application not only runs PHP but also often has queue, schedule, database, redis, socketio … so how to handle it?

In this article I will go over some concepts related to Heroku and practice deploying Laravel, Socket.IO and Nginx.

To practice you need a Heroku Free account and install heroku-cli locally.

Deploy Laravel App

Create a Laravel project with composer :

Create heroku app:

When creating the app, heroku cli will automatically add 1 git remote repository, you can check it with the command:

If you don’t have heroku remote, add it yourself with the command git remote add heroku <heroku git url>

Ok, try to push on heroku repo:

Unlike normal git push when pushing on Github, for example, after pushing on heroku, Heroku will build and deploy.

Buildpacks

Because when the buildpacks have not been added to the heroku app, Heroku will automatically detect the appropriate buildpack to build (perhaps Heroku relies on composer.json and package.json files to detect buidpack?).

What is buildpack ? => In a nutshell, buildpacks is a collection of scripts commonly used to build, compile applications depending on the programming language of each application. For example, here, our application is PHP Laravel, there will be steps: composer install to install packages, npm install to install JS packages, npm run dev to compile and generate assets such as Sass, CSS, Javascript …

The main buildpacks supported by Heroku are: Ruby, Node.js, Clojure, Python, Java, Gradle, JVM, Grails 3.x, Scala, Play 2.x, PHP, Go

Here we need 2 buildpacks, heroku-buildpack-php, to run composer and heroku-buildpack-nodejs to run npm . Add to Heroku App with the following command:

Heroku will run buildpacks in turn in the order they are added.

Push back and see the result:

Now the npm install command has been run by Nodejs Buildpack with production NODE_ENV . But we still lack the running npm run dev or the npm run prod , so how to run it?

According to the document https://devcenter.heroku.com/articles/nodejs-support#customizing-the-build-process , we need to define 1 more npm scripts to guide Heroku run build, declared in package.json file either build or heroku-postbuild , where heroku-postbuild takes precedence.

So the package.json file will look like this:

Procfile

Open the app with the command: heroku open , a new browser tab is open: https://heroku-laravel-nginx-socketio.herokuapp.com/ and you will see 403 Forbidden error ??

In the log when pushing on heroku, there is a paragraph:

=> NOTICE: No Procfile, using 'web: heroku-php-apache2'

Procfile? => Each app will have a file named Procfile to declare the commands that are run when the app starts, for example:

  • Run the web server
  • Run queue worker

Syntax of Procfile file:

Inside:

  • <process type> is the name of the command, also known as Process Type , for example, web , worker
  • <command> is the command that is run when app start, for example heroku-php-apache2 , php artisan queue:work

There are two special process types, of which the web is the only process type that can handle HTTP requests. If your app needs a web server then you need to declare the command to run the web server in this type of process.

Each line will be a process type, each process type is run on a completely independent dyno. One of the advantages of Heroku is that it can be easily scaled by increasing the number of dynos per process type. But this will be discussed after we have money and need to run an app production on Heroku because with Free plan Heroku only allows us to create 1 web process and 1 worker process, maximum 1 dyno per process.

Back to our Laravel app, if we use Apache we will have the following Procfile file:

By default, heroku-php-apache2 uses the current folder as the root document, but Laravel needs to set the document root to public to navigate to the public/index.php file.

Want to use Nginx instead of Apache we will use command vendor/bin/heroku-php-nginx instead of vendor/bin/heroku-php-apache2 and need to custom Nginx config, because Nginx does not support .htaccess files like Apache , according to the instructions :

Deploy and reopen to see why this time and the correct Laravel page but 500 page error ?? Well first of all, there is no .env file .env .

Environment variable

Because the filesystem on Heroku is special in that the changes on the filesystem (not through git) will only be kept until dyno shutdown or restart, or every time deploy or restart all files change or add new. During the run (eg laravel.log file) will be deleted, only the files on git are kept.

So here we will not use the .env file because this file is usually not added to git. Instead environment variables will be set in your app’s settings on heroku (called Config Vars ) or maybe through heroku cli.

The most important environment variable with Laravel is APP_KEY , we will generate the key and use heroku cli to set the environment variable for the app:

Generate key with artisan command:

Then set the environment variable for heroku app with heroku cli:

In addition, we need to reconfigure the log to be able to view the log via the heroku logs command because if you use log in the file, the log file will be deleted after each restart => https://devcenter.heroku.com/articles/getting-started -with-laravel # changing-the-log-destination-for-production

Deploy Socket.io

To get started we’ll refer to a simple socketio code in repo: https://github.com/heroku-examples/node-socket.io , which displays the server time realtime via socketio:

Now how to run both Laravel and socketio Node at port 3000. We have the following solutions:

  • Adding a new process type => Not feasible because the process type runs on an independent dyno so it cannot connect between Laravel (web) and Socket.IO server
  • Add 1 new app just to run Socket.IO => Need more apps (Free plan can only create up to 5 App), or if the plan pays, it will cost an extra $ ?
  • Running on the same dyno with Laravel ( web processs type) => It seems fine for the demo, but the configuration of dyno is only 512MB so if the application is a bit bigger it will be not ok. Since the purpose is a free demo, we will try to run on the same dyno with the web ?

Fortunately, Heroku also has a topic on this issue => https://help.heroku.com/CTFS2TJK/how-do-i-run-multiple-processes-on-a-dyno . The way to do this is to use Shell’s background jobs to run multiple commands at the same time by adding the & character at the end of each statement.

wait -n is the Shell command, it will exit when there is at least one command exit and therefore will trigger restart dyno.

Need to fix the build step in package.json, to socketio-server dependency in the socketio-server directory:

The client (Browser) needs to connect to the socketio server, but since Heroku only opens a single port for the web process, it can’t connect to the 3000 port on the client. So we need to create a reverse proxy with nginx to proxy the request to localhost: 3000, in addition to the nginx config file:

When there is a request to url /socketio/ , Nginx will request to http://localhost:3000/socket.io/ .

Using socketio on client:

Deploy and demo succeeded =))

Share the news now

Source : Viblo