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:
- Verify all environment variables are set correctly
- Ensure APP_DEBUG is false
- Set up proper logging with Slack notifications
- Configure Redis for cache and queues
- Set up Supervisor for queue workers
- Configure cron for task scheduling
- Run all optimization commands
- Set proper file permissions
Exercise 2: Deployment Script
Create a zero-downtime deployment script:
- Write a bash script that creates timestamped releases
- Include steps for: clone code, install dependencies, run migrations
- Use symbolic links to switch between releases
- Keep only the last 5 releases
- Add rollback functionality
- Test the script on a staging server
Exercise 3: SSL and Security
Implement security best practices:
- Install and configure SSL certificate with Let's Encrypt
- Force HTTPS in Laravel
- Add security headers in web server config
- Set up automatic certificate renewal
- Configure rate limiting for API endpoints
- 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!