Target Architecture
This tutorial deploys a Laravel application to an Ubuntu 22.04 or 24.04 LTS VPS using Nginx, PHP-FPM 8.3, and MySQL 8. The stack is battle-tested, works with every Laravel version from 10 onward, and gives you fine control over performance and security. You'll need a registered domain, DNS pointing at the VPS, and SSH access as a sudo user.
Install the LEMP Stack
Update packages and install Nginx, MySQL, and PHP along with the extensions Laravel requires:
sudo apt update && sudo apt upgrade -y
sudo apt install nginx mysql-server php-fpm php-cli php-mysql php-mbstring \
php-xml php-bcmath php-curl php-zip php-gd php-intl php-redis \
unzip git curl -y
Confirm services are running:
sudo systemctl status nginx
sudo systemctl status mysql
sudo systemctl status php8.3-fpm
If you're on Ubuntu 22.04 the default package is php8.1-fpm. Add the ppa:ondrej/php repository if you need PHP 8.3 on 22.04.
Install Composer
Composer manages Laravel's dependencies. Install it globally so the composer binary is available to the deploy user:
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
composer --version
Create the Database
Log into MySQL and create a dedicated database and user for the app. Never use the root MySQL account for application access.
sudo mysql
CREATE DATABASE laravel CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'laravel'@'localhost' IDENTIFIED BY 'StrongPasswordHere';
GRANT ALL PRIVILEGES ON laravel.* TO 'laravel'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Run sudo mysql_secure_installation on fresh installs to remove anonymous users and test databases.
Deploy the Application Code
Clone the Laravel project into /var/www. Use a deploy user so Nginx and PHP-FPM can read files without running as root.
sudo mkdir -p /var/www/laravel
sudo chown $USER:www-data /var/www/laravel
cd /var/www/laravel
git clone https://github.com/yourorg/yourapp.git .
composer install --no-dev --optimize-autoloader
Copy the environment template and generate the application key:
cp .env.example .env
php artisan key:generate
Edit .env with your database credentials, APP_ENV=production, APP_DEBUG=false, and the production APP_URL. Then run migrations and cache config:
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
Set File Permissions Correctly
Laravel needs write access to storage/ and bootstrap/cache/ but nothing else should be writable by the web server.
sudo chown -R www-data:www-data /var/www/laravel
sudo find /var/www/laravel -type f -exec chmod 644 {} \;
sudo find /var/www/laravel -type d -exec chmod 755 {} \;
sudo chmod -R ug+rwx /var/www/laravel/storage /var/www/laravel/bootstrap/cache
This pattern keeps the code read-only for the web user while allowing Laravel to write cache files and session data.
Configure Nginx for Laravel
Create /etc/nginx/sites-available/laravel with a Laravel-aware server block. The try_files directive routes every request through index.php, which is how Laravel's router picks up requests.
server {
listen 80;
server_name example.com www.example.com;
root /var/www/laravel/public;
index index.php;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
charset utf-8;
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Enable the site and reload:
sudo ln -s /etc/nginx/sites-available/laravel /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Check out our Nginx reverse proxy guide for more advanced tuning such as rate limiting and gzip.
Enable HTTPS with Let's Encrypt
Once DNS propagates, issue a free certificate with Certbot:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d example.com -d www.example.com
Certbot configures auto-renewal via a systemd timer. For EV or wildcard needs, see our SSL certificate options or our SSL install walkthrough.
Queue Workers and the Scheduler
Most production Laravel apps need a background worker and the scheduler. Install Supervisor and create a worker config:
sudo apt install supervisor -y
sudo nano /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
command=php /var/www/laravel/artisan queue:work --sleep=3 --tries=3
autostart=true
autorestart=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/laravel-worker.log
Reload Supervisor with sudo supervisorctl reread && sudo supervisorctl update. For the scheduler, add one cron entry as the deploy user:
* * * * * cd /var/www/laravel && php artisan schedule:run >> /dev/null 2>&1
Deployment Checklist
APP_ENV=productionandAPP_DEBUG=falsein.env- Cache config, routes, and views after each deploy
- Run
php artisan storage:linkfor public uploads - Keep
.envmode 640 and never commit it - Schedule regular database and file backups
Running a production Laravel stack? MassiveGRID's Cloud VPS gives you NVMe storage, root access, and four global data centers for reliable Laravel hosting. Contact our team to scope your deployment.
Published by MassiveGRID, your trusted Linux VPS hosting partner. Explore our Cloud VPS plans for root-access Ubuntu hosting.