NotesWhat is notes.io?

Notes brand slogan

Notes - notes.io

Step-By-Step Guide To Deploying Laravel Applications On Virtual Private Servers

Laravel makes it much easier to develop modern web applications. However, deploying them on a server is a different story.



There are just so many options.



PaaS like Heroku or AWS Elastic Beanstalk, unmanaged virtual private servers, shared hosting, and so on.



Using cPanel to deploy a Laravel app to a shared server, it is as easy as zippping up the source code and all its dependencies and uploading them to the server. You don't have much control on shared hosting.



PaaS like AWS Elastic Beanstalk or Heroku strike a good compromise between ease of use and control. However they can be quite expensive at times. For example, a Heroku standard 1x dyno is $25 per month, and only 512MB RAM.



Unmanaged virtual private servers are affordable and provide a lot of control on the server. A server with 2GB RAM, 20GB SSD space and 2TB transfer bandwidth is available for $15 per month.



Now the problem with unmanaged virtual private servers is that they are unmanaged. You'll be responsible for installing all necessary software, configuring them, and keeping them updated.



In this article, I'll guide you step-by-step in the process of how to deploy a Laravel project on an unmanaged virtual private server (we'll refer to it as VPS from now on). If you want to check out the benefits of the framework first, go ahead and get an answer to the question of why use the Laravel framework. We are now ready to jump in, so let's go!



Prerequisites



This article assumes you have some experience working with the Linux command line. Ubuntu will be the server's operating-system. This means that you will have to use the terminal to perform all necessary tasks. You will need to be familiar with concepts such as Sudo and file permissions. Also, the differences between root users and non-root users, and git.



Project Code and Deployment Plans



This article is based on a mockup project I created. It's a simple application for question boards where users can ask questions and others can answer them. This is a simplified version StackOverflow.



The project source code is available on https://github.com/fhsinchy/guide-to-deploying-laravel-on-vps repository. Make a fork of this repository and clone it on your local computer.



Once you have a copy the project on your computer you are ready to begin the Laravel deployment process. You'll need to create a VPS and configure a way to push the source from your computer to the server.



Provisioning a new Ubuntu Server



There are several VPS providers out there, such as DigitalOcean, Vultr, Linode, and Hetzner. While the process of working on an unmanaged virtual private server is the same for all providers, they do not offer the same services.



DigitalOcean provides managed database services. Vultr (on the other hand) doesn't have such services. You don't have to worry about these differences.



I will show only the unmanaged ways of doing things. So regardless of the provider, you're using, the steps should be identical.



Before you can provision a new server, it is necessary to generate SSH keys.



Generating new SSH keys



According to Wikipedia - "Secure Shell (SSH) is a cryptographic network protocol for operating network services securely over an unsecured network." It allows you to connect with a remote server by using a password or key-pair.



If you're already familiar with SSH and have previously generated SSH key-pairs on your computer, you may skip this subsection. You can use the following command to generate a key-pair on macOS or Linux.



You'll see several prompts on the terminal. You can scroll through them by pressing enter. You don't need to enter any password. Once you've generated the key-pair, you'll find a file named id_rsa.pub inside the ~/.ssh/ directory. This file is required to provision a new VPS.



Provisioning a New Virtual Private Server



I've already said there are some differences between the VPS service providers, so if you want to be absolutely in line with this article, use DigitalOcean.



A single virtual private server on DigitalOcean is known as a droplet. It's known as an instance on Vultr, and a Linode as a Linode. Log in to your provider and create a new virtual private server. Use Ubuntu 20.04 LTS as the operating system.



Choose the one with 1GB RAM and 25GB SSD storage for size. It should cost around $5 per month. Choose the closest region to your users. I am a Bangladeshi citizen, and my users are mostly from Bangladesh. Therefore, I deploy my applications in Singapore.



Create a new SSH key under the SSH Section. Copy the content from the ~/.ssh/id_rsa.pub file and paste it as the content. Put a descriptive name for the key and save.



You can also leave the rest of these options unaffected. Most of the providers come with an automatic backup service. For this demonstration, keep that option disabled. But in a real scenario, it can be a lifesaver. After the process is finished, you can connect via SSH to your new server.



Basic Setup



Now that your server is up and running, it is time to set up the basics. To log in as the root user, you will need to use SSH with your server IP address.



The server's IP address can be found on the dashboard or in the server details. Once you have access to the server, create a non-root account.



By default, all servers come with the root user. As you probably know, the root user has a lot of power. Hackers can cause havoc if they hack into your server and log in as root user. These mishaps can be prevented by disabling root user login.



Logging in using key-pairs is safer than using passwords. Users should therefore disable logging in using passwords.



To create a user from the terminal, run the following command within your server:



You can use the name nonroot for anything you want. I used nonroot as the name to make the fact clear that this is a non-root user. The adduser program will ask for a password and several other information. You can use a strong password, and leave the rest blank.



After creating the user you will need to add the new user to your sudo group. Otherwise, the nonroot user will be unable to execute commands using sudo.



In this command, sudo is the group name, and nonroot is the username. If you try to log into the account, you'll see a permission denied error.



This happens because most VPS providers don't allow login with a password after you add an SSH keys to the server. Also, you haven’t set up the new user to use SSH key pairs. You can fix this by copying the content of the directory /root/.ssh to the directory /home/nonroot/.ssh. This can be done with the rsync utility.



The --archive option allows you to rsync copy directories recursively, preserving symbolic links, user ownership and group ownership as well as timestamps. The --chown option sets the nonroot user as the owner in the destination. Now you should be able to log in as the new user using SSH.



After logging in as non-root, you will need to update the operating system. This includes all installed programs. To do this, run the following command.



The download and installation of the updates will take just a few seconds. If you are prompted to configure openssh-server and ask about file changes, choose the "keep local version currently installed” option and hit the enter key.



After the update process finishes, reboot the server by executing the sudo reboot command. Wait a few minutes for the server to boot again and log back in as a non-root user.



Deploying code on the server



Next, you will need to deploy the code on the server after you have completed the basic setups. I've seen people cloning the repository somewhere on the production server and logging into the server to perform a pull whenever there are some new changes to the code.



There is a better way. Instead of logging into the server to perform a pull, you can use the server itself as a repository and push code directly to the server. You can automate post-deployment tasks like installing dependencies and running migrations. But before doing all these, you'll first have to install PHP and Composer on the server.



Install PHP



You can find a list of PHP packages required by Laravel on the official docs. The following command will install all of these packages on your server:



Depending on if you're using MySQL, PostgreSQL and SQLite for your project, one of the following packages will be required:



The following package provides support for the Redis in-memory databases:



These packages are not the only ones you will need.



MySQL is used for its database system, and Redis is used to cache and run queues. You will need to install the php7.4 - mysql and php7.4 - redis packages.



You may need to install additional PHP packages depending on the project. For example, projects that require work images depend on the PHP-gd package. It doesn't matter if you mention the PHP version with each package name. APT will automatically install the latest version of PHP if you don’t specify it.



This article was written using PHP 7.4 as the most recent version of Ubuntu's package repositories. However, the question board project requires PHP 7.

PHP 4 and PHP 8 could become the default, I've indicated the version number in this article.

Installing a Composer



After installing PHP and all the required packages on the server, now you're ready to install Composer. To do this, go to the official composer page and follow the instructions on the command-line.



Now that PHP and Composer have been installed on your server you can configure automated deployment of your code.



Git for Deploying Code



For automating code deployment on the server, log in as a non-root user and create a new directory under the /home/nonroot directory. This directory will be used as the repository. You can also push production code to it.



Any non-existent parent repository can be created by adding the -p option in the mkdir command. Next, cd into this directory and create a new bare Git repository.



A bare is a git repository that doesn't contain a working tree. The practical usage of such a git repository is as a remote origin. Don't worry if you don't understand what I said just now. You will see things clearly as you keep going.



Assuming you're still inside the /home/nonroot/repo/question-board.git directory, cd inside the hooks subdirectory and create a new file called post-receive.



These files are regular shell scripts that git invokes whenever there is a major event in a repository. When you push code, it waits for all code to be received before calling the postreceive script.



Assuming you're still inside the hooks directory, open the post-receive script by executing the following command:



As you might have guessed, /sbin/deploy will be another script that you will need. The /sbin directory houses scripts that perform administrative tasks. Move over to the /sbin/deploy file and open it with nano text editor.



Now update the script content as follows



Evident by the #!/bin/sh line, this is a shell script. After that line, the only line of code in this script copies the content of the /home/nonroot/repo/question-board.git repository to the /srv/question-board directory.



Here, the --work-tree option specifies the destination directory, and the --git-dir option specifies the source repository. I like to use the /srv directory for storing files served by this server. If you prefer to use /var/www, go ahead.



Save the file using Ctrl+O and exit nano by pressing Ctrl+X key combination. The following command will verify that the script is executable:



To make this process fully functional, you must create the destination directory and the work tree. To do so, execute the following command:



Now you have the proper work tree directory and a bare repository. A post-hook calls the /sbin/deploy command with sudo. But, how does the post receive hook invoke the sudo /sbin/deployscript without a password?



Open the /etc/sudoers file on your server using the nano text editor and append the following line of code at the end of the file:



This line of code indicates that the nonroot user can execute the /sbin/deploy command with sudo on ALL hosts without password or NOPASSWD. Save the file by pressing Ctrl+O. Exit nano by pressing the Ctrl+K key combination.



Finally, you're ready to push the project source code. Assuming that you've already forked and cloned the https://github.com/fhsinchy/guide-to-deploying-laravel-on-vps repository on your local system, open up your terminal on the project root and execute the following command:



Make sure to replace my IP address with the IP address from your server. You can now push code to the server, assuming stable code is not the master.



After sending the code to the server, log back in as a non-root user and cd into the /srv/question-board directory. Use the ls command to list out the content, and you should see that git has successfully checked out your project code.



Automated Post-Deployment Steps



Congratulations on being able deploy Laravel project directly to the server. But is that enough! What about the post-deployment steps? Tasks like installing or updating dependencies, migrating the database, caching the views, configs, and routes, restarting workers, and so on.



Automating these tasks really is easier than you might think. All you have to do is create a program that automates these tasks, grant permissions, and then call that script within the post receipt hook.



In the /sbin directory, create another script called post deploy. After creating the file open it within the nano text editor.



Update the content of the post-deploy script as follows. Don't worry if you don't clearly understand everything. I'll go over each line in detail.



The first line changes the working directory to the /srv/question-board directory. The second line makes a copy of the .env.example file. The -n option ensures that the cp command does not override an already existing file.



The third and fourth commands will install all the necessary dependencies and update them if necessary. The COMPOSER_ALLOW_SUPERUSER environment variable disables a warning about running the composer binary as root.



Save the file by pressing Ctrl + O and exit nano by pressing Ctrl + X key combination. The following command will verify that the script is executable:



Open the /home/nonroot/repo/question-board.git/hooks/post-receive script with nano and append the following line after the sudo /sbin/deploy script call:



After calling the deploy script, make sure you call the post deployment script. Save the file by pressing Ctrl+ O, and exit nano using the Ctrl+ K key combination.



Use the nano text editor to open your /etc/sudoers files on your server. Once again, update the previously added line:



Save the file using Ctrl+O and exit nano with the Ctrl+K key combination. This script can be extended with additional post-deploy steps if needed.



To test the new post-deploy script, make some changes to your code, commit the changes and push to the production master branch. This will allow you to see progress in composer package installation on the terminal as well as outputs of other artisan calls.



Once the deployment process is completed, log back onto the server and cd in to the /srv/question board directory. Then, list the content by running the following command.



Among other files and folders, you'll see a newly created vendor directory and an env file. This is where you can generate the Laravel application encryption keys. The following command will be executed to do this:



You can see the APP_KEY value in the.env file if you use the nano text editor. It is populated with a long string.



Configuring NGINX



Once you have successfully pushed your source code to the server the next step is installing a web server to host your application. In this article I'll be using NGINX. If you need to use Apache, you will be on your own.



This article will only discuss configuring the webserver to serve a Laravel app. It will not cover NGINX-related stuff. NGINX is a complex software. The NGINX Handbook is a great resource if you want to learn more about NGINX.



The following command will install NGINX on Ubuntu Server:



This command should install NGINX and should also register as a systemd service. You can verify this command by running the following command



As you can see, the output should be as follows:



You can regain control of the terminal by hitting q on your keyboard. If you visit the server IP address, you will see NGINX's default welcome page.



You'll have to change the NGINX configuration to serve your Laravel application instead. To do so, create a new file /etc/nginx/sites-available/question-board and open the file using the nano text editor.



This file will contain NGINX configuration code to serve the question board application. Configuring NGINX from scratch can be difficult, but the official Laravel docs have a pretty good configuration. The following code is taken from the documentation:



This code doesn't need to be modified except the first two lines. Make sure to use the IP address from your server for the server_name, and that root points to correct directory. You'll replace this IP address with a domain name in a later section.



Also, inside the location ~ .php$ block, make sure that the fastcgi_pass directive is pointing to the correct PHP version. In this demonstration, I'm using PHP 7.4, so this configuration is correct. If you're using a different version, like 8.0 or 8.1, update the code accordingly.



If you cd into the /etc/nginx directory and list out the content using the ls command, you'll see two folders named sites-available and sites-enabled.



Sites-available contains all configuration files that serve applications (yes, you can have multiple) from this server.



Sites-enabled also contains symbolic links that point to the active configuration files. So if you do not make a symbolic link of the /etc/nginx/sites-available/question-board file inside the sites-enabled folder, it'll not work. The following command will do this:



To avoid unintended conflicts, the second command will delete the default configuration file.

To test if the configuration code is okay or not, execute the following command:

If everything is okay, you can reload NGINX by running the following command.



You can visit your server IP address to see NGINX is correctly serving your application, but the application is throwing an internal error 500.



As you can see the application tries to write to the logs directory but fails. This happens because the root user has the /srv/question board directory and the www-data user has the NGINX process. To make the /srv/question-board/storage directory writable by the application, you'll have to alter the directory permissions.



Configuring Directory Permissions



There are different ways of configuring directory permissions in a Laravel project but, I'll show you the one I use. First, you need to make sure that the www-data user who owns NGINX is also the owner for the /srv/question board directory. Execute the following command to do this:



Then, set the permission of the /srv/question-board/storage to 755, which means read and execute access for all users and write access for the owner by executing the following command:



The last subdirectory you must make writable is the /srv/question-board/bootstrap/cache directory. That is the /srv/question-board/bootstrap/cache directory. To do so, execute the following command:



You should be able to see the application working fine if you go back to the IP address of the server and refresh.



MySQL installation and configuration



Now that you have successfully installed and configured NGINX, it is time to install and configure MySQL. To do so, install the MySQL server by executing the following command:



After the installation process finishes, execute the following command to make your MySQL installation more secure:



First, the script asks if you want to use a validate password component. Enter "Y" and hit enter. Next, you'll need to adjust the password difficulty. Datchley.Name I recommend that you set it as high as possible. It can be frustrating to pick a difficult-to-guess password each time you create a new user, but it is necessary for security. In the next step, set a secure password for the root user. For the rest of these questions, you can answer with "Y". Give the questions a chance if possible.



Now, before you can log into your database server as root, you'll have to switch to the root user. To do so, execute the following command:



Log into your database server as root by executing the following command:



Once you're logged in, create new databases for the question board application using the following SQL code.



Next, create a new database user by executing the following SQL code:



I used the name "nonroot" to clarify that it is a non-root user. You can use any name you like. You can also replace the password word with something more secure.



After that, provide the user full privilege of the question_board database to the newly created user by executing the following SQL code:



This code uses the term question_board. * refers to all tables within the question_board data base. Finally, exit MySQL client by executing q and exit root shell by invoking exit



Now, try logging in as the nonroot user by executing the following command:



The MySQL client will require the password. Use the password you put in when creating the nonroot user. If you manage to log in successfully, exit the MySQL client by executing the q command.



Now that your database server is up and running, it's time configure the question-board project to make it work. First, navigate into the directory /srv/questionboard and open the Env file with the nano editor.



Change the database configuration as following:



Make sure to replace the username and password with yours. Save the file by pressing Ctrl+ O, and exit nano by pressing Ctrl+ X key combination. The following command will allow you to test the database connection.



If everything goes smoothly, it is a good sign that the database connection works. The project comes with two Seeder Classes, one to seed the admin user and the other for the categories. To run them, follow the following commands:



If you now visit the server IP address, navigate to the /questions route and you'll see the categories. You will also be logged in as the administrator user using these credentials:



If you've been using Laravel for a while you may be familiar with the practice that new migration files are added when there is a change to the database. To automate the process of running the migrations on every deployment, open the /sbin/post-deploy script using nano once again and append the following line at the end of the file:



The --force option will suppress an artisan warning about running migrations on a production environment. Seeders should be run only once, not like migrations. If you add new seeders on later deployments, you'll have to run them manually.



Configure Laravel Horizon



Laravel Horizon comes pre-installed with this question board project. Once Redis is running, you can begin processing jobs.



Official docs recommend using the supervisor program to run Laravel Horizon on a production machine. The following command will install the program:



Supervisor configuration files live within your server's /etc/supervisor/conf.d directory. Create a new file /etc/supervisor/conf.d/horizon.conf and open it using the nano text editor:



Update the file's content as follows:



Save the file by pressing Ctrl+ O. To exit nano, press the Ctrl+ X key combination. Execute the following commands to update your supervisor configuration and start the Horizon process:



To find out if Laravel Horizon has been running, visit your server's address and navigate the /login page. Log in as the admin user and navigate to the /horizon route. Laravel Horizon will now be in active.



Laravel Horizon has been configured to only allow the admin user in. This means that if you log into Laravel Horizon with any other credential, you'll get a 403 forbidden error message when you go to the /horizon route.



Many people are unaware that Laravel Horizon will need to be restarted if you make any changes to your job. I recommend adding a line to the /sbin/post-deploy script to reinitiate the Laravel Horizon process on every deployment.



To do so, open /sbin/post deploy using nano text editor and add the following line at end of file:



This command will stop and restart the Laravel Horizon process on every deployment.



Configuring a Domain name with HTTPS



This step will require you to have your own domain name. I'll use the questionboard.farhan.dev domain name for this demonstration.



Log in to your domain provider of choice and go into the DNS settings for domain names. Whenever you want a domain name to point to a server's IP address, you need to create a DNS record of type A.



Add the following attributes to a new DNS record.



Type: A Record



Host: questionboard



Value: 104.248.157.172



You must replace my IP address by yours. You can use a @ to point your top-level domain at an IP address.



Now go back to your server and open the /etc/nginx/sites-available/questionboard config file using the nano text editor. Now, remove the IP address in the server_name directive. Enter your domain name. Do not put HTTPS or HTTPS at first



You can put multiple domain names such as the top-level domain and the www subdomain separated by spaces. Save the configuration file with Ctrl+O and Ctrl+X key combinations. This command will allow you to restart NGINX configuration.



Now you can access your application with your domain name and not the IP address. You can use certbot to enable HTTPS on an application.



To do so, install certbot by executing the following command:



It is a python program that allows you to use free SSL certificates very easily. To obtain a new certificate, run the following command after installing the program:



First, the program will ask you for your email address. Next, it will ask you if the terms and conditions are acceptable to you.



Then, It'll ask you about sharing your email address with the Electronic Frontier Foundation.



The program will then read the NGINX configuration file, extract the domain names from server_name directive and proceed to the third step. Look at the domain names it shows and press enter if they are all correct. After deploying the new certificate, the program will congratulate you, and now you've got free HTTPS protection for 90 days.



After 90 days, the program will attempt automatically to renew the certificate. This command will test the auto renewal feature.



If the simulation succeeds, you're good to go.



Configuring a Firewall



A firewall properly configured is crucial for the security of your server. In this article, I will show how to configure the popular UFW software.



UFW is an uncomplicated firewall that comes as a default in Ubuntu. UFW is configured to allow all outgoing traffic to the server by default. To do this, run the following command.



All incoming traffic is blocked. This means that you and no one else will be able access your server.

Next, you will need to allow incoming messages in three ports. These are the following:

Port 80, used to send HTTP traffic.



Port 443, used to send HTTPS traffic.



Port 22 is used by SSH traffic.



To do so, follow the following commands:



Finally, enable UFW by executing the following command:



That's it. Your server now allows HTTP, HTTPS and SSH traffic to come from the outside. This makes it a bit more secure.



Laravel Post Deployment Optimizations



Your application is now almost ready to accept requests from all over the world. One last step that I would like to suggest is caching the Laravel configuration, views, and routes for better performance.



To do this, use the nano text editor to open the /sbin/postdeploy script and add the following lines to the end of the file.



Now, on every deployment, the caches will be cleared and renewed automatically. Also, ensure that you set the APP_ENV and APP_DEBUG settings to production in the env file. If you do not, sensitive information may be compromised.



I would like to express my gratitude to all Laravel developers that took the time and effort to read this article. I hope that you enjoyed the article and learned some useful tips about application deployment. If you want to learn more about NGINX, consider checking out my open-source NGINX Handbook with tons of fun content and examples.



If you are looking to increase your knowledge about Laravel, check out the Laravel Vs Symfony article, the Laravel Corecel article, and the Laravel Blockchain article.



If you have any questions or confusion, feel free to reach out to me. I'm available via Twitter and LinkedIn, and am always happy to help. Keep safe and keep learning.


Here's my website: https://datchley.name/
     
 
what is notes.io
 

Notes.io is a web-based application for taking notes. You can take your notes and share with others people. If you like taking long notes, notes.io is designed for you. To date, over 8,000,000,000 notes created and continuing...

With notes.io;

  • * You can take a note from anywhere and any device with internet connection.
  • * You can share the notes in social platforms (YouTube, Facebook, Twitter, instagram etc.).
  • * You can quickly share your contents without website, blog and e-mail.
  • * You don't need to create any Account to share a note. As you wish you can use quick, easy and best shortened notes with sms, websites, e-mail, or messaging services (WhatsApp, iMessage, Telegram, Signal).
  • * Notes.io has fabulous infrastructure design for a short link and allows you to share the note as an easy and understandable link.

Fast: Notes.io is built for speed and performance. You can take a notes quickly and browse your archive.

Easy: Notes.io doesn’t require installation. Just write and share note!

Short: Notes.io’s url just 8 character. You’ll get shorten link of your note when you want to share. (Ex: notes.io/q )

Free: Notes.io works for 12 years and has been free since the day it was started.


You immediately create your first note and start sharing with the ones you wish. If you want to contact us, you can use the following communication channels;


Email: [email protected]

Twitter: http://twitter.com/notesio

Instagram: http://instagram.com/notes.io

Facebook: http://facebook.com/notesio



Regards;
Notes.io Team

     
 
Shortened Note Link
 
 
Looding Image
 
     
 
Long File
 
 

For written notes was greater than 18KB Unable to shorten.

To be smaller than 18KB, please organize your notes, or sign in.