October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
VGSources
Blog

How to Deploy Laravel on a VPS (Ubuntu, Nginx, PHP-FPM, MySQL, and Queues)

Deploy Laravel on a VPS with Ubuntu, Nginx, PHP-FPM, Composer, a database, HTTPS, Supervisor queue workers, production caches, permissions, and a rollback-ready release process.
Length11 min Posted Quest giverVGSources Team
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Deploying Laravel on a VPS means preparing a production Linux server, installing a PHP version supported by your Laravel release, configuring Nginx to serve the application’s public directory, and running the release with production-safe settings. The reliable sequence is: provision Ubuntu, install PHP-FPM and required extensions, install Composer and a database, deploy the code, create and protect .env, set write permissions for storage and bootstrap/cache, run migrations and Laravel caches, add HTTPS, and keep queue workers supervised.

This guide uses the conventional Ubuntu plus Nginx plus PHP-FPM stack. It also shows how to choose PHP for Laravel 12 or 13, configure workers, recover from common failures, and move from a simple in-place deployment to a symlink-based release process.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose the PHP and Laravel versions first

PHP must meet the minimum required by the Laravel major version you deploy. Laravel 12 requires PHP 8.2 or newer; Laravel 13 raises the minimum to PHP 8.3. Both releases require the same core extensions: Ctype, cURL, DOM, Fileinfo, Filter, Hash, Mbstring, OpenSSL, PCRE, PDO, Session, Tokenizer, and XML. Install a PHP minor release that your operating-system packages and application dependencies support, then keep that version consistent across the command-line binary and PHP-FPM.

Laravel release Minimum PHP Practical VPS choice Important qualification
12.x 8.2 PHP 8.2 or 8.3 with the matching FPM service The minimum is from Laravel’s 12.x deployment requirements; packages and extensions still have to be available for your Ubuntu release.
13.x 8.3 PHP 8.3 or newer supported by your dependency lock file Do not run a Laravel 13 application on PHP 8.2, even if another application on the same VPS does.

Check the project’s composer.json and composer.lock before provisioning. A lock file generated with a different PHP platform can cause Composer to reject the server or select an unexpected dependency set.

Prepare the VPS and a deployment account

Use a current Ubuntu LTS image from your VPS provider. Create a non-root account for deployments, grant it administrative access through sudo, and use SSH keys rather than password login. Replace the example account and hostname values below with your own.

  1. Connect and update packages.
    ssh root@SERVER_IP
    apt update && apt full-upgrade -y
  2. Create an administrator account.
    adduser deploy
    usermod -aG sudo deploy
  3. Install basic tools.
    apt install -y git unzip curl ca-certificates software-properties-common ufw
  4. Enable a basic firewall policy.
    ufw allow OpenSSH
    ufw allow 'Nginx Full'
    ufw enable
    ufw status
  5. Copy your SSH key to the new account and test it.
    ssh-copy-id deploy@SERVER_IP
    ssh deploy@SERVER_IP

Only after testing the new account should you disable direct root SSH access and password authentication in /etc/ssh/sshd_config. Validate the configuration with sshd -t, then reload SSH. Keep your current session open until a second key-based session succeeds.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Install Nginx, PHP-FPM, and Laravel extensions

Nginx serves static files and forwards PHP requests to PHP-FPM. Install the FPM package whose version matches the Laravel requirement. The package names below use PHP 8.3; substitute 8.2 when deploying Laravel 12 on a PHP 8.2 system.

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

If you use PostgreSQL, install php8.3-pgsql instead of (or in addition to) the MySQL driver. If your application uses Redis, install a Redis PHP extension supported by your chosen PHP version. Laravel’s required list is the baseline; package additions such as intl, zip, a database driver, GD, or Redis depend on your application.

Confirm that the CLI and FPM versions agree:

php -v
php -m
systemctl status php8.3-fpm --no-pager
sudo systemctl enable --now nginx php8.3-fpm

For production, enable OPcache and size it according to the application and available memory. Make configuration changes in the PHP-FPM and OPcache configuration files supplied by your distribution, then restart FPM and verify that the service returns to active (running).

Install and verify the database

Choose the database engine declared in .env. A local database reduces network latency but means database backups and upgrades are your responsibility. A managed database can isolate failure and backups, but requires a private network path, firewall rules, and credentials that are not exposed publicly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

MySQL or MariaDB on the VPS

sudo apt install -y mysql-server
sudo mysql_secure_installation

Create a database and a user with a strong, unique password. Grant that user access only to the application database; do not use the database administrator account in Laravel. Test the credentials from the server before deploying the application.

PostgreSQL

sudo apt install -y postgresql postgresql-contrib

Create a dedicated role and database, then set DB_CONNECTION=pgsql and the corresponding host, port, database, username, and password in the production environment file.

Install Composer safely

Composer is Laravel’s dependency manager. Install it with Composer’s official installer, verify the installer signature as described by Composer’s documentation, and place the binary somewhere on the system path:

curl -sS https://getcomposer.org/installer -o composer-setup.php
php composer-setup.php --install-dir=/usr/local/bin --filename=composer
rm composer-setup.php
composer --version

Run Composer as the deployment user, not as root. Installing dependencies as root often leaves a tree of root-owned files that the web process cannot update or read during a later release.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Deploy the Laravel code

Keep application releases outside the web server’s default document root. A simple layout is /var/www/example with the current release in current; a release-based layout is better for rollback and is covered later.

  1. Create the directory and assign ownership.
    sudo mkdir -p /var/www/example
    sudo chown -R deploy:www-data /var/www/example
  2. Clone the repository as the deployment user.
    cd /var/www/example
    git clone --depth 1 --branch main GIT_REPOSITORY current
    cd current
  3. Install production dependencies.
    composer install --no-dev --optimize-autoloader
  4. Create the production environment file.
    cp .env.example .env
    php artisan key:generate --force

Do not commit production secrets. Set APP_ENV=production, APP_DEBUG=false, the exact APP_URL, database values, mail transport, cache and queue drivers, and any third-party credentials directly on the server or through a secret-management system. Protect .env so only the deployment user and the web-service group can read it.

If the repository contains frontend assets, install the project’s pinned Node.js version and run its documented production build before switching traffic. Keep generated assets in the release directory and ensure Nginx can read them.

Set Laravel’s required permissions

Laravel must be able to write logs, compiled views, sessions, and framework cache files. The two directories that must be writable are storage and bootstrap/cache. Make the deployment user the owner and grant the web-service group access without making the entire application world-writable.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
sudo chown -R deploy:www-data /var/www/example/current
sudo find /var/www/example/current -type d -exec chmod 755 {} ;
sudo find /var/www/example/current -type f -exec chmod 644 {} ;
sudo chmod -R ug+rwx /var/www/example/current/storage
sudo chmod -R ug+rwx /var/www/example/current/bootstrap/cache

If workers run under a separate account, add that account to the group that owns these directories or adjust the shared-release permissions. A “permission denied” exception for a log, cache file, or compiled view nearly always indicates ownership or mode drift after a deploy.

Configure Nginx to expose only public

Never point Nginx at the repository root. The document root must be Laravel’s public directory, which keeps source files, the environment file, and Composer metadata outside the directly served path.

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    root /var/www/example/current/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;
    }

    location ~ /. {
        deny all;
    }

    location = /.env {
        deny all;
    }
}

Save the site as /etc/nginx/sites-available/example, enable it, disable the default site if it conflicts, and test before reloading:

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

The try_files fallback sends routes that are not physical files to index.php. The dot-file rule prevents accidental exposure of Git metadata and other hidden files; the explicit .env rule makes the intent clear. If the site returns a 502 error, check that the socket path matches the installed PHP-FPM version and inspect journalctl -u php8.3-fpm.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Run migrations and production caches

Before migrating, verify that the application can reach the database and that the backup policy is in place. Laravel’s production migration command requires an explicit force flag because migrations can alter or remove data:

php artisan migrate --force

Laravel’s deployment guidance recommends caching configuration, events, routes, and views in production. The single optimization command creates those caches:

php artisan optimize

When diagnosing stale configuration or route behavior, clear generated caches with:

php artisan optimize:clear

Run cache-clearing only when you understand the effect: a busy application may briefly regenerate files, and a deployment that clears caches without rebuilding them can increase first-request latency. After changing .env, rebuild the caches before sending traffic to the new release.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep queue workers running with Supervisor

A web request does not process queued jobs unless a worker is running. Supervisor starts workers at boot, restarts them after a crash, and keeps the process count under control. Install it from the distribution packages:

sudo apt install -y supervisor
sudo systemctl enable --now supervisor

Create /etc/supervisor/conf.d/laravel-worker.conf:

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/example/current/artisan queue:work --sleep=3 --tries=3 --timeout=90
directory=/var/www/example/current
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=deploy
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/example/current/storage/logs/worker.log
stopwaitsecs=3600

Choose numprocs, timeout, retry count, and queue names for your workload. Long-running jobs need a worker timeout shorter than Supervisor’s stop wait, and the queue’s visibility timeout must exceed the worker timeout so a job is not delivered twice while still running.

Load and inspect the program:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status laravel-worker:*

Every deployment must tell existing workers to finish their current job and reload the new code. Run this after the new release is ready:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
php artisan queue:restart
sudo supervisorctl restart laravel-worker:*

queue:restart signals workers to exit after the current job; Supervisor then starts them with the new release. If you use Laravel Horizon, supervise the Horizon process instead and follow the same release-restart principle.

Add HTTPS with Let’s Encrypt

Point your DNS A or AAAA records at the VPS, confirm that ports 80 and 443 are reachable, and install Certbot with its Nginx integration:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

Certbot can install the certificate and update the Nginx server block. Test renewal rather than waiting for expiration:

sudo certbot renew --dry-run

After TLS is active, set APP_URL to the HTTPS URL and configure Laravel’s trusted proxy behavior if TLS terminates at a load balancer or CDN. Mixed-content warnings usually mean an asset URL or application URL still uses HTTP.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Use a release-and-symlink deployment for safer updates

An in-place git pull is easy but exposes a partially updated codebase while Composer, assets, or migrations run. A release layout makes the switch atomic:

  1. Create /var/www/example/releases/20261014090000 and clone the exact commit into it.
  2. Link the shared environment file and persistent storage: .env should point to /var/www/example/shared/.env, and storage should point to /var/www/example/shared/storage.
  3. Run composer install --no-dev --optimize-autoloader, build frontend assets, set permissions, and run checks without serving the release.
  4. Run database migrations. Use backward-compatible migrations when old and new code overlap; if a migration cannot be compatible, schedule a maintenance window and use php artisan down before it, then php artisan up after validation.
  5. Run php artisan optimize, then atomically replace the current symlink with the new release.
  6. Reload PHP-FPM if required by your opcode configuration and run php artisan queue:restart. Supervisor will start workers against the new path.
  7. Keep several previous releases until health checks pass, then remove old releases without deleting the shared storage.

Nginx continues to use /var/www/example/current/public; only the symlink target changes. Rollback is a symlink switch to the previous release, followed by a worker restart. Database rollbacks are a separate decision: code can usually be reverted quickly, but destructive schema changes may not be reversible.

Verify the deployment before announcing it

  • Open the HTTPS homepage and a representative authenticated route.
  • Check storage/logs/laravel.log for configuration, database, and permission errors.
  • Run php artisan about if available in your Laravel version to inspect environment and cache state without exposing secrets.
  • Confirm php artisan migrate:status shows the expected migrations.
  • Submit a test job and watch supervisorctl status and the worker log.
  • Verify scheduled tasks. If using Laravel’s scheduler, configure one system cron entry for the deployment user:
    * * * * * cd /var/www/example/current && php artisan schedule:run >> /dev/null 2>&1
  • Check certificate expiry and run the Certbot dry-run periodically.
  • Confirm backups can be restored, not merely created.

Troubleshooting the failures seen most often

502 Bad Gateway

Nginx cannot reach PHP-FPM. Check systemctl status php8.3-fpm, confirm the fastcgi_pass socket exists under /run/php, and inspect Nginx and FPM logs. A PHP version mismatch between the installed service and the configured socket is the usual cause.

500 error after deployment

Read the Laravel log, then verify APP_KEY, database credentials, PHP extensions, and cached configuration. Run php artisan optimize:clear, correct the environment, and rebuild with php artisan optimize. Ensure the web user can write to storage and bootstrap/cache.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Routes return 404 but the homepage works

The Nginx try_files fallback is missing or points to the wrong front controller. Confirm the root ends in /public, reload only after nginx -t passes, and check that the route exists in the deployed code.

Jobs keep running old code

Workers are long-lived and do not automatically reload PHP files. Run php artisan queue:restart, then verify Supervisor starts workers from the current release. In a symlink deployment, ensure the worker command references the stable current path rather than a retired timestamped directory.

Composer refuses to install

Compare php -v with the Laravel and Composer platform requirements, install the missing extension named in the error, and run Composer as the deployment user. Do not bypass platform checks on production; they indicate that the server does not match the application’s declared requirements.

Production hardening checklist

  • Set APP_DEBUG=false and use a unique application key.
  • Keep .env, Git metadata, and Composer files outside the Nginx document root.
  • Use least-privilege database credentials and restrict database network access.
  • Allow only required firewall ports, normally SSH, HTTP, and HTTPS.
  • Patch Ubuntu, PHP, Nginx, the database, and Composer dependencies on a scheduled maintenance cycle.
  • Rotate exposed credentials and revoke old SSH keys promptly.
  • Monitor disk usage, especially logs, uploaded files, database dumps, and failed queue jobs.
  • Back up the database and user uploads separately from the VPS, and periodically perform a restore test.
  • Document the PHP-FPM socket, Supervisor program, deployment commit, migration policy, and rollback path so another operator can recover the service.

The finished deployment has a narrow Nginx entry point, a PHP runtime that matches Laravel, immutable production dependencies, writable framework directories, cached application metadata, encrypted transport, and workers that are explicitly restarted whenever code changes. That combination is what turns a cloned repository into a maintainable Laravel production service on a VPS.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.