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
apt-get install apache2 -yYou can verify the installation with the following command:
apache2ctl -v
# result
Server version: Apache/2.4.48 (Debian)Install PHP and other required extensions
apt-get install apt-transport-https gnupg2 ca-certificates -yThen, we add the necessary repository to install php 8.0:
wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpgsh -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:
apt-get update -yapt-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-mysqlWe verify that php has installed correctly:
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 TechnologiesNow we proceed to edit the php.ini file.
nano /etc/php/8.0/apache2/php.iniWe 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.
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.pharNow we will move the file to the OS binaries so that we can execute it directly:
mv composer.phar /usr/local/bin/composerWe verify the installation:
composer --version
# result
Composer version 2.1.6 2021-08-19 17:11:08Install mysql/mariadb
We proceed to install mariadb as the database engine. In the terminal:
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:
mariadb --version
# result
mariadb Ver 15.1 Distrib 10.5.11-MariaDB, for debian-linux-gnu (x86_64) usingWe verify the service status:
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:
systemctl start mariadbNow we run the mariadb console as root to create a new database user:
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.
create user codalas@localhost identified by 'password';We give it all privileges:
GRANT ALL PRIVILEGES ON *.* TO 'codalas'@localhost IDENTIFIED BY 'password';We update the database privileges:
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:
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:
adduser codalasWe 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:
sudo apt updateWe install git:
sudo apt install gitWe verify the installation:
git --version
# result
git version 2.30.2Assuming 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.
usermod -a -G www-data codalasThen we change the ownership of all files in the project folder to codalas with the www-data group:
cd htmlchown -R codalas:www-data laravelWe update the permissions of the folders:
chmod -R 775 laravelOnce 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:
sudo -u codalas bashThis 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:
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=120Install system dependencies
We run composer install to install the system dependencies.
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:
php artisan config:cache
# result
Configuration cache cleared!
Configuration cached successfully!php artisan route:cache
# result
Route cache cleared!
Routes cached successfully!php artisan view:cache
# result
Compiled views cleared!
Blade templates cached successfully!Migrations
We run the migrations to create the tables in the database:
php artisan migrateConfigure apache
Using the root user, we will create a virtual host configuration for the application.
We create a new configuration file for laravel:
nano /etc/apache2/sites-available/laravel.confWe modify the file with the following content:
<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:
apachectl configtest
# result
Syntax OKWe enable the created virtual host and the rewrite module:
a2enmod rewritea2ensite laravel.confTip
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:
systemctl restart apache2Configure 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:
ufw status
# result
Status: active
To Action From
-- ------ ----
22 ALLOW AnywhereWe enable port 80:
ufw allow 80If we check the status again, the new port should be shown:
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.
sudo chown -R codalas:www-data storage
sudo chmod 775 -R storageWe generate the unique identifier used to generate authentication tokens.
php artisan key:generateWe refresh the application configuration.
php artisan config:cache
# result
Configuration cache cleared!
Configuration cached successfully!php artisan route:cache
# result
Route cache cleared!
Routes cached successfully!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

