DEV Community

Cover image for Deploy Laravel 8 to Compute Engine instance (LEMP stack)
Andre Kho
Andre Kho

Posted on

Deploy Laravel 8 to Compute Engine instance (LEMP stack)

In this post I'll walk you through to get a Laravel 8 app running on a fresh Compute Engine instance in NGINX way. We'll be using these assumptions:

  • Already have a Laravel 8 project stored in a remote repository (I'm using Github for this post).
  • Stateful solution which means the database and application code are placed together in the instance.
  • You want to use Google Cloud Platform services to host your application.
  • You want to have full control over server environments eg. web server configurations.
  • You want your app to just works as is.
  • You have familiarity with Linux commands.
  • You need optimal configuration for your app.
  • You may skip the database steps if you are not actually use it on your project.

Now, let's get started.

Step 1: Create a Compute Engine instance

First thing to do is to create and configure our VM instance to serve our app. In Cloud Console, refer to Compute Engine menu then click on CREATE INSTANCE button as shown below.
Compute Engine main page

Then in create instance form, fill and set properties for our instance. My configuration goes like these:

Compute Engine instance creation configuration part 1/6

  • Choose your instance name wisely as it can't be changed later after creation.
  • Pick your nearest region and zone for lower latency.
  • Choose lowest possible machine class and type to avoid huge charge to your bill, even if it's just for trial use.

Compute Engine instance creation configuration part 2/6

  • On boot disk configuration, choose your most familiar operating system. If it's required, you may choose latest LTS version.
  • Choose SSD persistent disk and smallest size as starting point (10 GB is the lowest). SSD comes with greater performance compared to others except the Extreme one, but costs higher.

Compute Engine instance creation configuration part 3/6

  • Enable all HTTP and HTTPS access, just in case you need to configure SSL certificate for your instance.

Compute Engine instance creation configuration part 4/6

  • On advanced configuration, the Network section, choose your existing VPC network. (I already have a custom one called local-vpc. You may already have the default one.)
  • Set a static external IP address as shown below.

Compute Engine instance creation configuration part 5/6

  • Set a static external IP address now so you won't bother to reconfigure DNS A record to this instance everytime its IP address changes.

Compute Engine instance creation configuration part 6/6

  • On advanced configuration, the Security section, enable Shielded VM for increased security to your instance.
  • You may want to add a Public SSH key in this section, so you can access your instance securely from your workstation or PC.

Click on CREATE button to create the instance.


Step 2: Install and configure server environments

Now your instance already up and running. We need to install our necessary environments for our app.
Created VM
Click on SSH button (or run ssh command from your PC that access this VM's external IP address)

Now first in your SSH terminal, run these commands:

  • Set your timezone
sudo timedatectl set-timezone Asia/Jakarta
Enter fullscreen mode Exit fullscreen mode
  • Check for any updates
sudo apt-get update
Enter fullscreen mode Exit fullscreen mode
  • Do upgrade according to updates
sudo apt-get upgrade
Enter fullscreen mode Exit fullscreen mode
  • (Optional) Do reboot the instance
sudo reboot
Enter fullscreen mode Exit fullscreen mode

At this point, access our instance once again and we will install our environments:

  • Install NGINX
sudo apt install nginx
Enter fullscreen mode Exit fullscreen mode
  • Install MariaDB server. At this point, follow the instruction to make our MariaDB server secure. You'll do the following:
    • Authenticate as root user (just press enter to move forward)
    • Set new root password (remember the password you entered here!)
    • Accept everything (don't forget to read the instruction, though)
sudo apt install mariadb-server
sudo mysql_secure_installation
Enter fullscreen mode Exit fullscreen mode
  • Create a new database and database user for our app
sudo mysql
Enter fullscreen mode Exit fullscreen mode
mysql> CREATE DATABASE 'mydb';
mysql> CREATE USER 'mymy'@'localhost' IDENTIFIED BY 'myPassw0rd';
mysql> GRANT ALL ON 'mydb'.* TO 'mymy'@'localhost';
mysql> FLUSH PRIVILEGES;
mysql> EXIT
Enter fullscreen mode Exit fullscreen mode
  • Install unzip so our Composer can do its work when installing dependencies
sudo apt install unzip
Enter fullscreen mode Exit fullscreen mode
  • Add PPA repository for NGINX and PHP
sudo add-apt-repository ppa:ondrej/php && sudo apt update
sudo add-apt-repository ppa:ondrej/nginx && sudo apt update
sudo apt upgrade
Enter fullscreen mode Exit fullscreen mode
  • Install PHP-FPM and some 'necessary' PHP modules (refer to Laravel official docs anyway)
sudo apt install -y php7.4-fpm php7.4-curl php7.4-gd php7.4-json php7.4-mbstring php7.4-mysql php7.4-opcache php7.4-xml php7.4-xmlrpc php7.4-fileinfo php7.4-imagick php-pear
sudo apt upgrade
Enter fullscreen mode Exit fullscreen mode
  • Check PHP CLI version with php -v command. If the PHP CLI version check returns other than version 7.4, run below command to change its version. Type a number option that shows version 7.4 then press Enter.
sudo update-alternatives --config php
Enter fullscreen mode Exit fullscreen mode
  • Create an NGINX config file under /etc/nginx/sites-available/ directory. Name the config file either with a domain name you have or just give it main or anything you want. I'll go with example.com.
sudo nano /etc/nginx/sites-available/example.com
Enter fullscreen mode Exit fullscreen mode

This will open a NANO text editor ready to create the file. Copy and paste the code below to the editor.

server {
  listen 80;

  # Use your domain/subdomain name here
  server_name example.com;

  # We will clone our "repo-name" repository which
  # contains our Laravel project to /var/www/ directory later.
  # Change this path according to your repository name
  # and end with /public.
  root /var/www/repo-name/public;

  index index.php;

  add_header X-Frame-Options "SAMEORIGIN";
  add_header X-Content-Type-Options "nosniff";

  charset utf-8;

  location / {
    try_files $uri $uri/ /index.php?$query_string;
  }

  location = /favicon.ico { access_log off; log_not_found off; }
  location = /robots.txt  { access_log off; log_not_found off; }

  error_page 404 /index.php;

  location ~ \.php$ {
    fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
    include fastcgi_params;
  }

  location ~ /\.(?!well-known).* {
    deny all;
  }

  # The access log file name is up to you.
  access_log /var/log/nginx/access_laravel.log;

  # And this error log file name as well is up to you.
  error_log /var/log/nginx/error_laravel.log;
}
Enter fullscreen mode Exit fullscreen mode

Make sure everything is correct, then press Ctrl + X, press Y, and finally press Enter. This will save the new file and exit the editor.

  • Create a symbolic link to the config file in /etc/nginx/sites-enabled/ directory, so NGINX can read our file from there.
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
Enter fullscreen mode Exit fullscreen mode
  • Unlink existing default configuration
sudo unlink /etc/nginx/sites-enabled/default
Enter fullscreen mode Exit fullscreen mode
  • Test our NGINX configuration
sudo nginx -t
Enter fullscreen mode Exit fullscreen mode
  • If no errors come up, restart the NGINX service to load our new configuration
sudo service nginx reload
Enter fullscreen mode Exit fullscreen mode

Now, we will install additional environments for our Laravel app.

  • Install Node Version Manager (NVM) to run npm installation in the project later.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.bashrc
Enter fullscreen mode Exit fullscreen mode
  • Install NodeJS latest LTS version. Use nvm list-remote to list Node versions (current latest LTS version is Gallium)
nvm install lts/gallium
Enter fullscreen mode Exit fullscreen mode
  • You may want to check the installed version of Node and NPM by running these commands.
node -v
npm -v
Enter fullscreen mode Exit fullscreen mode
  • Install Composer (Required)
curl -sS https://getcomposer.org/installer -o composer-setup.php
Enter fullscreen mode Exit fullscreen mode
HASH=`curl -sS https://composer.github.io/installer.sig`
php -r "if (hash_file('SHA384', 'composer-setup.php') === '$HASH') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
Enter fullscreen mode Exit fullscreen mode
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
Enter fullscreen mode Exit fullscreen mode

Step 3: Pull your app from a remote repository

Now it's time to put our app to this VM. I'll go with more secure way to clone our repository by using SSH keys. You may use your existing SSH keys or generate a new one.

First, we need to generate a new SSH key pair. Please refer to this site to learn about how to use SSH.

ssh-keygen -t rsa -b 4096 -C "<your-Github-email>"
Enter fullscreen mode Exit fullscreen mode

Make sure change "your-Github-email" to your actual email address used in Github (it's not mandatory, to be honest. You can fill anything, but email is recommended as it's easier to identify).
You'll be asked file name for the key. You may just press Enter to use default file name.
Then, you'll be asked to type in a passphrase. It's a best practice to type in the passphrase, but for our case, it's not necessary to do so. Just press Enter twice so you won't use any passphrase.
Finally, the SSH key pair is generated and stored within .ssh folder in current user directory (~/.ssh/). There will be two files: one with .pub extension is called public key, the other one is called private key.

Next step, we will put our public key content to our Github account (if you are using other service that Github, refer to their documentation about how to clone a repo using SSH).
Copy our public key content which usually begins with ssh-rsa xxxx.
Then, go to your Github account settings page > SSH and GPG keys. Click on New SSH key button. Type a name for the key (anything but noticeable) and paste the public key in provided text area. Click on Add new key button.
Add SSH key to Github

Now, we can start cloning our repo from Github securely. Here are the steps.

  • (Required) Secure our SSH private key by changing file permission to read-only by its owner (current user who creates the key pair.
chmod 400 .ssh/id_rsa
Enter fullscreen mode Exit fullscreen mode
  • Activate ssh-agent so we can start use SSH.
eval `ssh-agent -s`
Enter fullscreen mode Exit fullscreen mode
  • Add our private key to the SSH agent. Remember your key file name.
ssh-add id_rsa
Enter fullscreen mode Exit fullscreen mode
  • Test whether we can connect to Github via SSH. Type yes and press Enter to confirm connection when being asked.
ssh -T git@github.com
Enter fullscreen mode Exit fullscreen mode
  • If it returns a message like: "Hi, <user>! You've successfully authenticated, but GitHub does not provide shell access.", then it's successful connection! We can continue to clone our repo to /var/www directory by running this command (change to your username and repository name accordingly).
cd /var/www/
sudo chown $USER:$USER .
git clone git@github.com:<your-Github-username>/<your-repo-name>
sudo chown root:root .
Enter fullscreen mode Exit fullscreen mode

What I'm doing upthere is changing ownership of /var/www folder to current user, so we can run git clone command as current user. Thus, our cloned repo will be owned by it. Then, we return the ownership of /var/www directory back to root user (not everything inside it). Pay attention to cd command.


Step 4: Install Laravel dependencies and configure project environment variables

At this point, we already have our cloned project inside /var/www directory. Make sure you are inside the project directory by using cd command.

First thing is to create a .env file by copying from .env.example file.

cp .env.example .env
Enter fullscreen mode Exit fullscreen mode

Then, open and edit the copied .env file with NANO editor.

nano .env
Enter fullscreen mode Exit fullscreen mode

Change respective environment variable values to suit your requirements. For my case, I'll go with updating these values.

APP_ENV=production
APP_DEBUG=false
DB_DATABASE=my-db
DB_USERNAME=mymy
DB_PASSWORD=myPassw0rd
Enter fullscreen mode Exit fullscreen mode

Remember your database user, its password, and the database whom its database user can access (refer to Step 2). Press Ctrl + X, then Y, and finally press Enter to save.

We will install all Laravel modules and its dependencies by running these commands.

composer install --optimize-autoloader --no-dev
npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

Moving forward, we need to generate application key which is mandatory.

php artisan key:generate
Enter fullscreen mode Exit fullscreen mode

Next we will create a symbolic link for our /storage folder.

php artisan storage:link
Enter fullscreen mode Exit fullscreen mode

One more step, we can migrate our database. You may skip this if you are actually not using database.

php artisan migrate:refresh --seed
Enter fullscreen mode Exit fullscreen mode

Finally, we need to change ownership for www-data user (web server user) so it can access and serve our app. Change repo-name to your actual repository or folder name.

sudo chown -R www-data:www-data /var/www/repo-name
Enter fullscreen mode Exit fullscreen mode

There are many alternatives to this. One way, you can add www-data user to current user group and vice versa.

At last, we can access our website through external IP address of our VM. There are more steps if we want to make use of our own domain instead of server's IP address, but those are for future post.
Final view


Conclusion

It's pretty hefty to just deploying one website, especially if there are many requirements behind the scene. As the title said, one way we can deploy our Laravel app is just by leveraging most used server engine like NGINX, then we can put every other Laravel dependencies in place inside the server in a pretty straightforward way (read: running through commands).
One thing to note that we need to pay attention about security of the server itself, which a small part of it is by making use of SSH. We can further improve this by configuring correct firewall rules, install SSL certificates, reconfigure SSH access, optimize NGINX configurations, etc. If you mind though, you can configure high availability to your app even with Compute Engine by making our VMs stateless.


That's all for this post. Hope it helps you. Don't hesitate to ask me if you are unsure or you want add something that I might be missing in this post. Thank you for your time reading this 🤗

Top comments (3)

Collapse
 
amarzouk profile image
Ahmed Marzouk

Thank you very much, it is a very helpful article.

I struggled a little bit with the "permission denied" error. I would appreciate it if you direct me to any tutorial about that.

Thank you!

Collapse
 
nekoto profile image
Andre Kho

Yeah sure! Here is one I've found, mostly about using sudo chown or sudo chmod for a directory or files. You can also use sudo -i and you can have full control.

Collapse
 
amarzouk profile image
Ahmed Marzouk

Thank you very much! really appreciate it!