DEV Community

Cover image for Deploying Laravel with Nginx on Ubuntu 24.04
Sanskriti Harmukh for Vultr

Posted on with Aashish Chaurasiya Originally published at docs.vultr.com

Deploying Laravel with Nginx on Ubuntu 24.04

Laravel is a popular PHP framework with routing, authentication, and database management built in. This guide deploys a Laravel app behind Nginx on Ubuntu 24.04, wires it to MySQL, secures it with Let's Encrypt, and builds a small dashboard that queries live data.

Prerequisites: Ubuntu 24.04 with Nginx and MySQL installed, a non-root sudo user, a domain A record (e.g. app.example.com).


Create the Database

$ sudo mysql
Enter fullscreen mode Exit fullscreen mode
mysql> CREATE DATABASE laravel_demo;
mysql> CREATE USER 'laravel_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'secure_password';
mysql> GRANT ALL ON laravel_demo.* TO 'laravel_user'@'localhost';
mysql> FLUSH PRIVILEGES;
mysql> EXIT;
Enter fullscreen mode Exit fullscreen mode

Seed a demo table to query later:

$ mysql -u laravel_user -p
Enter fullscreen mode Exit fullscreen mode
mysql> USE laravel_demo;
mysql> CREATE TABLE server_stats (
            id INT AUTO_INCREMENT,
            server_name VARCHAR(255),
            region VARCHAR(255),
            cpu_usage DECIMAL(5,2),
            memory_usage DECIMAL(5,2),
            status ENUM('active', 'maintenance', 'offline'),
            last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY(id)
        );
mysql> INSERT INTO server_stats (
           server_name, region, cpu_usage, memory_usage, status
       )
       VALUES
           ('app-01', 'us-east', 24.50, 45.30, 'active'),
           ('db-01',  'eu-west', 12.75, 78.20, 'active'),
           ('web-01', 'ap-south', 65.80, 89.50, 'active');
mysql> EXIT;
Enter fullscreen mode Exit fullscreen mode

Install Composer and PHP Extensions

$ sudo apt update
$ sudo apt install composer php php-curl php-fpm php-bcmath php-json php-mysql php-mbstring php-xml php-tokenizer php-zip -y
$ composer --version
$ php --version
$ sudo systemctl restart php8.3-fpm
Enter fullscreen mode Exit fullscreen mode

php-fpm runs PHP as a service Nginx can talk to; php-mysql/php-mbstring/php-xml/php-tokenizer/php-zip cover Laravel's runtime requirements.


Create the Laravel Project

$ cd ~
$ composer create-project --prefer-dist laravel/laravel laravel-demo
$ cd laravel-demo
$ php artisan key:generate
Enter fullscreen mode Exit fullscreen mode

Edit .env:

$ nano .env
Enter fullscreen mode Exit fullscreen mode
APP_NAME=laravel-demo
APP_ENV=development
APP_KEY=base64:APPLICATION_UNIQUE_KEY
APP_DEBUG=true
APP_URL=http://app.example.com

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_demo
DB_USERNAME=laravel_user
DB_PASSWORD=secure_password

SESSION_DRIVER=file
Enter fullscreen mode Exit fullscreen mode

Move it to the web root and set permissions:

$ sudo mv ~/laravel-demo /var/www/laravel-demo
$ sudo chown -R www-data:www-data /var/www/laravel-demo
$ sudo chmod -R 755 /var/www/laravel-demo/storage
$ sudo chmod -R 755 /var/www/laravel-demo/bootstrap/cache
$ cd /var/www/laravel-demo
$ php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Configure Nginx

$ sudo nano /etc/nginx/sites-available/laravel-demo.conf
Enter fullscreen mode Exit fullscreen mode
server {
    listen 80;
    server_name app.example.com;
    root /var/www/laravel-demo/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; }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
Enter fullscreen mode Exit fullscreen mode
$ sudo ln -s /etc/nginx/sites-available/laravel-demo.conf /etc/nginx/sites-enabled/
$ sudo nginx -t
$ sudo systemctl restart nginx
$ sudo ufw allow 80/tcp
$ sudo ufw reload
Enter fullscreen mode Exit fullscreen mode

Visit http://app.example.com — you should see the default Laravel welcome page.


Secure with Let's Encrypt

$ sudo apt install certbot python3-certbot-nginx -y
$ sudo certbot --nginx --redirect -d app.example.com -m admin@app.example.com --agree-tos
$ sudo certbot renew --dry-run
$ sudo systemctl restart nginx
$ sudo ufw allow 443/tcp
$ sudo ufw reload
Enter fullscreen mode Exit fullscreen mode

Build a Demo Dashboard

Replace the default route to query server_stats and render it.

$ sudo mv /var/www/laravel-demo/routes/web.php /var/www/laravel-demo/routes/web.php.ORIG
$ sudo nano /var/www/laravel-demo/routes/web.php
Enter fullscreen mode Exit fullscreen mode
<?php

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    $servers = DB::table('server_stats')
                ->orderBy('status')
                ->orderBy('cpu_usage', 'desc')
                ->get();

    return view('dashboard', ['servers' => $servers]);
});
Enter fullscreen mode Exit fullscreen mode

Create the Blade view:

$ sudo nano /var/www/laravel-demo/resources/views/dashboard.blade.php
Enter fullscreen mode Exit fullscreen mode
<!DOCTYPE html>
<html>
<head>
    <title>Server Dashboard</title>
    <style>
        body { font-family: 'Segoe UI', sans-serif; margin: 2rem; }
        .server-card {
            padding: 1rem;
            margin-bottom: 1rem;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .active {
            border-left: 4px solid #10B981;
            background: #F0FDF4;
        }
        .maintenance {
            border-left: 4px solid #F59E0B;
            background: #FFFBEB;
        }
        .offline {
            border-left: 4px solid #EF4444;
            background: #FEF2F2;
        }
        .metric {
            font-weight: 600;
            color: #1F2937;
        }
    </style>
</head>
<body>
    <h1>Server Status Dashboard</h1>
    <p>Live metrics from your infrastructure</p>

    @foreach($servers as $server)
    <div class="server-card {{ $server->status }}">
        <h3>{{ $server->server_name }} <small>({{ $server->region }})</small></h3>
        <p class="metric">Status: {{ ucfirst($server->status) }}</p>

        @if($server->status != 'offline')
            <p class="metric">CPU: {{ $server->cpu_usage }}%</p>
            <p class="metric">Memory: {{ $server->memory_usage }}%</p>
        @endif

        <p>Last updated: {{ $server->last_updated }}</p>
    </div>
    @endforeach
</body>
</html>
Enter fullscreen mode Exit fullscreen mode
$ sudo systemctl restart nginx
Enter fullscreen mode Exit fullscreen mode

Visit https://app.example.com — the dashboard should render live rows from server_stats.


Next Steps

Laravel is deployed behind Nginx with TLS and a working database-backed view. From here:

  • Add Eloquent models and migrations instead of raw DB::table() queries for larger apps
  • Switch APP_ENV to production and APP_DEBUG to false before going live
  • Set up a queue worker (php artisan queue:work) as a systemd service for background jobs

For the full guide, visit the original article on Vultr Docs.

Top comments (0)