---
title: "How to host Laravel on a VPS with Nginx, PHP 8.3 and MariaDB | StreetHosting"
description: "Host Laravel on an Ubuntu 24.04 VPS: PHP 8.3 FPM, Composer, MariaDB, correct storage permissions, Nginx with HTTPS, queues, scheduler and deploy."
url: "https://streethosting.com.br/en/guides/vps/host-laravel-on-vps"
type: "page"
language: "en-US"
---

VPS · 9 min · Intermediate

Published on Sep 28, 2026 · Updated on Sep 28, 2026

# Hosting Laravel on a VPS: PHP FPM, database, permissions and HTTPS with no 500 errors

From a blank server to Laravel answering over HTTPS: Ubuntu 24.04's PHP 8.3 FPM, Composer, MariaDB, storage and bootstrap/cache permissions, Nginx in the official layout, queues with systemd and a deploy script.

By [Equipe StreetHosting](https://streethosting.com.br/en/autores#equipe-streethosting) · StreetHosting infrastructure and support team

[Linux administration](https://streethosting.com.br/en/guides/topics/linux) [Deploying and running apps](https://streethosting.com.br/en/guides/topics/deploy) [Errors and diagnostics](https://streethosting.com.br/en/guides/topics/troubleshooting) [Databases](https://streethosting.com.br/en/guides/topics/databases)

Summarize with:

[](https://chat.openai.com/?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-laravel-on-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "ChatGPT") [](https://claude.ai/new?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-laravel-on-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Claude") [](https://www.google.com/search?udm=50&aep=11&q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-laravel-on-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Google AI Mode") [](https://x.com/i/grok?text=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-laravel-on-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Grok") [](https://www.perplexity.ai/search/new?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-laravel-on-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Perplexity")

Share:

[](https://x.com/intent/tweet?text=How%20to%20host%20Laravel%20on%20a%20VPS%20with%20Nginx%2C%20PHP%208.3%20and%20MariaDB&url=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-laravel-on-vps "Share on X") [](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-laravel-on-vps "Share on Facebook") [](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-laravel-on-vps "Share on LinkedIn") [](https://wa.me/?text=How%20to%20host%20Laravel%20on%20a%20VPS%20with%20Nginx%2C%20PHP%208.3%20and%20MariaDB%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Fhost-laravel-on-vps "Share on WhatsApp")

For agents: Copy as Markdown [.md](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps.md)

In this guide 7 sections

* [What the VPS needs](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#stack)
* [PHP 8.3, Composer and MariaDB](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#instalar)
* [Code, .env and permissions](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#codigo-permissoes)
* [Nginx, domain and HTTPS](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#nginx-ssl)
* [Queues, scheduler and deploy](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#filas-deploy)
* [Common errors and performance](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#erros-desempenho)
* [Which VPS to pick](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#onde-rodar)

Quick answer

To **host Laravel on a VPS**, install PHP 8.3 FPM with the framework's extensions, Composer and MariaDB on Ubuntu 24.04, clone the project into `/var/www`, give `www-data` write permission only on `storage` and `bootstrap/cache`, and point Nginx at the `public` folder with HTTPS from Let's Encrypt. Queues and the scheduler run with systemd and cron.

## What the VPS needs[](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#stack)

Unlike Node or Java, Laravel does not run as a process listening on a port. Nginx receives the requests, serves static files straight from disk and hands the rest to PHP FPM, a pool of PHP processes ready to execute the code. That is why the Nginx configuration differs from an ordinary reverse proxy.

| Component        | Role                                                          | Version on Ubuntu 24.04 |
| ---------------- | ------------------------------------------------------------- | ----------------------- |
| Nginx            | Receives traffic, serves static files and terminates HTTPS    | nginx package           |
| PHP FPM          | Runs the Laravel code                                         | PHP 8.3                 |
| Composer         | Installs the project's PHP dependencies                       | composer package (2.7)  |
| MariaDB          | Database                                                      | MariaDB 10.11           |
| Node.js          | Compiles CSS and JavaScript with Vite, if the project uses it | Node 24 from NodeSource |
| systemd and cron | Keep queues and scheduled tasks running                       | Already in the system   |

The PHP 8.3 in Ubuntu 24.04's default repository covers Laravel 13, which requires exactly that version as its minimum, and Laravel 12. If your VPS is on Ubuntu 22.04, its PHP is 8.1, too old; reinstalling with 24.04 is simpler than maintaining an external PHP repository.

## Install PHP 8.3, Composer and MariaDB[](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#instalar)

The extensions below cover what Laravel requires (mbstring, XML, cURL, PDO and the ones already built into PHP) and what almost every project uses, such as zip for Composer, intl for formatting and bcmath for money calculations:

`sudo apt update sudo apt install -y nginx php8.3-fpm php8.3-cli php8.3-mysql \ php8.3-mbstring php8.3-xml php8.3-curl php8.3-zip php8.3-bcmath \ php8.3-intl php8.3-gd unzip git composer php -v composer --version`

Installing Composer through apt is the simplest path and already gives you version 2. If a package in the project asks for a newer Composer, follow the official installer described on the Composer website.

Next, the database. The hardening script removes anonymous users and the test database; then create a database and a user dedicated to the application:

`sudo apt install -y mariadb-server sudo mariadb-secure-installation sudo mariadb CREATE DATABASE meu_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'meu_app'@'localhost' IDENTIFIED BY 'troque-esta-senha'; GRANT ALL PRIVILEGES ON meu_app.* TO 'meu_app'@'localhost'; FLUSH PRIVILEGES; EXIT;`

`utf8mb4`is the one that accepts emojis and any character; MySQL's old utf8 truncates those characters. The guide on [MariaDB and MySQL on a VPS](https://streethosting.com.br/en/guides/vps/install-mariadb-mysql-ubuntu-vps) covers secure remote access, backups with dump and tuning the database's memory.

## Code, .env and permissions[](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#codigo-permissoes)

Create the application folder with your user as owner and clone the repository. The code lives in `/var/www` rather than your home folder because, on Ubuntu 24.04, home folders are closed to other users, and Nginx and PHP FPM run as `www-data`.

`sudo mkdir -p /var/www/meu-app sudo chown usuario:usuario /var/www/meu-app git clone https://github.com/sua-conta/meu-app.git /var/www/meu-app cd /var/www/meu-app composer install --no-dev --optimize-autoloader cp .env.example .env php artisan key:generate`

Edit `.env` with the production values:

`APP_ENV=production APP_DEBUG=false APP_URL=https://seu-dominio.com.br DB_CONNECTION=mariadb DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=meu_app DB_USERNAME=meu_app DB_PASSWORD=troque-esta-senha`

`APP_DEBUG=false` is not a detail. With debug on, any error shows the visitor a page with code excerpts, queries and environment variables, passwords included. Never leave it on in production.

### storage and bootstrap/cache permissions[](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#permissoes)

Laravel only needs to write to two folders: `storage`, with logs, cache, sessions and uploads, and `bootstrap/cache`, with the files generated by the configuration and route caches. The rest of the code should be read-only for PHP. Running `chmod 777`on everything "fixes" the error, but lets a flawed upload write an executable PHP file inside the project.

`cd /var/www/meu-app sudo chown -R usuario:www-data /var/www/meu-app sudo chmod -R u=rwX,g=rX,o= /var/www/meu-app sudo apt install -y acl sudo setfacl -R -m u:www-data:rwX -m u:usuario:rwX storage bootstrap/cache sudo setfacl -dR -m u:www-data:rwX -m u:usuario:rwX storage bootstrap/cache`

* **Your user as owner, the web server as group:** the code is yours, and PHP reads it through the group without being able to change anything. The chmod removes write from the group and closes everything to other system users, including the `.env`, which ends up at 640.
* **First setfacl line:** gives PHP FPM and your user write access to the two folders, including what already exists in them.
* **Second line, with -d:** sets the default rule for new files. It is what prevents the classic error where `laravel.log` is created by one of your commands and the site then cannot write to it, or the other way around.

With the permissions in place, run the migrations, the public uploads link and, if the project uses Vite, the asset build. For Node.js, follow the same NodeSource install used in the guide on [Node.js applications on a VPS](https://streethosting.com.br/en/guides/vps/host-nodejs-on-vps), or build in CI and ship the finished folder.

`php artisan migrate --force php artisan storage:link npm ci && npm run build php artisan optimize`

`--force` is required because Laravel asks for confirmation before migrating in production. `optimize` caches configuration, routes, events and views, which cuts the work of each request. After it, changes to `.env` only take effect once you run the command again.

## Nginx, domain and HTTPS[](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#nginx-ssl)

Point the domain at the VPS IP with an A record, as shown in the guide on [pointing a domain at your VPS](https://streethosting.com.br/en/guides/vps/point-domain-to-vps). The configuration below follows the one recommended by the Laravel documentation, with the path and the domain adjusted:

`sudo nano /etc/nginx/sites-available/meu-app server { listen 80; listen [::]:80; server_name seu-dominio.com.br www.seu-dominio.com.br; root /var/www/meu-app/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; client_max_body_size 20m; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ ^/index\.php(/|$) { fastcgi_pass unix:/var/run/php/php8.3-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; fastcgi_buffer_size 32k; fastcgi_buffers 8 32k; fastcgi_busy_buffers_size 64k; fastcgi_hide_header X-Powered-By; } location ~ /\.(?!well-known).* { deny all; } }`

Three details matter. `root` points to `public`, never to the project root, otherwise `.env` and the code become reachable from the web. Only `index.php` is executed, so a PHP file sent through an upload does not run. And the last block denies hidden files, allowing only the folder used by certificate validation.

`sudo ln -s /etc/nginx/sites-available/meu-app /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/default sudo nginx -t && sudo systemctl reload nginx sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d seu-dominio.com.br -d www.seu-dominio.com.br`

Removing the `default` site deletes only the shortcut to Nginx's default page, which would otherwise answer any request made to the IP. The firewall rules and how not to lose SSH access when enabling UFW are in the [UFW firewall](https://streethosting.com.br/en/guides/vps/ufw-firewall-ubuntu-vps) guide.

Certbot adds the HTTPS block and the redirect on its own, and renewal is automatic. Extra security headers and the renewal test are in the guide on [SSL certificates with Let's Encrypt](https://streethosting.com.br/en/guides/vps/lets-encrypt-ssl-certificate-vps). `client_max_body_size` allows larger uploads in Nginx, but PHP has its own limits: `upload_max_filesize` (2 MB by default) and `post_max_size` (8 MB), in `/etc/php/8.3/fpm/php.ini`. After changing them, reload PHP FPM.

## Queues, scheduler and deploy[](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#filas-deploy)

If the application uses queues to send emails or process heavy jobs, something has to keep the worker running at all times. A systemd unit does it without installing anything:

`sudo nano /etc/systemd/system/meu-app-fila.service [Unit] Description=Worker de filas do meu-app After=network.target mariadb.service [Service] User=www-data Group=www-data WorkingDirectory=/var/www/meu-app ExecStart=/usr/bin/php artisan queue:work --sleep=3 --tries=3 --max-time=3600 Restart=always RestartSec=5 [Install] WantedBy=multi-user.target sudo systemctl daemon-reload sudo systemctl enable --now meu-app-fila`

`Restart=always` is deliberate. `--max-time` makes the worker exit every hour to release accumulated memory, and the deploy's `queue:restart` also shuts it down on purpose. In both cases the exit is normal, and only `always` guarantees it comes back.

The Laravel scheduler needs a single cron entry, running every minute as `www-data`. Open it with `sudo crontab -u www-data -e` and add the line below. Cron syntax and how to debug tasks that do not run are in the guide on [cron and scheduled tasks](https://streethosting.com.br/en/guides/vps/schedule-tasks-vps-cron).

`* * * * * cd /var/www/meu-app && php artisan schedule:run >> /dev/null 2>&1`

### Deploy script[](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#script-deploy)

Updating by hand, in the right order, every single time, is where errors are born. Save this script as `/var/www/meu-app/deploy.sh` and run it with your user:

`#!/usr/bin/env bash set -e umask 022 cd /var/www/meu-app php artisan down git pull origin main composer install --no-dev --optimize-autoloader npm ci && npm run build php artisan migrate --force php artisan optimize php artisan queue:restart sudo systemctl reload php8.3-fpm php artisan up`

`set -e` stops the script at the first error. That avoids migrating a database with broken code, but leaves the site in maintenance mode: fix the problem and run `php artisan up`. `umask 022` ensures the new files brought in by git and Composer are created without group write, keeping the rule that PHP only reads the code. Reloading PHP FPM clears OPcache, which holds the compiled code in memory, so the new version takes effect immediately. To deploy on every push, your CI only has to open an SSH connection and call this same script.

## Common errors and PHP FPM performance[](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#erros-desempenho)

| Symptom                                     | Likely cause                                   | How to fix                                                             |
| ------------------------------------------- | ---------------------------------------------- | ---------------------------------------------------------------------- |
| 500 error with Permission denied in the log | PHP cannot write to storage or bootstrap/cache | Redo the setfacl rules on both folders                                 |
| No application encryption key               | Empty APP\_KEY in .env                         | Run php artisan key:generate and then optimize                         |
| 502 Bad Gateway                             | PHP FPM stopped or wrong socket path           | Check that the PHP FPM service is active and the path in fastcgi\_pass |
| Change to .env does not show up             | Cached configuration                           | Run php artisan optimize again                                         |
| Blank page or 404 on every route            | Nginx pointing to the root instead of public   | Fix the root and reload Nginx                                          |
| Links and assets over http                  | Wrong APP\_URL or an untrusted proxy in front  | Fix APP\_URL and configure the trusted proxies                         |

The first place to look for any error is `storage/logs/laravel.log`. If it shows nothing, the problem happened before Laravel, and the answer is in `/var/log/nginx/error.log`. With a proxy in front, such as a CDN, trusted proxies are set in `bootstrap/app.php`, in the middleware's `trustProxies` method.

### How many PHP FPM processes you should have[](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#php-fpm-processos)

Ubuntu's default pool allows at most 5 simultaneous PHP processes, set in `pm.max_children` in `/etc/php/8.3/fpm/pool.d/www.conf`. Under traffic, the sixth simultaneous request waits in line and the site feels slow even with CPU to spare. Do not guess the number: measure how much each process takes after some real use.

`ps --no-headers -o rss -C php-fpm8.3 | awk '{s+=$1} END {print s/NR/1024 " MB por processo"}'`

Divide the memory left for PHP, after subtracting the database, Redis and the system, by that value. If 2 GB are left and each process uses about 60 MB, a limit near 30 is safe. Above that math, the VPS starts using swap and everything gets slower. Reload with `sudo systemctl reload php8.3-fpm` after the change.

If the application keeps cache and sessions in the database, the default in recent Laravel versions, moving those two to Redis takes load off MariaDB on high-traffic sites.

## Which VPS to pick for Laravel[](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#onde-rodar)

PHP FPM serves each request in a separate process, so more vCPUs let you serve more visitors at the same time. That is why the [Xeon VPS](https://streethosting.com.br/en/vps/xeon), which delivers more vCPU for the money, is the main recommendation for most Laravel projects. When the bottleneck is the time of each request, such as heavy reports or a lot of logic per page, the high clock of the Ryzen 9 9950X line helps more. Both are on the [VPS plans](https://streethosting.com.br/en/vps) page.

| Scenario                            | Suggested plan                       | Monthly price |
| ----------------------------------- | ------------------------------------ | ------------- |
| Small site or test environment      | Xeon 2 vCPU, 2 GB DDR4, 20 GB NVMe   | R$ 26.00      |
| Laravel site with MariaDB           | Xeon 3 vCPU, 4 GB DDR4, 40 GB NVMe   | R$ 43.00      |
| Store or SaaS with queues and Redis | Xeon 6 vCPU, 8 GB DDR4, 80 GB NVMe   | R$ 77.00      |
| Several projects or high traffic    | Xeon 9 vCPU, 16 GB DDR4, 160 GB NVMe | R$ 145.00     |
| Heavy requests, clock speed first   | Ryzen 2 vCPU, 4 GB DDR5, 40 GB NVMe  | R$ 66.00      |

All of them are in São Paulo, with Anti-DDoS included, NVMe disk, root access and activation within 60 seconds. If the site grows, upgrading through the control panel charges only the prorated difference and requires a VM restart; afterwards, redo the `pm.max_children` math with the new memory.

* PHP 8.3 FPM with the Laravel extensions
* Dedicated database and user with utf8mb4
* APP\_ENV=production and APP\_DEBUG=false
* PHP writes only to storage and bootstrap/cache
* Nginx with root on the public folder
* HTTPS with Certbot and UFW allowing only SSH, 80 and 443
* Queue worker in systemd and scheduler in cron
* pm.max\_children calculated from real memory

In this guide

* [What the VPS needs](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#stack)
* [PHP 8.3, Composer and MariaDB](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#instalar)
* [Code, .env and permissions](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#codigo-permissoes)
* [Nginx, domain and HTTPS](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#nginx-ssl)
* [Queues, scheduler and deploy](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#filas-deploy)
* [Common errors and performance](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#erros-desempenho)
* [Which VPS to pick](https://streethosting.com.br/en/guides/vps/host-laravel-on-vps#onde-rodar)

## Frequently asked questions

Which PHP version should I use for Laravel on a VPS?

Laravel 13 requires PHP 8.3 and Laravel 12 accepts PHP 8.2 or newer. Ubuntu 24.04 ships PHP 8.3 in its default repository, which covers both versions without an external repository. Ubuntu 22.04 ships PHP 8.1, which no longer works for the current versions.

Can I use SQLite instead of MariaDB in production?

Yes, for small sites with little concurrent writing. Laravel ships already configured for SQLite, and it needs no database server. When many users write at the same time, queues are heavy or more than one application reads the data, MariaDB or PostgreSQL handle the concurrency better.

Why does Laravel throw a 500 error right after deploy?

The most common causes are missing write permission on storage or bootstrap/cache, an empty APP\_KEY in .env and a stale configuration cache. The exact reason shows up in storage/logs/laravel.log and in the Nginx error log.

Do I need to install Supervisor for the queues?

It is not required. The Laravel documentation uses Supervisor as the example, but systemd, which already ships with Ubuntu, plays the same role: it keeps the worker running, restarts it when it exits and keeps the logs.

How much RAM does a Laravel application need?

A small Laravel site with MariaDB on the same VPS runs well with 2 GB. With queues, Redis and more traffic, 4 GB gives you headroom. What weighs most is the number of PHP FPM processes, because each one takes tens of MB; measure real usage before raising that limit.

Next step

See Xeon VPS

Xeon VPS for steady workloads, automation and long-running projects.

[See Xeon VPS](https://streethosting.com.br/en/vps/xeon)

[See VPS plans Root VPS in Brazil with NVMe and Anti-DDoS.](https://streethosting.com.br/en/vps)

## Related guides

[VPS Intermediate How to install MariaDB or MySQL on an Ubuntu VPS Almost every application needs a database. See how to install MariaDB or MySQL, run the security script, create a database and user, and allow access without exposing the server. 3 min Read guide](https://streethosting.com.br/en/guides/vps/install-mariadb-mysql-ubuntu-vps) [VPS Beginner SSL certificate on a VPS with Let's Encrypt and Nginx A site without the padlock loses trust and rankings. This guide shows how to issue, renew and audit a free SSL certificate on your VPS with Nginx and Certbot. 4 min Read guide](https://streethosting.com.br/en/guides/vps/lets-encrypt-ssl-certificate-vps) [VPS Intermediate How to schedule tasks on a VPS with cron A repetitive task you do by hand is a task you forget. Cron runs commands at fixed times: a backup overnight, a weekly cleanup, a scheduled restart. Here is how to set it up. 3 min Read guide](https://streethosting.com.br/en/guides/vps/schedule-tasks-vps-cron)

[← Back to the Guide Center](https://streethosting.com.br/en/guides)
