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;
#     ...
# }