Laravel Framework

Deployment & Production Best Practices

18 min Lesson 30 of 45

Deployment & Production Best Practices

Deploying a Laravel application to production requires careful planning and configuration. In this lesson, we'll cover everything you need to know about preparing, deploying, and maintaining a Laravel application in a production environment.

Server Requirements

Ensure your production server meets Laravel's requirements:

# Minimum Requirements - PHP >= 8.1 - Composer - OpenSSL PHP Extension - PDO PHP Extension - Mbstring PHP Extension - Tokenizer PHP Extension - XML PHP Extension - Ctype PHP Extension - JSON PHP Extension - BCMath PHP Extension (for Cashier, etc.) # Web Server - Apache with mod_rewrite OR - Nginx # Database - MySQL 5.7+ / MariaDB 10.3+ - PostgreSQL 10.0+ - SQLite 3.8.8+ - SQL Server 2017+ # Additional Tools - Redis (for cache and queues) - Supervisor (for queue workers) - Node.js and npm (for frontend assets)

Environment Configuration

Properly configure your .env file for production:

# Application APP_NAME="Your Application" APP_ENV=production APP_KEY=base64:GENERATE_THIS_WITH_php_artisan_key:generate APP_DEBUG=false APP_URL=https://yourdomain.com # Database DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=your_database DB_USERNAME=your_username DB_PASSWORD=your_secure_password # Cache & Sessions CACHE_DRIVER=redis SESSION_DRIVER=redis QUEUE_CONNECTION=redis # Redis REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 # Mail MAIL_MAILER=smtp MAIL_HOST=your-mail-server.com MAIL_PORT=587 MAIL_USERNAME=your-username MAIL_PASSWORD=your-password MAIL_ENCRYPTION=tls MAIL_FROM_ADDRESS=noreply@yourdomain.com MAIL_FROM_NAME="${APP_NAME}" # Logging LOG_CHANNEL=stack LOG_LEVEL=error
Warning: NEVER set APP_DEBUG=true in production! This exposes sensitive information like database credentials and application structure to potential attackers.

Optimization Commands

Run these commands before deploying or after any changes:

# Generate application key (only once, on first deployment) php artisan key:generate # Cache configuration php artisan config:cache # Cache routes php artisan route:cache # Cache views php artisan view:cache # Cache events php artisan event:cache # Optimize autoloader composer install --optimize-autoloader --no-dev # Clear all caches (if needed) php artisan optimize:clear # Run all optimizations at once php artisan optimize

Web Server Configuration

Nginx Configuration

server { listen 80; listen [::]:80; server_name yourdomain.com; root /var/www/yourdomain.com/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } }

Apache Configuration

<VirtualHost *:80> ServerName yourdomain.com DocumentRoot /var/www/yourdomain.com/public <Directory /var/www/yourdomain.com/public> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> ErrorLog ${APACHE_LOG_DIR}/yourdomain-error.log CustomLog ${APACHE_LOG_DIR}/yourdomain-access.log combined </VirtualHost>

File Permissions

Set proper permissions for security:

# Set ownership (replace www-data with your web server user) sudo chown -R www-data:www-data /var/www/yourdomain.com # Set directory permissions sudo find /var/www/yourdomain.com -type d -exec chmod 755 {} \; # Set file permissions sudo find /var/www/yourdomain.com -type f -exec chmod 644 {} \; # Storage and cache need write permissions sudo chmod -R 775 /var/www/yourdomain.com/storage sudo chmod -R 775 /var/www/yourdomain.com/bootstrap/cache

Database Migration

Handle database migrations carefully in production:

# Backup database before migrating mysqldump -u username -p database_name > backup_$(date +%Y%m%d_%H%M%S).sql # Run migrations php artisan migrate --force # Rollback if needed (have a plan!) php artisan migrate:rollback --step=1 # Check migration status php artisan migrate:status # Run seeders (be careful!) php artisan db:seed --class=ProductionSeeder
Tip: Always test migrations on a staging environment that mirrors production before running them on live data.

Queue Workers with Supervisor

Keep queue workers running with Supervisor:

# Install Supervisor sudo apt-get install supervisor # Create Supervisor configuration # /etc/supervisor/conf.d/laravel-worker.conf [program:laravel-worker] process_name=%(program_name)s_%(process_num)02d command=php /var/www/yourdomain.com/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600 autostart=true autorestart=true stopasgroup=true killasgroup=true user=www-data numprocs=4 redirect_stderr=true stdout_logfile=/var/www/yourdomain.com/storage/logs/worker.log stopwaitsecs=3600 # Reload Supervisor sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start laravel-worker:* # Check status sudo supervisorctl status

Task Scheduling

Set up the Laravel scheduler with cron:

# Edit crontab crontab -e # Add Laravel scheduler (runs every minute) * * * * * cd /var/www/yourdomain.com && php artisan schedule:run >> /dev/null 2>&1 # Verify it's working php artisan schedule:list

Zero-Downtime Deployment

Deploy without downtime using symbolic links:

# Directory structure /var/www/yourdomain.com/ ├── current -> releases/20240115120000 ├── releases/ │ ├── 20240115120000/ │ ├── 20240114110000/ │ └── 20240113100000/ └── shared/ ├── .env └── storage/ # Deployment script (deploy.sh) #!/bin/bash RELEASE=$(date +%Y%m%d%H%M%S) APP_DIR=/var/www/yourdomain.com RELEASE_DIR=$APP_DIR/releases/$RELEASE # Create release directory mkdir -p $RELEASE_DIR # Clone/copy code to release directory git clone --depth 1 git@github.com:username/repo.git $RELEASE_DIR # Link shared files ln -s $APP_DIR/shared/.env $RELEASE_DIR/.env ln -s $APP_DIR/shared/storage $RELEASE_DIR/storage # Install dependencies cd $RELEASE_DIR composer install --optimize-autoloader --no-dev # Run optimizations php artisan config:cache php artisan route:cache php artisan view:cache # Run migrations php artisan migrate --force # Switch to new release (atomic operation) ln -sfn $RELEASE_DIR $APP_DIR/current # Reload PHP-FPM sudo systemctl reload php8.2-fpm # Restart queue workers php artisan queue:restart # Keep only last 5 releases cd $APP_DIR/releases && ls -t | tail -n +6 | xargs rm -rf echo "Deployment completed: $RELEASE"

SSL/TLS Configuration

Secure your application with HTTPS using Let's Encrypt:

# Install Certbot sudo apt-get install certbot python3-certbot-nginx # For Nginx sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com # For Apache sudo certbot --apache -d yourdomain.com -d www.yourdomain.com # Auto-renewal (already configured by Certbot) sudo certbot renew --dry-run # Force HTTPS in Laravel // app/Providers/AppServiceProvider.php use Illuminate\Support\Facades\URL; public function boot() { if ($this->app->environment('production')) { URL::forceScheme('https'); } }

Laravel Forge

Simplify deployment with Laravel Forge (paid service):

  • Server Provisioning: Automatically set up servers on DigitalOcean, AWS, Linode, etc.
  • Deployment: Push-to-deploy from GitHub, GitLab, or Bitbucket
  • SSL Certificates: One-click Let's Encrypt integration
  • Queue Workers: Automatic Supervisor configuration
  • Scheduled Jobs: Visual cron job management
  • Monitoring: Server monitoring and alerts
# Forge deployment script (runs automatically on git push) cd /home/forge/yourdomain.com git pull origin main composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader php artisan migrate --force php artisan config:cache php artisan route:cache php artisan view:cache php artisan queue:restart php artisan optimize

Laravel Vapor

Serverless deployment on AWS Lambda:

  • Auto-scaling: Automatically scales to handle traffic
  • Pay Per Use: Only pay for actual usage
  • Zero Downtime: Built-in zero-downtime deployments
  • Database: Works with RDS, Aurora Serverless
  • Assets: CloudFront CDN integration
  • Queues: SQS integration for job queues
# Install Vapor CLI composer require laravel/vapor-cli # Login vapor login # Initialize project vapor init # Deploy to production vapor deploy production

Monitoring and Logging

Set up proper monitoring for production:

# Configure logging // config/logging.php 'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => ['daily', 'slack'], 'ignore_exceptions' => false, ], 'slack' => [ 'driver' => 'slack', 'url' => env('LOG_SLACK_WEBHOOK_URL'), 'username' => 'Laravel Log', 'emoji' => ':boom:', 'level' => 'critical', ], ], # Use monitoring services - Sentry (error tracking) - New Relic (APM) - Datadog (infrastructure monitoring) - Laravel Telescope (development debugging) - Laravel Horizon (queue monitoring)

Exercise 1: Production Checklist

Create a comprehensive deployment checklist:

  1. Verify all environment variables are set correctly
  2. Ensure APP_DEBUG is false
  3. Set up proper logging with Slack notifications
  4. Configure Redis for cache and queues
  5. Set up Supervisor for queue workers
  6. Configure cron for task scheduling
  7. Run all optimization commands
  8. Set proper file permissions

Exercise 2: Deployment Script

Create a zero-downtime deployment script:

  1. Write a bash script that creates timestamped releases
  2. Include steps for: clone code, install dependencies, run migrations
  3. Use symbolic links to switch between releases
  4. Keep only the last 5 releases
  5. Add rollback functionality
  6. Test the script on a staging server

Exercise 3: SSL and Security

Implement security best practices:

  1. Install and configure SSL certificate with Let's Encrypt
  2. Force HTTPS in Laravel
  3. Add security headers in web server config
  4. Set up automatic certificate renewal
  5. Configure rate limiting for API endpoints
  6. Test SSL configuration with SSL Labs

Production Checklist

  • Environment: APP_ENV=production, APP_DEBUG=false
  • Security: HTTPS enabled, security headers configured
  • Optimization: All cache commands run, autoloader optimized
  • Database: Proper credentials, connection pooling enabled
  • Cache: Redis configured for cache, session, and queues
  • Queue: Supervisor running workers, queue:restart on deploy
  • Schedule: Cron job configured for scheduler
  • Logging: Error logging to files and monitoring service
  • Backups: Automated database and file backups
  • Monitoring: Application and server monitoring enabled

Summary

In this lesson, you learned:

  • Server requirements for Laravel applications
  • Proper environment configuration for production
  • Essential optimization commands
  • Web server configuration for Nginx and Apache
  • File permissions and security
  • Database migration strategies
  • Queue worker management with Supervisor
  • Zero-downtime deployment techniques
  • SSL/TLS configuration with Let's Encrypt
  • Using Laravel Forge and Vapor for deployment
  • Monitoring and logging best practices

Congratulations! You've completed the Laravel Framework tutorial. You now have the knowledge to build, test, and deploy professional Laravel applications. Remember: deployment is not the end—continuous monitoring, maintenance, and improvement are essential for production applications. Keep learning, stay updated with Laravel releases, and build amazing things!