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.
Then in create instance form, fill and set properties for our instance. My configuration goes like these:
- 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.
- 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.
- Enable all HTTP and HTTPS access, just in case you need to configure SSL certificate for your instance.
- 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.
- 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.
- 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.
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
- Check for any updates
sudo apt-get update
- Do upgrade according to updates
sudo apt-get upgrade
- (Optional) Do reboot the instance
sudo reboot
At this point, access our instance once again and we will install our environments:
- Install NGINX
sudo apt install nginx
- 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
- Create a new database and database user for our app
sudo mysql
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
- Install
unzip
so our Composer can do its work when installing dependencies
sudo apt install unzip
- 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
- 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
- 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
- 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 itmain
or anything you want. I'll go with example.com.
sudo nano /etc/nginx/sites-available/example.com
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;
}
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/
- Unlink existing default configuration
sudo unlink /etc/nginx/sites-enabled/default
- Test our NGINX configuration
sudo nginx -t
- If no errors come up, restart the NGINX service to load our new configuration
sudo service nginx reload
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
- Install NodeJS latest LTS version. Use
nvm list-remote
to list Node versions (current latest LTS version is Gallium)
nvm install lts/gallium
- You may want to check the installed version of Node and NPM by running these commands.
node -v
npm -v
- Install Composer (Required)
curl -sS https://getcomposer.org/installer -o composer-setup.php
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;"
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
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>"
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.
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
- Activate
ssh-agent
so we can start use SSH.
eval `ssh-agent -s`
- Add our private key to the SSH agent. Remember your key file name.
ssh-add id_rsa
- 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
- 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 .
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
Then, open and edit the copied .env
file with NANO editor.
nano .env
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
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
Moving forward, we need to generate application key which is mandatory.
php artisan key:generate
Next we will create a symbolic link for our /storage
folder.
php artisan storage:link
One more step, we can migrate our database. You may skip this if you are actually not using database.
php artisan migrate:refresh --seed
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
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.
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)
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!
Yeah sure! Here is one I've found, mostly about using
sudo chown
orsudo chmod
for a directory or files. You can also usesudo -i
and you can have full control.Thank you very much! really appreciate it!