Skip to content

How to install Laravel on a VPS with Debian

To begin the installation, you must have:

  • A server with Debian 11 Operating System.
  • Access to the server with a user with permissions to install and update packages.

Install apache

sh
apt-get install apache2 -y

You can verify the installation with the following command:

sh
apache2ctl -v

# result
Server version: Apache/2.4.48 (Debian)

Install PHP and other required extensions

sh
apt-get install apt-transport-https gnupg2 ca-certificates -y

Then, we add the necessary repository to install php 8.0:

sh
wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
sh
sh -c 'echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'

Then we update the repository and install php and other extensions:

sh
apt-get update -y
sh
apt-get install libapache2-mod-php php php-common php-xml php-gd php8.0-opcache php-mbstring php-tokenizer php-json php-bcmath php-zip php-curl unzip curl -y php-mysql

We verify that php has installed correctly:

sh
php --version

# result 
PHP 8.0.11 (cli) (built: Sep 23 2021 22:04:05) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.11, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.11, Copyright (c), by Zend Technologies

Now we proceed to edit the php.ini file.

sh
nano /etc/php/8.0/apache2/php.ini

We remove the semicolon from the following extensions to enable them:

  • extension=pdo_mysql

Install composer

Now we will install composer to be able to add the laravel dependencies.

sh
curl -sS https://getcomposer.org/installer | php

# result
All settings correct for using Composer
Downloading...

Composer (version 2.1.6) successfully installed to: /root/composer.phar
Use it: php composer.phar

Now we will move the file to the OS binaries so that we can execute it directly:

sh
mv composer.phar /usr/local/bin/composer

We verify the installation:

sh
composer --version

# result
Composer version 2.1.6 2021-08-19 17:11:08

Install mysql/mariadb

We proceed to install mariadb as the database engine. In the terminal:

sh
apt install mariadb-server

# result 
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done 
The following additional packages will be installed:
  galera-4 gawk libconfig-inifiles-perl libdbi-perl libmariadb3 libmpfr6 libsigsegv2 libsnappy1v5 mariadb-client-10.5 mariadb-client-core-10.5 mariadb-common
  ...

We can verify the installed version with the following command:

sh
mariadb --version

# result
mariadb Ver 15.1 Distrib 10.5.11-MariaDB, for debian-linux-gnu (x86_64) using

We verify the service status:

sh
systemctl status mariadb

# result
 mariadb.service - MariaDB 10.5.11 database server
     Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)       
     Active: active (running) since Tue 2021-08-17 23:38:11 CEST; 21min ago
       Docs: man:mariadbd(8)
             https://mariadb.com/kb/en/library/systemd/
     ...

If for some reason the database is not running, start it with the command:

sh
systemctl start mariadb

Now we run the mariadb console as root to create a new database user:

sh
mysql -u root -p 

# result
password:
# Press ENTER as we have not set a password for the root user.

We create a new user, in this case we call it codalas since it will be the user intended to access the database of the system.

sh
create user codalas@localhost identified by 'password';

We give it all privileges:

sh
GRANT ALL PRIVILEGES ON *.* TO 'codalas'@localhost IDENTIFIED BY 'password';

We update the database privileges:

sh
FLUSH PRIVILEGES;

Warning

For production systems it is recommended to give only the necessary privileges.

Now we create the database that we will use for the system:

sh
CREATE DATABASE laravel;

Create a non-root user to manage the application (if one does not already exist)

In the operating system console, we will create a non-root user, for example called codalas, using the following command:

sh
adduser codalas

We fill in the information requested such as password, name, etc. The objective of creating a non-root user is to be able to install the laravel dependencies securely.

Install git (optional)

We install git depending on the environment to be able to clone and keep the project up to date with changes during the development stage.

We update the OS package index:

sh
sudo apt update

We install git:

sh
sudo apt install git

We verify the installation:

sh
git --version

# result  
git version 2.30.2

Assuming the project was cloned to the /var/www/html/ folder, we will use laravel as an example folder, we proceed to manage the permissions of the folders:

We add the non-root user to the www-data user group of the server. This is in order to be able to execute commands within the project without interfering with the ownership permissions that the server needs over the vendor, storage and bootstrap folders.

sh
usermod -a -G www-data codalas

Then we change the ownership of all files in the project folder to codalas with the www-data group:

sh
cd html
sh
chown -R codalas:www-data laravel

We update the permissions of the folders:

sh
chmod -R 775 laravel

Once we complete this, we can proceed to perform the rest of the functions with the newly created user. This can be logging in with the new user or from the root user, executing commands on behalf of the non-root user.

As the root user, we can execute commands on behalf of the created user using:

sh
sudo -u codalas bash

This will start a terminal as the codalas user.

We proceed to create the file with the environment variables:

We create an .env file with the following information:

sh
APP_NAME="Laravel"
APP_ENV=local  
APP_DEBUG=true
APP_URL=https://localhost
APP_KEY=

LOG_CHANNEL=stack
LOG_LEVEL=debug

DB_CONNECTION=mysql  
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=codalas
DB_PASSWORD=password

MAIL_MAILER=smtp
MAIL_FROM_ADDRESS=
MAIL_FROM_NAME=

MAIL_HOST=
MAIL_PORT=587  
MAIL_ENCRYPTION=tls
MAIL_USERNAME=
MAIL_PASSWORD=

BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120

Install system dependencies

We run composer install to install the system dependencies.

sh
composer install

# result
Installing dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Package operations: 117 installs, 0 updates, 0 removals
  - Installing doctrine/inflector (2.0.3): Extracting archive  
  - Installing doctrine/lexer (1.2.1): Extracting archive
  - Installing symfony/polyfill-ctype (v1.23.0): Extracting archive
  - Installing webmozart/assert (1.10.0): Extracting archive
  - Installing dragonmantank/cron-expression (v3.1.0): Extracting archive
  - Installing symfony/polyfill-php80 (v1.23.1): Extracting archive
  - Installing symfony/polyfill-mbstring (v1.23.1): Extracting archive  
  ...

Refresh the configuration

We proceed to refresh the application cache:

sh
php artisan config:cache

# result
Configuration cache cleared!
Configuration cached successfully!
sh
php artisan route:cache

# result  
Route cache cleared!
Routes cached successfully!
sh
php artisan view:cache

# result
Compiled views cleared!
Blade templates cached successfully!

Migrations

We run the migrations to create the tables in the database:

sh
php artisan migrate

Configure apache

Using the root user, we will create a virtual host configuration for the application.

We create a new configuration file for laravel:

sh
nano /etc/apache2/sites-available/laravel.conf

We modify the file with the following content:

sh
<VirtualHost *:80>
    ServerName http://localhost

    ServerAdmin example@example.com
    DocumentRoot /var/www/html/laravel/public

    <Directory /var/www/html/laravel>
      Options +Indexes +MultiViews
      AllowOverride None
      Require all granted
    </Directory>

    <Directory /var/www/html/laravel/public>
      Options +Indexes +MultiViews 
      AllowOverride None
      Require all granted

      <IfModule mod_rewrite.c>
          RewriteEngine On

          # Handle Authorization Header
          RewriteCond %{HTTP:Authorization} .
          RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

          # Redirect Trailing Slashes If Not A Folder...
          RewriteCond %{REQUEST_FILENAME} !-d
          RewriteCond %{REQUEST_URI} (.+)/$
          RewriteRule ^ %1 [L,R=301]

          # Send Requests To Front Controller...
          RewriteCond %{REQUEST_FILENAME} !-d
          RewriteCond %{REQUEST_FILENAME} !-f
          RewriteRule ^ index.php [L]
      </IfModule>
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Tip

Update the ServerName, ServerAdmin, DocumentRoot and Directory properties according to the server configuration.

We check that the configuration is correct:

sh
apachectl configtest

# result
Syntax OK

We enable the created virtual host and the rewrite module:

sh
a2enmod rewrite
sh
a2ensite laravel.conf

Tip

The console will indicate that you must restart the server after each command for it to take effect. You can restart the server after executing both.

We restart the server:

sh
systemctl restart apache2

Configure firewall

By default, the server only has port 22 open, which is used for ssh connection. Since the application runs on port 80, we must open it so that the application is externally accessible.

We verify the firewall status:

sh
ufw status

# result 
Status: active

To                         Action      From 
--                         ------      ----
22                         ALLOW       Anywhere

We enable port 80:

sh
ufw allow 80

If we check the status again, the new port should be shown:

sh
ufw status

# Result
Status: active

To                         Action      From
--                         ------      ----
22                         ALLOW       Anywhere
80                         ALLOW       Anywhere  
80 (v6)                    ALLOW       Anywhere (v6)

Final steps

Then, execute the following commands in the root of the project to update the permissions of the folders.

sh
sudo chown -R codalas:www-data storage

sudo chmod 775 -R storage

We generate the unique identifier used to generate authentication tokens.

sh
php artisan key:generate

We refresh the application configuration.

sh
php artisan config:cache

# result
Configuration cache cleared!
Configuration cached successfully!
sh
php artisan route:cache  

# result
Route cache cleared!
Routes cached successfully!
sh
php artisan view:cache

# result 
Compiled views cleared!
Blade templates cached successfully!

We access the server address on port 80, either by IP or if it has a URL assigned. Example http://localhost