In this guide, we’ll learn how to deploy a Laravel application to an Ubuntu server running Nginx.
Requirements:
- A Laravel application that exists in a repository on Github.com
- A web server running Nginx with SSH access
- A domain or subdomain configured to point to that server
- Your web server needs to have SSH keys set up with Github (Ref: SSH Keys and Github)
Server Requirements
Before beginning the deployment, we need to check that our server meets all the requirements necessary to run Laravel, referencing the Laravel Server Requirement docs.
You can confirm your PHP version meets the minimum version requirement by creating and running a page on your server that invokes the phpinfo()
function.
If your PHP is out of date, follow this guide: Upgrading PHP.
Next, you need you make sure you have the required PHP extensions. Run php -m
to output a list of the PHP extensions currently installed on your server. Example output:
> php -m
[PHP Modules]
bcmath
bz2
calendar
Core
ctype
curl
date
dba
dom
exif
fileinfo
filter
ftp
gd
gettext
hash
iconv
imap
json
ldap
libxml
mbstring
mysqli
mysqlnd
openssl
pcntl
pcre
PDO
pdo_mysql
pdo_pgsql
pdo_sqlite
Phar
posix
Reflection
session
shmop
SimpleXML
soap
sockets
SPL
sqlite3
standard
sysvsem
sysvshm
tokenizer
xml
xmlreader
xmlwriter
xsl
zip
zlib
[Zend Modules]
If I cross check the above output from my server with the list of extensions listed on Laravel’s server requirements docs (ref), I can see I’m missing the DOM and XML extensions:
Laravel PHP extension requirements:
- ✅ Ctype PHP Extension
- ✅ cURL PHP Extension
- ❌ DOM PHP Extension
- ✅ Fileinfo PHP Extension
- ✅ Filter PHP Extension
- ✅ Hash PHP Extension
- ✅ Mbstring PHP Extension
- ✅ OpenSSL PHP Extension
- ✅ PCRE PHP Extension
- ✅ PDO PHP Extension
- ✅ Session PHP Extension
- ✅ Tokenizer PHP Extension
- ❌ XML PHP Extension
Additionally, I want to add the zip and unzip extensions Composer will use when downloading my dependencies, and the MySQL PHP extension since that’s the database type I’m using in my application.
I can accomplish this using apt, a command line utility for managing packages on Linux systems.
First - I’ll specify a new repository apt can download software packages from. The repository we’re adding is ppa:ondrej/php, the primary source for PHP-related packages.
> sudo add-apt-repository ppa:ondrej/php
Next, invoke apt to get the latest package lists from your apt repositories:
> sudo apt update
Finally, we can get the necessary extensions. In my case, the command to do that looks like the following.
> sudo apt install php8.2-xml php8.2-dom php8.2-mysql zip unzip
Note that PHP-specific extensions are prefixed with php
and the appropriate PHP version number. E.g. the XML PHP Extension is listed as php8.2-xml
. The extensions zip
and unzip
are not PHP-specific extensions, so no prefix is used.
Get Composer
We need Composer in order to manage the dependencies in our Laravel project. If you invoke the command composer
on your server and it tells you the command is not found, you’ll need to install Composer via the following instructions.
Move into your /usr/bin
directory, a common location to put command line executable programs Linux servers:
> cd /usr/bin
Within this directory, run the following command to download the Composer installer and run it using php:
> curl -sS https://getcomposer.org/installer | sudo php
The resulting program (composer.phar
) has a .phar
(PHP Archive) extension. We can shorten this by renaming it to just composer:
> sudo mv composer.phar composer
Now we have a simple, terse command to invoke Composer. Test it out:
> composer
/ ____/___ ____ ___ ____ ____ ________ _____
/ / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
/_/
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
[...etc...]
Get the code base
Now that our server is prepped to run Laravel, we can get a copy of our application on the server by cloning it from Github. In my example, I will clone it to my web directory at /var/www/
> cd /var/www/
> git clone git@github.com:susanBuck/demo.git
Once this is done, I can move into the resulting directory:
> cd demo
Get dependencies
Next, we need to pull in the application’s Composer dependencies (i.e. our vendor/
directory).
If working on a production server, use the following command so any development-specific dependencies are excluded and the version number of your dependencies match whatever was used in development and written to composer.lock
:
> composer install --optimize-autoloader --no-dev
If working on a development server, use the following command so all dependencies are included and you get the latest versions within your version constraints listed in your composer.json
file:
> composer update
With the above step complete, your project should now have a vendor/
directory within your project.
Build .env file
Every Laravel application needs a .env
file with environment-specific configurations. Because the contents of this file are going to differ from environment to environment, it is not tracked as part of your version control repository (it’s ignored via the .gitignore
config file) and therefor you have to manually create it whenever setting up the application in a new environment.
To do this, you can copy the provided .env.example
file and update to as appropriate:
> cp .env.example .env
Run the following command to generate the APP_KEY
value within your .env file:
> php artisan key:generate
Set permissions
There are two directories within a Laravel application that need to be writable by the server: storage
and bootstrap/cache
. Within these directories, the server will write application-specific files such as cache info, session data, error logs, etc.
To allow this to happen, you need to update the permissions of storage
and bootstrap/cache
so they are owned by the system user your web server is running as.
Run the following command to see which user your Nginx web server runs as:
> ps aux | grep "nginx: worker process" | awk '{print $1}' | grep -v root
On my server, the above command outputs the user www-data
so I will update storage
and bootstrap/cache
to be owned by www-data
with the following two commands:
> chown -R www-data storage
> chown -R www-data bootstrap/cache
Configure site
At this point, everything is set up within our application, we just need to configure our server to run it.
Within /etc/nginx/sites-available/
create a new file with the following content (ref: Nginx deployment). You can call the file whatever you want; I’ll name mine demo
after the name of the app.
Within the content update the following:
-
server_name
should point to the domain (or subdomain) you’re using for this application -
root
should point to the path of your Laravel application’spublic
directory - The reference to
php8.2-fpm.sock
should match whatever version of PHP your server is running
server {
listen 80;
listen [::]:80;
server_name demo.com;
root /var/www/demo/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index index.php;
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/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Next, to enable this config we need to symbolically link the file to the /etc/nginx/sites-enabled
directory.
To do this, run the following command, replacing demo
with the name of the file you created:
> sudo ln -s /etc/nginx/sites-available/demo /etc/nginx/sites-enabled
With that complete, run the command sudo nginx -t
to check your configs making sure there are no issues:
> sudo nginx -t
Expected output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
If all looks good, restart Nginx to make the changes take effect:
> systemctl restart nginx
Test it
Load your application in the browser using the domain (or subdomain) you configured to make sure everything is working as expected. If your application fails to load, check out my guide Common Laravel Installation Issues.