Deploying a Laravel application into a production environment requires a robust, secure, and performant server stack. The combination of Nginx as the web server and PHP-FPM for processing provides a highly efficient foundation for modern PHP applications. This guide offers a comprehensive, step-by-step walkthrough for installing and configuring a production-ready Laravel 11 application on a server running Ubuntu 24.04. We will build a complete LEMP stack (Linux, Nginx, MySQL, PHP), securing it with proper file permissions, creating a dedicated database user, configuring Nginx server blocks for optimal routing, and enabling SSL encryption with Let's Encrypt. By the end of this tutorial, you will have a scalable and secure environment ready to host your Laravel project, built according to industry best practices.

Prerequisites

Before you start this tutorial, you need to have a clean server instance running Ubuntu 24.04. All commands in this guide assume you are operating from this baseline. You will also need the following fully configured:

  • A non-root user with sudo privileges: All commands should be executed as a regular user with `sudo` permissions. Running commands directly as the `root` user is a security risk that can lead to accidental, irreversible system damage.
  • A fully configured LEMP stack: This tutorial focuses on deploying Laravel within an existing LEMP (Linux, Nginx, MySQL, PHP) environment. You must have Nginx, MySQL 8 (or a compatible alternative), and PHP 8.3 installed and running.
  • Composer installed globally: Composer is the dependency manager for PHP, and Laravel relies on it for installation and package management. It must be installed and available in your system's PATH.
  • A domain name: For a production setup, you should have a fully qualified domain name (FQDN) with its DNS 'A' record pointing to your server’s public IP address. This is required for generating a valid SSL certificate.

This guide will walk you through every step, but it assumes you are starting from a properly provisioned virtual private server (VPS). If any of the prerequisite components are missing, you must install and configure them before proceeding.

Step 1: Installing PHP 8.3 and Essential Extensions

Laravel 11 requires a minimum of PHP 8.2. To ensure long-term support and access to the latest performance improvements, this guide uses PHP 8.3, which is available directly from Ubuntu 24.04's default package repositories. Alongside the core PHP-FPM processor, Laravel requires a specific set of PHP extensions to handle database connections, XML parsing, string manipulation, and other critical tasks. Without these extensions, the installation will fail or the framework will not function correctly.

First, update your server's package index to ensure you are fetching the latest available versions:

sudo apt update

Now, install PHP-FPM and the necessary extensions. Each extension serves a specific purpose:

  • php8.3-fpm: The FastCGI Process Manager, which is responsible for executing PHP code.
  • php8.3-mysql: Allows PHP to connect to MySQL databases.
  • php8.3-mbstring: Provides functions for handling multi-byte strings, essential for UTF-8 support.
  • php8.3-xml: Required for XML manipulation, used by many Composer packages.
  • php8.3-bcmath: Provides support for arbitrary precision mathematics, used by libraries like Laravel's cashier.
  • php8.3-curl: Allows PHP to make HTTP requests, used by Guzzle and Laravel's HTTP client.
  • php8.3-zip: Enables handling of .zip archives.
  • php8.3-gd: An image processing library used for tasks like resizing avatars.
  • php8.3-intl: Provides internationalization functions.
sudo apt install php8.3-fpm php8.3-mysql php8.3-mbstring php8.3-xml php8.3-bcmath php8.3-curl php8.3-zip php8.3-gd php8.3-intl

Once the installation is complete, verify that the correct version of PHP is active by running:

php -v

The output should confirm that you are running PHP 8.3.x. The move to PHP 8.3 offers significant advantages over older versions, with some benchmarks showing performance gains of up to 50% compared to PHP 7.4, thanks to optimizations in the JIT (Just-In-Time) compiler and more efficient memory handling.

Step 2: Securing a Dedicated Database for Laravel

For any production application, creating a dedicated database and user is a non-negotiable security measure. This adheres to the principle of least privilege, which dictates that an application should only have access to the resources absolutely necessary for its function. This prevents a potential SQL injection vulnerability in your Laravel app from affecting other databases on the same server.

First, log in to the MySQL command-line interface as the root user:

sudo mysql

Next, create a new database for your application. For this guide, we will name it `laravel_app`. Using the `utf8mb4` character set is the current standard, as it provides full support for Unicode, including emojis, which is essential for modern web applications.

CREATE DATABASE laravel_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Now, create a new, dedicated MySQL user. We will name this user `laravel_user`. Crucially, we will restrict this user to connecting only from `localhost`. This is a vital security hardening step that prevents any attempts to connect to your database from a remote location using these credentials. Replace `your_secure_password` with a strong, randomly generated password.

CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'your_secure_password';

With the user created, grant it full privileges, but *only* for the `laravel_app` database. This user will have no access to any other databases on the server.

GRANT ALL PRIVILEGES ON laravel_app.* TO 'laravel_user'@'localhost';

Finally, flush the MySQL privileges to ensure the changes are applied immediately, and then exit the client.

FLUSH PRIVILEGES;
EXIT;

Step 3: Installing and Configuring the Laravel Project

With the server's stack fully prepared, you can now install the Laravel framework itself. We will use Composer to create a new project. The standard location for web applications on Ubuntu is within the `/var/www` directory.

Navigate to this directory and execute the `composer create-project` command. This will create a new directory named `laravel_app`, download the latest stable version of Laravel, and install all of its required third-party dependencies.

cd /var/www
composer create-project laravel/laravel laravel_app

Once Composer finishes, navigate into the newly created project directory.

cd laravel_app

Your next critical task is to configure the application's environment. Laravel uses a special file named `.env` to store environment-specific variables, such as database credentials and API keys. This allows you to keep sensitive information separate from your version-controlled codebase. Open the `.env` file for editing:

nano .env

Locate the `DB_` section and update the values to match the database name, username, and password you created in the previous step.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_app
DB_USERNAME=laravel_user
DB_PASSWORD=your_secure_password

While this file is open, you must make two other crucial changes for a production environment. Set `APP_ENV` to `production` and, most importantly, set `APP_DEBUG` to `false`. Leaving debug mode enabled in production is a severe security vulnerability, as it can expose sensitive configuration data and stack traces to the public in the event of an error. Also, set `APP_URL` to your domain.

APP_ENV=production
APP_DEBUG=false
APP_URL=http://your_domain.com

Save and close the file. Your Laravel application is now installed and configured to communicate with its database.

Step 4: Configuring Nginx and Setting Correct File Permissions

This is the most critical step for ensuring your application is both secure and functional. It involves two parts: setting the correct file system permissions and creating an Nginx server block to properly route web traffic.

Setting File Permissions

The web server (Nginx) runs as a specific system user, which on Ubuntu is `www-data`. For Nginx to be able to read the application files and for Laravel to be able to write to its cache and log directories, this `www-data` user needs to have appropriate ownership and permissions.

First, change the ownership of the entire project directory to the `www-data` user and group.

sudo chown -R www-data:www-data /var/www/laravel_app

Next, set the standard permissions. A permission of `755` for directories allows the owner (www-data) to read, write, and execute, while the group and others can only read and execute. A permission of `644` for files allows the owner to read and write, while others can only read. This is a secure default.

sudo chmod -R 755 /var/www/laravel_app

However, Laravel needs to write to certain directories. The `storage` directory (for logs, sessions, and cached views) and the `bootstrap/cache` directory (for compiled service files) need more permissive settings. A permission of `775` allows the owner and the group to write to these directories.

sudo chmod -R 775 /var/www/laravel_app/storage
sudo chmod -R 775 /var/www/laravel_app/bootstrap/cache

Creating the Nginx Server Block

An Nginx server block (similar to a virtual host in Apache) tells Nginx how to handle incoming requests for a specific domain. We will create a new configuration file for our Laravel application.

sudo nano /etc/nginx/sites-available/laravel_app

Paste the following production-ready configuration into the file. Below the code block, we will break down what each directive does.

server {
    listen 80;
    server_name your_domain.com;
    root /var/www/laravel_app/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    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.3-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

Configuration Breakdown:

  • `listen 80;`: Tells Nginx to listen for incoming connections on port 80 (HTTP).
  • `server_name your_domain.com;`: Specifies which domain this server block should respond to.
  • `root /var/www/laravel_app/public;`: Sets the document root. This is critical; all requests should be directed to Laravel's `public` directory, never the project root.
  • `add_header ...`: These lines add HTTP security headers to help mitigate attacks like clickjacking and cross-site scripting (XSS).
  • `location / { ... }`: This is the main location block. The `try_files` directive is the key to Laravel's routing. It tells Nginx to first look for a file matching the request URI, then a directory. If neither exists, it passes the request to `index.php`, allowing Laravel's router to handle it.
  • `location ~ \.php$ { ... }`: This block handles all requests that end in `.php`. It passes the request to the PHP-FPM process via a Unix socket for execution.
  • `location ~ /\.(?!well-known).*`: This is a security rule that denies public access to all hidden files (like `.env`) except for the `.well-known` directory used by Let's Encrypt for domain validation.

Now, enable the new configuration by creating a symbolic link from `sites-available` to `sites-enabled`. Then, run a syntax test to ensure there are no errors.

sudo ln -s /etc/nginx/sites-available/laravel_app /etc/nginx/sites-enabled/
sudo nginx -t

If the test returns "syntax is ok", you can safely reload Nginx to apply all the changes.

sudo systemctl reload nginx

Step 5: Securing Your Application with a Let's Encrypt SSL Certificate

In the modern web, serving your application over an unencrypted HTTP connection is unacceptable. It exposes user data and damages trust. We will use Certbot and Let's Encrypt to obtain and install a free, trusted SSL certificate, enabling HTTPS.

First, install Certbot and its Nginx plugin:

sudo apt install certbot python3-certbot-nginx

Now, run Certbot, specifying the Nginx plugin and the domain you want to secure. Certbot will automatically read your Nginx configuration file to find the correct server block.

sudo certbot --nginx -d your_domain.com

Certbot will guide you through an interactive process. You will be asked to provide an email address for renewal notices and agree to the terms of service. When prompted to redirect HTTP traffic to HTTPS, you should always choose the "Redirect" option. This ensures all visitors use a secure connection. Upon completion, Certbot will have automatically modified your Nginx configuration to enable SSL and will set up a systemd timer or cron job to automatically renew the certificate before it expires.

Step 6: Final Production Optimizations

Before serving traffic, you should run Laravel's built-in optimization commands. On each request, Laravel normally has to read multiple configuration files and parse route files to figure out how to respond. The optimization commands compile all of these files into a few single, cached files, which dramatically reduces the framework's overhead and improves response times.

cd /var/www/laravel_app
php artisan config:cache
php artisan route:cache
php artisan view:cache

It is also wise to tune your PHP-FPM process manager based on your server's resources. The default settings are often too conservative. Edit the `www.conf` pool file:

sudo nano /etc/php/8.3/fpm/pool.d/www.conf

For a server with 2GB of RAM, adjusting the `pm` (process manager) settings to `dynamic` and increasing the number of child processes can provide a good balance between memory usage and request handling capacity. The following is a reasonable starting point:

pm = dynamic
pm.max_children = 25
pm.start_servers = 8
pm.min_spare_servers = 5
pm.max_spare_servers = 15

After saving the changes, restart the PHP-FPM service for them to take effect.

sudo systemctl restart php8.3-fpm

At this point, your Laravel application should be live and accessible at your domain. You have successfully deployed a secure, performant, and scalable application environment.

Troubleshooting Common Deployment Errors

Deployments can be complex, and small typos or misconfigurations can lead to errors. Here are solutions to the most common issues encountered during this process.

502 Bad Gateway

This is the most frequent error. It signifies that Nginx is running but is unable to get a response from the PHP-FPM backend. The most common causes are:

  • The `php8.3-fpm` service is not running. Check its status with `systemctl status php8.3-fpm`.
  • The socket path in your Nginx config (`fastcgi_pass`) does not match the actual socket file created by PHP-FPM. Verify the path in `/var/run/php/`.

Permission Denied / Errors Writing to `laravel.log`

If your application loads but you see errors in your log files (or the log file cannot be created), it is almost certainly a file permissions problem. Laravel, running as the `www-data` user, does not have permission to write to the `storage` or `bootstrap/cache` directories. Re-run the `chown` and `chmod` commands from Step 4 to fix this.

404 Not Found on All Routes Except the Homepage

This classic symptom indicates that Nginx is not correctly passing requests for sub-pages to Laravel's router. The problem lies in the `try_files` directive within the `location /` block of your Nginx configuration. Ensure it is written exactly as `try_files $uri $uri/ /index.php?$query_string;`. This tells Nginx to hand off any URI that doesn't match a physical file or directory to Laravel's front controller.

Source & Attribution

This article is based on original data belonging to ENGINYRING.COM blog. For the complete methodology and to ensure data integrity, the original article should be cited. The canonical source is available at: How to Install and Configure Laravel with Nginx on Ubuntu 24.04: The Definitive Guide.