// hosting
Hosting a Laravel app: VPS, requirements and deployment
A practical guide to hosting a Laravel application — why a VPS is usually the right fit, the server requirements that matter (PHP, extensions, queue workers, scheduler), and a real deployment walkthrough with Composer, .env, Nginx and Supervisor.
Laravel is a full PHP application framework, not a set of pages you upload by FTP. It expects a real runtime: a recent PHP, several extensions, a writable storage directory, scheduled tasks and — for anything non-trivial — background queue workers. That's why basic shared hosting often fights you, and why a VPS is usually the right home for a Laravel app. This guide covers what the server actually needs and walks through a real deployment.
Why a VPS suits Laravel
Shared hosting is built for "drop files in a web root". Laravel wants more than that: SSH access to run composer and artisan, the ability to point the web server at the public/ directory, a long-running process to drain the queue, and a cron entry for the scheduler. A VPS gives you root access, guaranteed RAM and CPU, and full control of the stack — exactly what those needs require. A managed platform can also work, but a VPS is the most flexible middle ground once an app has queues, a database and a cache.
Server requirements
PHP and extensions
Run a currently supported PHP version that matches your Laravel release — check the framework's documentation for the exact minimum. Laravel needs a standard set of PHP extensions enabled; on a Debian/Ubuntu VPS they're typically installed together:
# Ubuntu/Debian: PHP-FPM + the extensions Laravel expects
sudo apt install -y php-fpm php-cli php-mbstring php-xml \
php-bcmath php-curl php-mysql php-zip php-gd php-intl
# Composer (dependency manager)
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer The exact package names vary by distribution, but the extensions to confirm are mbstring, xml, bcmath, curl, the database driver (pdo_mysql or pdo_pgsql), zip and openssl. Missing one usually shows up as a clear error during composer install or at boot.
Database, cache and queue backend
Most Laravel apps pair with MySQL/MariaDB or PostgreSQL. For anything that uses queues, sessions or caching at scale, add Redis — it serves as a fast cache, session store and queue driver in one. Make sure the VPS has enough RAM for the database and Redis alongside PHP-FPM workers; memory is the resource you're most likely to exhaust.
Storage permissions
Laravel writes to two directories, storage/ and bootstrap/cache/. The web-server user (often www-data) must be able to write to them, or you'll get permission errors that look like application bugs:
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R ug+rwX storage bootstrap/cache
Deployment walkthrough
1. Get the code and install dependencies
Pull the repository onto the server, then install the production dependencies. The flags below skip dev packages and build an optimised autoloader:
git clone https://github.com/you/your-app.git /var/www/app
cd /var/www/app
composer install --no-dev --optimize-autoloader 2. Configure the environment
Laravel reads configuration from a .env file that is never committed to git. Copy the example, generate the app key, and fill in the real values:
cp .env.example .env
php artisan key:generate A minimal production .env looks like this — set APP_DEBUG to false so errors aren't leaked to visitors:
APP_NAME="Your App"
APP_ENV=production
APP_KEY=base64:...generated...
APP_DEBUG=false
APP_URL=https://yourapp.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=yourapp
DB_USERNAME=yourapp
DB_PASSWORD=change-me
CACHE_STORE=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
REDIS_HOST=127.0.0.1 3. Migrate and cache for production
Run the database migrations, then build Laravel's cached config, routes and views. Caching these is what makes the framework fast in production — but remember to rebuild them on every deploy, since they freeze the current state:
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache 4. Point Nginx at public/
The web server's document root must be the public/ directory — never the project root, or you'd expose .env and source files. A standard Nginx + PHP-FPM server block:
server {
listen 80;
server_name yourapp.com;
root /var/www/app/public;
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
} Add HTTPS with a free automated certificate (most hosts and tools like Certbot make this a one-liner) — a production app should always be served over TLS.
5. Run the queue worker with Supervisor
Background jobs (emails, notifications, exports) run through a queue worker, a long-running process that must survive crashes and reboots. Don't run php artisan queue:work by hand in an SSH session — use a process manager like Supervisor to keep it alive:
# /etc/supervisor/conf.d/app-worker.conf
[program:app-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/worker.log
stopwaitsecs=3600 sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start app-worker:* 6. Schedule the scheduler
Laravel's task scheduler is driven by a single cron entry that runs every minute; Laravel itself decides which tasks are due. Add it to the deploy user's crontab:
* * * * * cd /var/www/app && php artisan schedule:run >> /dev/null 2>&1 Sizing the VPS
| App profile | What it needs |
|---|---|
| Small app, light traffic | Entry VPS: a couple of GB RAM, NVMe storage, 1–2 vCPUs — PHP-FPM plus a small database. |
| Queues + Redis + database | More RAM headroom so Redis, the database and several PHP-FPM and worker processes coexist comfortably. |
| Higher traffic / heavy jobs | Dedicated vCPUs for steady load, more worker processes, room to scale up; consider separating the database later. |
Who it's for
- Prefer not to manage a server: a managed platform or managed VPS handles patching and process supervision for you, at a higher price.
- Comfortable on the command line: an unmanaged VPS is cheaper and fully flexible — you own the PHP, Nginx, Supervisor and cron setup shown above.
- Growing app with queues and cache: a VPS with guaranteed RAM/CPU and Redis is the natural fit; size memory first, then CPU, then storage.
How to decide
Start from what Laravel needs to run well: SSH access, a supported PHP with the right extensions, a database and (for queues/cache) Redis, writable storage, a queue worker under Supervisor and a one-line cron for the scheduler. A VPS gives you all of that with guaranteed resources and a clear path to scale — match the plan to your traffic and background-job load, and pick the smallest one that covers it with headroom.