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.