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 85+ global datacenter locations 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.
Need a fast, reliable Linux VPS?
Dedicated vCPU, NVMe storage and full root access, deployed in minutes and resizable without a rebuild.
Configure a VPS