Laravel
Deployment
Deploying a Laravel application to production involves configuring a web server, securing the environment, optimizing performance with Artisan commands, and setting up background workers and SSL certificates.
Server Requirements: Laravel requires specific PHP extensions and a minimum PHP version. Verify these are met before deploying.
Terminal – Check Requirements
BASH
# Laravel 11 requires PHP >= 8.2
php --version
# Required PHP extensions:
# BCMath, Ctype, cURL, DOM, Fileinfo, JSON, Mbstring,
# OpenSSL, PCRE, PDO, Tokenizer, XML
# Check loaded extensions
php -m | grep -E "bcmath|ctype|curl|dom|fileinfo|json|mbstring|openssl|pdo|xml"
# Install missing extensions on Ubuntu/Debian
sudo apt install php8.2-bcmath php8.2-xml php8.2-mbstring php8.2-curl \
php8.2-zip php8.2-gd php8.2-mysql php8.2-fpm
# Install Composer on server
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
# Install Node.js (for building assets)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
Nginx Configuration: Configure Nginx to serve your Laravel application. The
public directory is the web root — never expose the project root./etc/nginx/sites-available/myapp.com
BASH
server {
listen 80;
listen [::]:80;
server_name myapp.com www.myapp.com;
# Redirect HTTP to HTTPS (after SSL is set up)
# return 301 https://$host$request_uri;
root /var/www/myapp/public;
index index.php index.html;
charset utf-8;
client_max_body_size 20M;
# Serve static files or fall through to Laravel
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Handle PHP via 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;
fastcgi_index index.php;
}
# Deny access to hidden files (.env, .git, etc.)
location ~ /\.(?!well-known).* {
deny all;
}
# Gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript;
error_log /var/log/nginx/myapp_error.log;
access_log /var/log/nginx/myapp_access.log;
}
# Enable site and reload Nginx
# sudo ln -s /etc/nginx/sites-available/myapp.com /etc/nginx/sites-enabled/
# sudo nginx -t && sudo systemctl reload nginx
Deploying the Application: A typical deployment workflow: pull code, install dependencies, build assets, run migrations, and clear/rebuild caches.
deploy.sh – Deployment Script
BASH
#!/bin/bash
set -e
APP_DIR="/var/www/myapp"
cd $APP_DIR
echo "==> Pulling latest code..."
git pull origin main
echo "==> Installing PHP dependencies..."
composer install --no-dev --optimize-autoloader --no-interaction
echo "==> Installing Node dependencies and building assets..."
npm ci
npm run build
echo "==> Running database migrations..."
php artisan migrate --force
echo "==> Running database seeders (if needed)..."
# php artisan db:seed --class=ProductionSeeder --force
echo "==> Caching configuration, routes, and views..."
php artisan optimize
echo "==> Restarting queue workers..."
php artisan queue:restart
echo "==> Clearing old compiled files..."
php artisan view:clear
php artisan event:clear
echo "✅ Deployment complete!"
Environment Setup on Production: Never copy your local
.env to production. Create it fresh on the server with production-specific values..env (Production)
BASH
APP_NAME="My App"
APP_ENV=production
APP_KEY=base64:YOUR_GENERATED_KEY_HERE # php artisan key:generate
APP_DEBUG=false # MUST be false in production
APP_URL=https://myapp.com
LOG_CHANNEL=stack
LOG_LEVEL=error # Only log errors in production
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=myapp_production
DB_USERNAME=myapp_user
DB_PASSWORD=strong_random_password_here
CACHE_STORE=redis # Use Redis for caching
SESSION_DRIVER=redis # Use Redis for sessions
QUEUE_CONNECTION=redis # Use Redis for queues
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=your@mailgun.org
MAIL_PASSWORD=your_mailgun_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@myapp.com
# Set proper file permissions after creating .env
# chmod 600 /var/www/myapp/.env
Artisan Production Optimization Commands: Cache configuration, routes, and views to significantly improve performance in production.
Terminal – Optimization
BASH
# Cache all configuration files into a single file
php artisan config:cache
# Cache all routes into a single file
php artisan route:cache
# Compile all Blade templates
php artisan view:cache
# Cache event/listener discovery
php artisan event:cache
# ✅ Run all of the above at once (recommended):
php artisan optimize
# --- Clearing caches (when you deploy new code) ---
php artisan optimize:clear # Clears config, route, view, event caches
php artisan cache:clear # Clears application cache (Redis/file)
# Set proper file/directory permissions
sudo chown -R www-data:www-data /var/www/myapp
sudo chmod -R 755 /var/www/myapp
sudo chmod -R 775 /var/www/myapp/storage
sudo chmod -R 775 /var/www/myapp/bootstrap/cache
# Create storage symlink for public disk
php artisan storage:link
Supervisor for Queue Workers: Supervisor is a process control system that keeps your Laravel queue workers running. If a worker crashes, Supervisor automatically restarts it.
/etc/supervisor/conf.d/laravel-worker.conf
BASH
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/myapp/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=4 ; Number of worker processes
redirect_stderr=true
stdout_logfile=/var/log/supervisor/laravel-worker.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=3
stopwaitsecs=3600 ; Allow worker to finish current job before stopping
# --- Install and configure Supervisor ---
# sudo apt install supervisor
# sudo nano /etc/supervisor/conf.d/laravel-worker.conf
# (paste the above config)
# --- Apply changes ---
# sudo supervisorctl reread
# sudo supervisorctl update
# sudo supervisorctl start laravel-worker:*
# --- Common Supervisor commands ---
# sudo supervisorctl status # Check worker status
# sudo supervisorctl restart laravel-worker:* # Restart workers
# sudo supervisorctl stop laravel-worker:* # Stop workers
SSL Certificate with Certbot (Let's Encrypt): Secure your application with a free SSL certificate using Certbot. It automatically configures Nginx and sets up auto-renewal.
Terminal – Certbot SSL Setup
BASH
# Install Certbot and the Nginx plugin
sudo apt install certbot python3-certbot-nginx
# Obtain and install SSL certificate for your domain
# This automatically modifies your Nginx config for HTTPS
sudo certbot --nginx -d myapp.com -d www.myapp.com
# Certbot will prompt for:
# - Email address (for renewal alerts)
# - Agree to terms (A)
# - Redirect HTTP to HTTPS? (2 - recommended)
# Test auto-renewal (dry run)
sudo certbot renew --dry-run
# Certbot installs a systemd timer or cron job to auto-renew certificates
# You can verify it with:
sudo systemctl status certbot.timer
# --- After SSL is enabled, update your .env ---
# APP_URL=https://myapp.com
# --- Nginx HTTPS server block (auto-generated by Certbot) ---
# server {
# listen 443 ssl;
# server_name myapp.com www.myapp.com;
# ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;
# include /etc/letsencrypt/options-ssl-nginx.conf;
# ...
# }