VPS Deployment with Nginx
This guide covers deploying the backend (PHP-FPM) and frontend (static SPA) on separate subdomains using Nginx, with SSL via Let's Encrypt.
Prerequisites
- Ubuntu 22.04+ VPS with root access
- PHP 8.2+ with required extensions (mbstring, xml, curl, pgsql/mysql, redis, zip)
- Composer
- Node.js 18+ and npm
- Nginx
- PostgreSQL or MySQL
- Redis
- Certbot (for Let's Encrypt SSL)
Backend: Nginx Configuration
Create /etc/nginx/sites-available/api.yourdomain.com:
server {
listen 80;
server_name api.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
root /var/www/backend/public;
index index.php;
charset utf-8;
client_max_body_size 20M;
# Laravel pretty URLs
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP-FPM
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
# Deny dotfiles
location ~ /\.(?!well-known) {
deny all;
}
}
Frontend: Nginx Configuration
Create /etc/nginx/sites-available/app.yourdomain.com:
server {
listen 80;
server_name app.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name app.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/app.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.yourdomain.com/privkey.pem;
root /var/www/frontend/dist;
index index.html;
# SPA: route all paths to index.html
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Enable Sites
sudo ln -s /etc/nginx/sites-available/api.yourdomain.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/app.yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
SSL with Let's Encrypt
# Install certbot
sudo apt install certbot python3-certbot-nginx
# Obtain certificates
sudo certbot --nginx -d api.yourdomain.com
sudo certbot --nginx -d app.yourdomain.com
# Auto-renewal is configured automatically
# Verify with:
sudo certbot renew --dry-run
Supervisor for Queue Workers
Create /etc/supervisor/conf.d/saaskitfy-worker.conf:
[program:saaskitfy-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/backend/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/supervisor/saaskitfy-worker.log
stopwaitsecs=3600
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start saaskitfy-worker:*
File Permissions
sudo chown -R www-data:www-data /var/www/backend/storage
sudo chown -R www-data:www-data /var/www/backend/bootstrap/cache
sudo chmod -R 775 /var/www/backend/storage
sudo chmod -R 775 /var/www/backend/bootstrap/cache
Deploy Script Example
#!/bin/bash
cd /var/www/backend
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
sudo supervisorctl restart saaskitfy-worker:*
cd /var/www/frontend
git pull origin main
npm install
npm run build