Recommended Free Tools
iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Antihost offers Linux VPS plans with full root access, unlimited traffic on a 1 Gbps port and a 14-day free trial.
Deploying Laravel on a VPS means preparing a Linux server, installing a web server and the PHP runtime, uploading your application, configuring its environment, and securing and supervising the processes that keep it running. This guide uses Ubuntu with Nginx, PHP-FPM, Composer and a relational database. It covers Laravel 12 and 13, including production caching, HTTPS, queues and a safer release workflow.
Use PHP 8.2 or newer for Laravel 12. Laravel 13 requires PHP 8.3 or newer. Confirm the Laravel major version in your application’s composer.json before choosing the PHP package.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesWhat you need before deploying
Have these items ready before changing the server:
- An Ubuntu VPS with a public IPv4 address, a non-root sudo user and a DNS record pointing your domain to that address.
- Your Laravel application’s Git repository and a production-ready
composer.json. - Database credentials and a database server, either on the VPS or on a separate private service.
- SSH access and enough disk space for the operating system, PHP packages, Composer dependencies, logs, uploads and database backups.
- A plan for queues, scheduled tasks, storage and backups. A web request alone does not run queued jobs or Laravel’s scheduler.
For a small application, one VPS can run Nginx, PHP-FPM, a database, Redis and queue workers. Separating the database or cache later is easier if connection details are kept in environment variables from the beginning.
Match PHP to your Laravel release
| Laravel release | Minimum PHP version | Required extension families |
|---|---|---|
| Laravel 12 | PHP 8.2 or higher | Ctype, cURL, DOM, Fileinfo, Filter, Hash, Mbstring, OpenSSL, PCRE, PDO, Session, Tokenizer and XML |
| Laravel 13 | PHP 8.3 or higher | Ctype, cURL, DOM, Fileinfo, Filter, Hash, Mbstring, OpenSSL, PCRE, PDO, Session, Tokenizer and XML |
Applications commonly need additional drivers such as pdo_mysql or pdo_pgsql, plus zip, intl, gd, opcache or a Redis client. Check the application’s dependency constraints rather than installing an arbitrary PHP version.
#1 Best Overall
Prepare Ubuntu and a deployment user
Log in as the sudo-enabled user, not as root for routine application work. Replace deploy and example.com with your own values.
ssh deploy@SERVER_IP
sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx unzip git curl ca-certificates supervisor
Keep SSH protected with keys, disable password authentication after confirming key login, and restrict the firewall to the services you actually use. A typical web server permits SSH, HTTP and HTTPS:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
sudo apt install -y ufw
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
Do not expose a database port publicly when the application and database share the VPS. If the database is remote, allow access only from the VPS address and use TLS where the provider supports it.
Install PHP-FPM and Laravel extensions
Install the PHP version selected for your Laravel major release. Package names differ by Ubuntu release; the following example uses PHP 8.3. If your application requires PHP 8.2, substitute that version consistently in every package and service name.
sudo apt install -y php8.3-fpm php8.3-cli php8.3-common
php8.3-mbstring php8.3-xml php8.3-curl php8.3-zip
php8.3-bcmath php8.3-intl php8.3-gd php8.3-mysql
php8.3-opcache
For PostgreSQL, use php8.3-pgsql instead of, or in addition to, the MySQL driver. If the application uses Redis, install a compatible Redis PHP extension or configure the client specified by the application’s dependencies.
Verify the command-line and FPM versions. They should use the same major and minor release so Composer checks and web requests do not disagree:
php -v
php -m
systemctl status php8.3-fpm --no-pager
If the VPS repository does not provide the required PHP release, use a maintained distribution repository appropriate to your Ubuntu version and review its security and update policy. Do not mix packages from unrelated PHP versions.
Install Composer safely
Composer resolves and installs Laravel’s PHP dependencies. Install it globally from the official Composer installer, verify the installer signature using the current value published by Composer, and place the executable on the system path. The exact signature changes, so do not copy an old hash from an unrelated tutorial.
cd /tmp
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 inside the application directory. Running it as root can leave root-owned files that PHP-FPM cannot update later.
Install and configure the database
Choose MySQL or PostgreSQL according to the application’s configured driver. Install the server only if you are hosting the database on this VPS.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Rank #2
sudo apt install -y mysql-server
sudo mysql_secure_installation
Create a database and a dedicated account with a strong password. The exact SQL should use your own database name and a password generated for this application:
sudo mysql
CREATE DATABASE laravel_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'REPLACE_WITH_A_LONG_RANDOM_PASSWORD';
GRANT ALL PRIVILEGES ON laravel_app.* TO 'laravel_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
For PostgreSQL, create a database and role with equivalent least-privilege access. Record the host, port, database, username and password for the production environment file. Schedule tested backups; a database on the same VPS is not protected from disk failure or an account compromise.
Place the Laravel application on the VPS
Create a directory outside the web server’s default document root. The public directory, not the project root, will be exposed by Nginx.
sudo mkdir -p /var/www/example.com
sudo chown -R deploy:www-data /var/www/example.com
cd /var/www/example.com
git clone YOUR_REPOSITORY_URL .
Use a deploy key or another non-interactive Git credential. Never commit .env, private keys, database passwords or application secrets. If the repository contains only an example environment file, copy it and edit the production version:
cp .env.example .env
nano .env
Set at least these values:
APP_NAME="Your Application"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://example.com
APP_KEY=
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_app
DB_USERNAME=laravel_user
DB_PASSWORD=REPLACE_WITH_THE_DATABASE_PASSWORD
Set the mail, cache, session, queue, filesystem and Redis variables required by your application. APP_DEBUG must be false in production; true exposes implementation details and secrets in error pages. Generate the encryption key only after the production .env is in place:
php artisan key:generate --force
Changing an existing application’s key invalidates encrypted data and sessions, so preserve the original production key during later deployments.
Install dependencies, permissions and database schema
From the project directory, install only production dependencies and build Composer’s optimized autoloader:
composer install --no-dev --optimize-autoloader
Laravel must be able to write to storage and bootstrap/cache. Keep the code owned by the deploy user while granting the PHP-FPM group access to writable directories:
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →sudo chown -R deploy:www-data /var/www/example.com
sudo find /var/www/example.com/storage /var/www/example.com/bootstrap/cache -type d -exec chmod 775 {} ;
sudo find /var/www/example.com/storage /var/www/example.com/bootstrap/cache -type f -exec chmod 664 {} ;
If your distribution runs PHP-FPM under a different account, use that account’s group instead of www-data. Avoid making the entire project world-writable. Confirm that uploaded files and logs can be created before putting traffic on the site.
Run migrations in production mode so Laravel does not pause for an interactive confirmation:
php artisan migrate --force
Only run seeders when your deployment process explicitly intends to modify production data. Review migration compatibility before deploying code that removes columns, renames fields or changes a type used by the currently running release.
Configure Nginx to serve Laravel’s public directory
Create a server block whose root is /var/www/example.com/public. The try_files directive sends requests that are not static files to Laravel’s front controller, while the FastCGI block passes PHP scripts to PHP-FPM.
sudo nano /etc/nginx/sites-available/example.com
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/public;
index index.php;
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;
}
}
The dotfile rule prevents accidental exposure of .env and other hidden files. Keep the PHP-FPM socket aligned with the installed version. Enable and test the site before reloading Nginx:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
Visit the domain over HTTP and confirm that Laravel renders a response. A 404 from Laravel is different from an Nginx 404: the former indicates the request reached the framework, while the latter often indicates an incorrect root or server-name match.
Cache Laravel for production
Laravel’s production guidance recommends caching configuration, events, routes and views. The single command below builds those caches:
php artisan optimize
Run it after changing environment values or application code. To remove generated caches while diagnosing a configuration problem, use:
php artisan optimize:clear
Clear and rebuild caches deliberately. If configuration was cached before an environment variable was corrected, Laravel will continue using the cached value until the cache is cleared and regenerated.
Enable HTTPS with Let’s Encrypt
After DNS resolves to the VPS and the HTTP server block works, install Certbot and its Nginx integration:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
Certbot can update the Nginx configuration and schedule renewal. Test the renewal path rather than assuming it works:
sudo certbot renew --dry-run
Use the HTTPS URL in APP_URL, enable secure cookies through your framework configuration, and check that any reverse proxy or load balancer forwards the original HTTPS scheme. Mixed-content warnings usually mean an asset URL or application URL still uses HTTP.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Rank #4
Run queues with Supervisor
A web request does not keep a queue worker alive. Use Supervisor to restart workers after a crash and to start them when the VPS boots. Install it with apt, then create a program configuration:
sudo nano /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/example.com/artisan queue:work --sleep=3 --tries=3 --timeout=90
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=deploy
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/example.com/storage/logs/worker.log
stopwaitsecs=3600
Choose numprocs, timeout and retry values for the jobs and VPS capacity. Long-running jobs need a worker timeout longer than the job’s maximum runtime, while Supervisor’s stop wait must allow a worker to finish gracefully. Apply the configuration:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status
Laravel workers keep the old application code in memory. After every successful release, tell them to exit after the current job so Supervisor starts fresh processes:
php artisan queue:restart
sudo supervisorctl status
If the application uses Horizon, supervise the Horizon process instead and use the process-management instructions appropriate to that installation. Do not run both an unmanaged worker and a Supervisor-managed worker for the same queue unless you intentionally want both capacities.
Free tools Windows power users keep installed
One-click scans. No signup required.
Schedule Laravel’s recurring tasks
Define scheduled commands in the application’s scheduler and add one system cron entry. The cron invokes Laravel every minute; Laravel decides whether a task is due:
* * * * * cd /var/www/example.com && php artisan schedule:run >> /dev/null 2>&1
Use the deploy user’s crontab with crontab -e, or configure a system-wide entry with an explicit user. Confirm the server timezone and application timezone so reports, billing and maintenance jobs run at the intended time.
Choose an in-place or zero-downtime deployment workflow
Simple in-place deployment
For a small site that can tolerate a brief maintenance window, update the existing directory in this order:
- Put the application into maintenance mode if a migration or code change is not backward compatible:
php artisan down. - Fetch the approved commit and install dependencies:
git pull --ff-only, thencomposer install --no-dev --optimize-autoloader. - Run compatible migrations with
php artisan migrate --force. - Refresh caches with
php artisan optimize. - Restart workers with
php artisan queue:restart. - Reload PHP-FPM if OPcache or process state requires it, then leave maintenance mode with
php artisan up.
Keep migrations backward-compatible when old workers may still process jobs during the update. Add new columns before deploying code that writes them, and remove obsolete columns only after no running release needs them.
Timestamped releases and a current symlink
For a busier application, deploy each release into a timestamped directory such as /var/www/example.com/releases/20261014-120000. Keep shared .env and persistent storage outside the release, link them into the new directory, install dependencies, run checks and migrations, then atomically point a current symlink at the release.
- Create the release directory and check out the exact commit.
- Link the shared environment file and storage directory.
- Run Composer, permission checks and any asset build required by the project.
- Run migrations after verifying compatibility with both the current and new code.
- Switch the
currentsymlink in one filesystem operation. - Reload PHP-FPM if needed, run
php artisan optimizefrom the new release, and issuephp artisan queue:restart. - Retain a small number of previous releases for rollback, and remove old releases only after confirming workers and requests use the new path.
This pattern reduces the period in which files are mixed between versions. It does not make an incompatible database migration safe; database changes still need an expand-and-contract plan.
Best Value
Verify the deployment
Run a short acceptance checklist from outside the server:
- The domain resolves to the VPS and HTTPS presents a valid certificate.
- The home page, authentication, forms and representative API routes return expected responses.
- Static assets load from the
publicdirectory and do not expose source files or.env. - A test upload can be written to the configured filesystem and retrieved through its intended URL.
- A test queued job is processed, and Supervisor shows workers as running.
- The scheduler’s command appears in cron and produces no permission or PHP-version errors.
- Application, Nginx, PHP-FPM and worker logs are being written and rotated.
- The database backup job completes and a restore procedure has been tested separately.
Useful local checks include:
sudo nginx -t
systemctl is-active nginx php8.3-fpm supervisor
sudo supervisorctl status
tail -n 100 /var/www/example.com/storage/logs/laravel.log
Common deployment failures and fixes
502 Bad Gateway
Nginx cannot reach PHP-FPM. Check that PHP-FPM is running and that fastcgi_pass names the existing socket:
systemctl status php8.3-fpm
ls -l /run/php/
sudo journalctl -u php8.3-fpm -n 100 --no-pager
“Permission denied” in storage or bootstrap/cache
Correct ownership and group permissions for those two directories, then retry the failing request or Artisan command. Do not solve this by changing the whole project to mode 777.
Composer reports an incompatible PHP version or missing extension
Compare php -v and php -m with the application’s Composer constraints and Laravel’s required extension list. Install the missing extension for the same PHP version used by both CLI and FPM, then restart PHP-FPM.
The application ignores updated environment values
Cached configuration is still active. Run php artisan optimize:clear, verify the production .env, then run php artisan optimize. Never print the full environment file into a public log while debugging.
Queued jobs still execute old code
Workers are long-lived. Run php artisan queue:restart after the release and verify Supervisor starts replacement processes. A worker can finish its current job before exiting, so allow time for graceful turnover.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Routes return 404 through Nginx
Confirm that the root ends in /public, the try_files fallback points to /index.php?$query_string, the intended server block is enabled, and sudo nginx -t passes. Check the Laravel route list with php artisan route:list.
HTTPS renewal or redirects fail
Check DNS for both hostnames, ensure ports 80 and 443 are reachable, inspect the active Nginx server blocks, and run sudo certbot renew --dry-run. A proxy in front of the VPS must forward the original scheme correctly or Laravel may generate HTTP URLs.
Production security and maintenance
- Keep
APP_DEBUG=falseand store secrets outside Git. - Patch Ubuntu, Nginx, PHP, Composer dependencies and the database on a regular schedule.
- Use least-privilege database accounts and firewall rules; do not expose administrative database services to the public internet.
- Limit SSH to keys, remove unused accounts and review authentication logs.
- Rotate application secrets only with a plan for encrypted records, sessions and integrations that depend on them.
- Configure log rotation so application and worker logs cannot fill the disk.
- Back up the database, user uploads and the shared environment file securely; test restoration on a separate system.
- Monitor disk space, memory, PHP-FPM saturation, queue latency, failed jobs, certificate expiry and backup results.
Deployment command checklist
A repeatable release normally follows this sequence after code review and a backup check:
git pull --ff-onlyor check out the approved release commit.composer install --no-dev --optimize-autoloader.- Confirm writable
storageandbootstrap/cachedirectories. php artisan migrate --forceafter checking migration compatibility.php artisan optimize.php artisan queue:restart.- Reload PHP-FPM or Nginx only when configuration, code loading or OPcache requires it.
- Check the site, logs, workers, scheduled tasks and a representative user flow.
Once this process is scripted, use the timestamped-release approach for applications that cannot tolerate files changing during a request. For a small application, the in-place process is easier to operate, provided maintenance windows and rollback steps are explicit.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallQuick Recap
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.

