How to Deploy a Laravel App on Ubuntu 24.04 with a Secure LEMP Stack

How to Deploy a Laravel App on Ubuntu 24.04 with a Secure LEMP Stack

So you have built a Laravel app and it works perfectly on your local machine. Now comes the real challenge: putting it online. The moment you start thinking about production servers, questions pop up. How do I set up PHP, Nginx, and MySQL? What about SSL certificates? And most importantly, how do I make sure the server is not wide open to attackers?

In this guide we will deploy the official Filament Demo application on Ubuntu 24.04 using the LEMP stack (Linux, Nginx, MySQL, PHP). Along the way we will also secure the server with best practices so that the application is production ready.

🎥 Watch the Video Tutorial

Step 1: Connect to Your Server as Root

When you first get a new VPS or cloud instance the only way in is usually as root.

ssh root@your-server-ip

Replace your-server-ip with your server’s actual IP address.

This works, but root access is risky. Attackers often try to brute force root logins. We will lock this down soon.


Step 2: Create a Safer Deploy User

Instead of living inside root, let’s create a new user called deploy and give it sudo rights.

adduser deploy
usermod -aG sudo deploy

Now you have a non-root user for everyday work. You can replace deploy with another username if you prefer.


Step 3: Use SSH Keys for Authentication

Passwords are not secure enough. We can use SSH keys for safer logins. On your local system run:

ssh-keygen -t ed25519 -C "your_email@example.com"
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@your-server-ip

Replace your_email@example.com with your own email and your-server-ip with your server’s IP.

Now you can log in as:

ssh deploy@your-server-ip

No password needed, and much more secure.


Step 4: Harden SSH Access

Open the SSH configuration:

sudo nano /etc/ssh/sshd_config

Update the following lines:

PermitRootLogin no
PasswordAuthentication no
Port 2222

You can replace 2222 with another custom port if you like. Restart SSH:

sudo systemctl restart ssh

From now on you can log in with:

ssh deploy@your-server-ip -p 2222

Root access will be denied and only your key-based login works.


Step 5: Update the System and Enable Firewall

Always patch your server before using it.

sudo apt update && sudo apt -y upgrade

Then enable UFW, the firewall:

sudo apt -y install ufw
sudo ufw allow 2222/tcp
sudo ufw allow "Nginx Full"
sudo ufw enable

This allows only SSH, HTTP, and HTTPS. Everything else stays blocked. Replace 2222 with the SSH port you set earlier.


Step 6: Install the LEMP Stack

First install Nginx.

sudo apt -y install nginx

Now install PHP 8.3 and required extensions.

sudo apt -y install   php8.3 php8.3-fpm php8.3-cli   php8.3-mysql php8.3-zip php8.3-bcmath   php8.3-mbstring php8.3-xml php8.3-curl   php8.3-gd php8.3-intl

And install MySQL.

sudo apt -y install mysql-server
sudo mysql_secure_installation

Create a database and user inside MySQL (replace with your own values):

CREATE DATABASE demo_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'demo_user'@'localhost' IDENTIFIED BY 'Str0ngP@ss!';
GRANT ALL PRIVILEGES ON demo_db.* TO 'demo_user'@'localhost';
FLUSH PRIVILEGES;

Step 7: Deploy the Filament Demo App

Go to the web directory and clone the demo.

cd /var/www
sudo git clone https://github.com/filamentphp/demo.git demo
sudo chown -R deploy:www-data demo
cd demo

Install dependencies and prepare the environment file.

composer install --no-dev --optimize-autoloader
cp .env.example .env
php artisan key:generate

Edit .env (replace values with your own):

APP_URL=https://demo.example.com
DB_DATABASE=demo_db
DB_USERNAME=demo_user
DB_PASSWORD=Str0ngP@ss!

Run migrations and seed data.

php artisan migrate --seed

Step 8: Configure Nginx for Laravel

Create a server block file at /etc/nginx/sites-available/demo.example.com:

server {
    listen 80;
    server_name demo.example.com;

    root /var/www/demo/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

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

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

Replace demo.example.com with your own domain.

Enable the site and reload Nginx.

sudo ln -s /etc/nginx/sites-available/demo.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Step 9: Add SSL with Let’s Encrypt

Use Certbot to get a free SSL certificate.

sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --nginx -d demo.example.com --redirect -m you@example.com --agree-tos

Replace demo.example.com with your domain and you@example.com with your email address.


Step 10: Optimize Laravel

Cache configuration, routes, and views for performance.

php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan optimize

Add the scheduler to cron:

* * * * * cd /var/www/demo && php artisan schedule:run >> /dev/null 2>&1

Set up Supervisor for queues in /etc/supervisor/conf.d/demo-queue.conf:

[program:demo-queue]
directory=/var/www/demo
command=/usr/bin/php artisan queue:work --sleep=1 --tries=3 --timeout=90
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/supervisor/demo-queue.log
stderr_logfile=/var/log/supervisor/demo-queue.err

Activate it:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl restart demo-queue

Step 11: Extra Hardening

Install automatic security updates and Fail2Ban.

sudo apt -y install unattended-upgrades fail2ban
sudo dpkg-reconfigure --priority=low unattended-upgrades

Fix permissions for Laravel.

sudo chown -R deploy:www-data /var/www/demo
sudo find /var/www/demo/storage -type d -exec chmod 775 {} \;
sudo find /var/www/demo/storage -type f -exec chmod 664 {} \;

Final Result

Open your browser and visit https://demo.example.com/admin. Replace demo.example.com with your own domain. You will see the Filament demo application running securely on your production server.

You have now learned not only how to deploy a Laravel application but also how to secure the server properly. With these steps you avoid common mistakes like leaving root login open or forgetting to enable SSL.