Introduction
This guide covers deploying the Laravel backend API to production, including optimization, database setup, queue workers, and scheduled tasks.
Prerequisites
Before deploying, ensure your server meets these requirements:
- PHP 8.2 or higher
- Composer 2.x
- MySQL 8.0+ or PostgreSQL 13+
- Web server (Nginx or Apache)
- SSL certificate installed
- SSH access to server
Production Build
1. Install Dependencies
Install Composer dependencies without development packages:
composer install --no-dev --optimize-autoloader
The --optimize-autoloader flag generates optimized class maps for better performance.
2. Optimize Autoloader
After making code changes, regenerate the optimized autoloader:
composer dump-autoload --optimize --classmap-authoritative
Use --classmap-authoritative to prevent the autoloader from scanning the filesystem, improving performance significantly.
3. Environment Configuration
Set up your production .env file:
APP_NAME="Your App Name"
APP_ENV=production
APP_KEY=base64:your-generated-key-here
APP_DEBUG=false
APP_URL=https://api.yourdomain.com
FRONTEND_URL=https://app.yourdomain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_production_database
DB_USERNAME=your_production_user
DB_PASSWORD=your_secure_password
QUEUE_CONNECTION=database
CACHE_STORE=database
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_DOMAIN=.yourdomain.com
SANCTUM_STATEFUL_DOMAINS=app.yourdomain.com
Never commit your .env file to version control. Always set APP_DEBUG=false in production.
4. Generate Application Key
If you haven’t already, generate a unique application key:
5. Cache Configuration
Cache your configuration files for better performance:
# Cache configuration files
php artisan config:cache
# Cache routes
php artisan route:cache
# Cache views
php artisan view:cache
# Cache events
php artisan event:cache
After caching, changes to configuration files won’t take effect until you run these commands again or clear the cache with php artisan cache:clear.
Database Setup
1. Run Migrations
Execute database migrations in production:
php artisan migrate --force
The --force flag is required to run migrations in production environment.
2. Seed Production Data (Optional)
If you have production seeders:
php artisan db:seed --class=ProductionSeeder --force
3. Database Backups
Set up automated database backups using Laravel’s backup package or your hosting provider’s backup solution.# Using Laravel Backup package
composer require spatie/laravel-backup
php artisan backup:run
Configure scheduled backups in your cron job.
Web Server Configuration
Nginx Configuration
Create a server block for your Laravel application:
server {
listen 80;
listen [::]:80;
server_name api.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name api.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;
fastcgi_hide_header X-Powered-By;
}
location ~ /\.(?!well-known).* {
deny all;
}
ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
}
Apache Configuration
If using Apache, ensure .htaccess is enabled:
<VirtualHost *:80>
ServerName api.yourdomain.com
Redirect permanent / https://api.yourdomain.com/
</VirtualHost>
<VirtualHost *:443>
ServerName api.yourdomain.com
DocumentRoot /var/www/yourdomain.com/public
<Directory /var/www/yourdomain.com/public>
AllowOverride All
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/api.yourdomain.com/privkey.pem
</VirtualHost>
Queue Workers
Set your queue connection in .env:
QUEUE_CONNECTION=database
Database Queue
Redis Queue
# Create jobs table
php artisan queue:table
php artisan migrate
# Install Redis
composer require predis/predis
# Configure in .env
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
2. Supervisor Configuration
Create a Supervisor configuration file at /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 --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/www/yourdomain.com/storage/logs/worker.log
stopwaitsecs=3600
3. Start Supervisor
# Reload Supervisor configuration
sudo supervisorctl reread
sudo supervisorctl update
# Start workers
sudo supervisorctl start laravel-worker:*
# Check status
sudo supervisorctl status
4. Restart Workers After Deployment
After deploying new code, restart queue workers:
php artisan queue:restart
Add this to your deployment script to ensure workers pick up the latest code.
Cron Jobs (Task Scheduling)
Laravel’s scheduler allows you to run scheduled tasks. Set up a single cron entry:
Add Cron Entry
# Edit crontab
crontab -e
# Add this line
* * * * * cd /var/www/yourdomain.com && php artisan schedule:run >> /dev/null 2>&1
This runs Laravel’s scheduler every minute, which then executes scheduled tasks defined in app/Console/Kernel.php.
Verify Scheduled Tasks
# List scheduled tasks
php artisan schedule:list
# Test scheduler
php artisan schedule:work
Security Best Practices
Set proper permissions for Laravel directories:# Set ownership
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 must be writable
sudo chmod -R 775 /var/www/yourdomain.com/storage
sudo chmod -R 775 /var/www/yourdomain.com/bootstrap/cache
Environment File Security
# Restrict .env file access
chmod 600 .env
chown www-data:www-data .env
# Ensure .env is not publicly accessible
# (Should be outside public directory)
Disable Directory Listing
In Nginx, add to server block:In Apache, add to .htaccess:
Configure rate limiting in app/Http/Kernel.php:protected $middlewareGroups = [
'api' => [
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
Adjust limits in config/sanctum.php or RouteServiceProvider.php.
1. OPcache Configuration
Enable OPcache in php.ini:
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
2. PHP-FPM Tuning
Optimize PHP-FPM pool configuration:
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
3. Database Indexing
Ensure proper database indexes:
# Review slow queries
php artisan telescope:install # Optional: for debugging
# Add indexes in migrations
Schema::table('users', function (Blueprint $table) {
$table->index('email');
});
Deployment Script
Create a deployment script deploy.sh:
#!/bin/bash
set -e
echo "🚀 Starting deployment..."
# Navigate to project directory
cd /var/www/yourdomain.com
# Enable maintenance mode
echo "📦 Enabling maintenance mode..."
php artisan down
# Pull latest code
echo "⬇️ Pulling latest code..."
git pull origin main
# Install/update dependencies
echo "📚 Installing dependencies..."
composer install --no-dev --optimize-autoloader --no-interaction
# Clear and cache config
echo "🔄 Clearing caches..."
php artisan config:clear
php artisan cache:clear
echo "💾 Caching configuration..."
php artisan config:cache
php artisan route:cache
php artisan view:cache
# Run migrations
echo "🗄️ Running migrations..."
php artisan migrate --force
# Restart queue workers
echo "🔄 Restarting queue workers..."
php artisan queue:restart
# Disable maintenance mode
echo "✅ Disabling maintenance mode..."
php artisan up
echo "🎉 Deployment completed successfully!"
Make it executable:
Monitoring and Logging
In config/logging.php, set up appropriate channels:
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'slack'],
'ignore_exceptions' => false,
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'error'),
'days' => 14,
],
],
Error Tracking
Integrate error tracking services:
composer require sentry/sentry-laravel
php artisan sentry:publish --dsn=your-sentry-dsn
composer require bugsnag/bugsnag-laravel
php artisan vendor:publish --provider="Bugsnag\BugsnagLaravel\BugsnagServiceProvider"
Health Check Endpoint
Create a health check endpoint for monitoring:
Route::get('/health', function () {
return response()->json([
'status' => 'ok',
'timestamp' => now()->toISOString(),
'services' => [
'database' => DB::connection()->getPdo() ? 'ok' : 'error',
'cache' => Cache::has('health-check') ? 'ok' : 'error',
],
]);
});
Troubleshooting
500 Internal Server Error
- Check Laravel logs:
storage/logs/laravel.log
- Verify file permissions
- Ensure
.env exists and is readable
- Check web server error logs
Queue Jobs Not Processing
- Check Supervisor status:
sudo supervisorctl status
- View worker logs:
storage/logs/worker.log
- Restart workers:
sudo supervisorctl restart laravel-worker:*
- Check queue connection in
.env
- Verify
FRONTEND_URL in .env
- Check
config/cors.php configuration
- Clear config cache:
php artisan config:clear
- Ensure SSL is properly configured
Database Connection Failed
- Verify database credentials in
.env
- Check database server is running
- Test connection:
php artisan tinker then DB::connection()->getPdo();
- Verify firewall rules allow database access
Next Steps
Frontend Deployment
Deploy your Next.js frontend application
Environment Variables
Configure production environment variables
Backend Testing
Learn about testing your Laravel backend
API Routes
Review your API endpoint structure