How to Deploy a Laravel App to a VPS
How to deploy Laravel to a server: PHP-FPM, Composer, .env, the database, file permissions, Nginx, queue workers, the scheduler, SSL and shipping updates.

Contents
Deploying a Laravel application to a server takes more than getting the code onto the server: PHP-FPM, the Composer dependencies, the .env settings, the database, file permissions, Nginx, the queue workers and the scheduler all need to be configured correctly. Once you've followed the steps in this guide, you can deploy your next projects with the same setup in minutes.
This guide covers deploying a Laravel application on a clean Ubuntu server from start to finish, setting up the queue and the scheduler, SSL and shipping later updates.
Requirements
- Server: An Ubuntu server with root access. In this guide we use Ubuntu 24.04.
- PHP: The PHP version and extensions your Laravel version requires. The
phpline in your project'scomposer.jsonfile shows the minimum version needed. - Database: MariaDB, MySQL or PostgreSQL. For the setup, see our How to install MariaDB and MySQL or How to install PostgreSQL guides.
- Domain: An A record pointing to your server's IP address.
Step 1: PHP, Composer and Nginx
After connecting to your server with SSH:
sudo apt update
sudo apt install nginx git unzip \
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-redis
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
If your project needs a different PHP version, change the 8.3 in the package names; we cover running more than one version in our How to change the PHP version guide. If you use PostgreSQL, install php8.3-pgsql instead of php8.3-mysql.
Step 2: Get the code onto the server
Create the application's folder and pull the code from the Git repository:
sudo mkdir -p /var/www/app
sudo chown $USER:www-data /var/www/app
git clone git@github.com:user/app.git /var/www/app
cd /var/www/app
composer install --no-dev --optimize-autoloader
We cover adding a deploy key to the server that can only read that repository, and automating deployments, in our How to deploy with Git guide. If you build your front-end files with Vite, also add the npm ci && npm run build step.
Step 3: .env settings
cp .env.example .env
php artisan key:generate
nano .env
The most important settings for production:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://yourdomain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=app
DB_USERNAME=app
DB_PASSWORD=a-strong-password
APP_DEBUG=false: This must be off in production. If it's left on, error pages can show visitors configuration details, including passwords.APP_KEY: Created withkey:generate. If you lose this key, encrypted data and sessions can't be read; back it up somewhere safe.- If you use Redis, set the
CACHE_STORE,SESSION_DRIVERandQUEUE_CONNECTIONvalues toredis; see our What is Redis? article for details.
Never add the .env file to the Git repository.
Step 4: Database, permissions and caching
php artisan migrate --force
php artisan storage:link
sudo chown -R $USER:www-data storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache
php artisan optimize
migrate --force: Creates the database tables in production;--forceskips the confirmation prompt.storage:link: Creates thepublic/storagelink so that uploaded files can be reached from the web.- Permissions: The web server needs to be able to write to the
storageandbootstrap/cachefolders. Don't give the whole project777. optimize: Speeds up the application by building the configuration, route and view caches. Run this command again after changing the.envfile.
Step 5: Nginx configuration
Create the /etc/nginx/sites-available/app file. Note that the web root is the public folder, not the project itself:
server {
listen 80;
listen [::]:80;
server_name yourdomain.com;
root /var/www/app/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 ~ /\.(?!well-known).* {
deny all;
}
}
Enable the site:
sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Making the project folder the web root instead of public can let your .env file be read from the internet. We cover the basics of Nginx in our How to install Nginx guide.
Step 6: SSL
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com
Certbot gets the certificate, updates the Nginx configuration for HTTPS and sets up automatic renewal. See our What is an SSL certificate? article for details.
Step 7: Queue worker and scheduler
Queue worker: Runs jobs like sending email and processing files in the background. Create the /etc/systemd/system/app-queue.service file:
[Unit]
Description=Laravel Queue Worker
After=network.target
[Service]
User=www-data
Group=www-data
Restart=always
WorkingDirectory=/var/www/app
ExecStart=/usr/bin/php artisan queue:work --sleep=3 --tries=3 --max-time=3600
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now app-queue
Scheduler: A single cron line that runs every minute is enough for Laravel's scheduled tasks. Add it with sudo crontab -u www-data -e:
* * * * * cd /var/www/app && php artisan schedule:run >> /dev/null 2>&1
We explain how cron works in our What is cron? article.
Deploying updates
When you deploy a new version, follow these steps:
cd /var/www/app
php artisan down
git pull
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan optimize
php artisan queue:restart
php artisan up
queue:restart makes the queue workers use the new code. We cover collecting these steps in a script and automating them with GitHub Actions in our How to deploy with Git guide. Before deployments that include database changes, don't forget to back up the database.
Nodesty servers for Laravel
Nodesty AMD Ryzen 9 9950X VPS plans give PHP requests low response times with high single-core speed, while AMD EPYC 7763 VPS plans offer plenty of RAM for running the application, database, Redis and queue workers on the same server. With full root access, NVMe storage, nShield DDoS protection and free daily backups kept for 7 days, you can deploy your Laravel projects with confidence.
Frequently Asked Questions
Can I run Laravel on shared hosting?
It's possible for small projects, but tools like queue workers, the scheduler and Composer need SSH access and control over the server. For applications that use queues and background jobs, a VPS with full root access is a much better fit.
I get a 500 error but no error message. Why?
In production, APP_DEBUG is false, so detailed errors aren't shown. The details of the error are in the storage/logs/laravel.log file. The most common causes are a missing APP_KEY, wrong database credentials and a storage folder that isn't writable.
I changed the .env file, but the change didn't apply. Why?
If the configuration has been cached, Laravel doesn't read the .env file again. Run the php artisan config:cache command again to refresh the cache; if queue workers are running, restart them too with php artisan queue:restart.
Why doesn't the queue worker run the new code?
Queue workers are long-running processes and keep the code from the moment they started in memory. Run php artisan queue:restart after every deployment; systemd or Supervisor restarts the workers automatically.
Can I put the application in maintenance mode during a deployment?
Yes. The php artisan down command puts the application in maintenance mode and shows visitors a maintenance page; once the deployment is done, you bring it back up with php artisan up. For short deployments, it's also possible to set up a zero-downtime deployment process instead.
Which database should I use for Laravel?
Laravel supports MariaDB, MySQL and PostgreSQL. Which one you choose depends on your project's needs and your team's experience. Using Redis in addition for queues, caching and sessions improves performance.
About the author
Nodesty Team · Infrastructure & Support
The Nodesty team runs our servers, network and DDoS protection day to day and answers customer support requests around the clock. These guides come from that work.
Related articles

How to Upgrade Ubuntu Server to the Next LTS Release
How to upgrade an Ubuntu server to the next LTS release: backups, preparation, do-release-upgrade, SSH access, post-upgrade checks and a clean install.

What Is a Load Balancer and How Does It Work?
What is a load balancer and what does it do? Balancing methods, health checks, session handling, load balancing with Nginx, HAProxy and when you need one.

How to Install PostgreSQL: Linux Server Guide
How to install PostgreSQL on a Linux server: creating roles and databases, access with pg_hba.conf, remote connections, pg_dump backups and tuning.