# Production Deployment Guide **Target**: Ubuntu 24.04 with Apache/Nginx + PHP-FPM (NO Docker) --- ## Prerequisites on Production Server ### 1. Install Required Software ```bash # Update system sudo apt update && sudo apt upgrade -y # Install PHP 8.3+ and extensions sudo apt install -y php8.3 php8.3-fpm php8.3-cli php8.3-common \ php8.3-mysql php8.3-pgsql php8.3-sqlite3 \ php8.3-redis php8.3-curl php8.3-mbstring php8.3-xml \ php8.3-zip php8.3-bcmath php8.3-gd php8.3-intl # Install web server (choose one) sudo apt install -y apache2 # OR nginx # Install database (choose one) sudo apt install -y mysql-server # OR postgresql # Install Redis sudo apt install -y redis-server # Install Composer curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer # Install Node.js (for frontend assets) curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt install -y nodejs ``` ### 2. Verify PHP Extensions ```bash php -m | grep -E "redis|pdo_mysql|mbstring|xml|curl|zip|bcmath|gd" ``` All should be listed. If not, install missing extensions. --- ## Deployment Steps ### 1. Clone Repository ```bash cd /var/www sudo git clone https://your-repo-url.git your-domain.com cd your-domain.com/src ``` ### 2. Set Permissions ```bash sudo chown -R www-data:www-data /var/www/your-domain.com sudo chmod -R 775 /var/www/your-domain.com/src/storage sudo chmod -R 775 /var/www/your-domain.com/src/bootstrap/cache ``` ### 3. Install Dependencies ```bash # Composer dependencies composer install --no-dev --optimize-autoloader # Node dependencies and build assets npm install npm run build ``` ### 4. Configure Environment ```bash # Copy appropriate .env template cp .env.mysql .env # or .env.pgsql or .env.sqlite # Edit .env nano .env ``` **Required .env settings:** ```env APP_NAME="Your App Name" APP_ENV=production APP_DEBUG=false APP_URL=https://your-domain.com DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=your_database DB_USERNAME=your_user DB_PASSWORD=your_password CACHE_STORE=redis QUEUE_CONNECTION=redis SESSION_DRIVER=database SESSION_DOMAIN=.your-domain.com SESSION_SECURE_COOKIE=true REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_MAILER=smtp MAIL_HOST=your-smtp-host MAIL_PORT=587 MAIL_USERNAME=your-email MAIL_PASSWORD=your-password MAIL_ENCRYPTION=tls MAIL_FROM_ADDRESS=noreply@your-domain.com MAIL_FROM_NAME="${APP_NAME}" ``` ### 5. Generate Application Key ```bash php artisan key:generate --force ``` ### 6. Run Migrations and Seeders ```bash # Run migrations php artisan migrate --force # CRITICAL: Run seeders to create roles, permissions, and admin user php artisan db:seed --force ``` This creates: - **Admin user**: admin@example.com / password - **Roles**: admin, editor, viewer - **Permissions**: users.view, users.create, users.edit, users.delete, settings.manage ### 7. Optimize for Production ```bash # Cache configuration php artisan config:cache # Cache routes php artisan route:cache # Cache views php artisan view:cache # Optimize autoloader composer dump-autoload --optimize ``` ### 8. Create Storage Link ```bash php artisan storage:link ``` --- ## Web Server Configuration ### Option A: Apache with Virtual Host Create `/etc/apache2/sites-available/your-domain.com.conf`: ```apache ServerName your-domain.com ServerAlias www.your-domain.com DocumentRoot /var/www/your-domain.com/src/public Options -Indexes +FollowSymLinks AllowOverride All Require all granted ErrorLog ${APACHE_LOG_DIR}/your-domain.com-error.log CustomLog ${APACHE_LOG_DIR}/your-domain.com-access.log combined # PHP-FPM SetHandler "proxy:unix:/var/run/php/php8.3-fpm.sock|fcgi://localhost" ``` Enable site and modules: ```bash sudo a2enmod rewrite proxy_fcgi setenvif sudo a2enconf php8.3-fpm sudo a2ensite your-domain.com.conf sudo systemctl restart apache2 ``` ### Option B: Nginx with Server Block Create `/etc/nginx/sites-available/your-domain.com`: ```nginx server { listen 80; listen [::]:80; server_name your-domain.com www.your-domain.com; root /var/www/your-domain.com/src/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.3-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } } ``` Enable site: ```bash sudo ln -s /etc/nginx/sites-available/your-domain.com /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl restart nginx ``` --- ## SSL Certificate (Let's Encrypt) ```bash # Install Certbot sudo apt install -y certbot python3-certbot-apache # For Apache # OR sudo apt install -y certbot python3-certbot-nginx # For Nginx # Get certificate sudo certbot --apache -d your-domain.com -d www.your-domain.com # Apache # OR sudo certbot --nginx -d your-domain.com -d www.your-domain.com # Nginx # Auto-renewal is set up automatically ``` --- ## Queue Worker Setup (Optional but Recommended) Create `/etc/systemd/system/laravel-queue.service`: ```ini [Unit] Description=Laravel Queue Worker After=network.target [Service] Type=simple User=www-data Group=www-data Restart=always ExecStart=/usr/bin/php /var/www/your-domain.com/src/artisan queue:work --sleep=3 --tries=3 --max-time=3600 [Install] WantedBy=multi-user.target ``` Enable and start: ```bash sudo systemctl enable laravel-queue sudo systemctl start laravel-queue sudo systemctl status laravel-queue ``` --- ## Scheduler Setup Add to crontab: ```bash sudo crontab -e -u www-data ``` Add this line: ``` * * * * * cd /var/www/your-domain.com/src && php artisan schedule:run >> /dev/null 2>&1 ``` --- ## Post-Deployment Checklist - [ ] PHP Redis extension installed: `php -m | grep redis` - [ ] Database migrations run: `php artisan migrate --force` - [ ] **Database seeded**: `php artisan db:seed --force` ✅ CRITICAL - [ ] Storage permissions set: `chmod -R 775 storage bootstrap/cache` - [ ] Storage link created: `php artisan storage:link` - [ ] Config cached: `php artisan config:cache` - [ ] Routes cached: `php artisan route:cache` - [ ] Views cached: `php artisan view:cache` - [ ] SSL certificate installed - [ ] Queue worker running (if using queues) - [ ] Scheduler configured (if using scheduled tasks) - [ ] Admin user created and can login at `/admin` - [ ] `.env` has `APP_DEBUG=false` and `APP_ENV=production` --- ## Access Your Application - **Public Site**: https://your-domain.com - **Admin Panel**: https://your-domain.com/admin - **Admin Login**: admin@example.com / password **⚠️ IMPORTANT**: Change the default admin password immediately after first login! --- ## Troubleshooting ### 500 Error - Check Logs ```bash # Laravel logs tail -f /var/www/your-domain.com/src/storage/logs/laravel.log # Apache logs sudo tail -f /var/log/apache2/your-domain.com-error.log # Nginx logs sudo tail -f /var/log/nginx/error.log # PHP-FPM logs sudo tail -f /var/log/php8.3-fpm.log ``` ### Class "Redis" not found ```bash # Install PHP Redis extension sudo apt install php8.3-redis # Restart PHP-FPM sudo systemctl restart php8.3-fpm # Restart web server sudo systemctl restart apache2 # or nginx ``` ### 419 Page Expired (CSRF) Check `.env`: ```env SESSION_DOMAIN=.your-domain.com SESSION_SECURE_COOKIE=true APP_URL=https://your-domain.com ``` Clear cache: ```bash php artisan config:clear php artisan cache:clear ``` ### Roles Don't Exist ```bash # Run the seeder php artisan db:seed --class=RolePermissionSeeder # Or run all seeders php artisan db:seed --force ``` ### Permission Denied Errors ```bash sudo chown -R www-data:www-data /var/www/your-domain.com sudo chmod -R 775 /var/www/your-domain.com/src/storage sudo chmod -R 775 /var/www/your-domain.com/src/bootstrap/cache ``` --- ## Updating the Application ```bash cd /var/www/your-domain.com # Pull latest code sudo -u www-data git pull # Update dependencies cd src composer install --no-dev --optimize-autoloader npm install && npm run build # Run migrations php artisan migrate --force # Clear and recache php artisan config:clear php artisan cache:clear php artisan config:cache php artisan route:cache php artisan view:cache # Restart queue worker sudo systemctl restart laravel-queue # Restart web server sudo systemctl restart apache2 # or nginx ``` --- ## Security Recommendations 1. **Change default admin password** immediately 2. **Set up firewall**: `sudo ufw enable && sudo ufw allow 80,443/tcp` 3. **Disable directory listing** in web server config 4. **Keep system updated**: `sudo apt update && sudo apt upgrade` 5. **Use strong database passwords** 6. **Enable fail2ban**: `sudo apt install fail2ban` 7. **Regular backups** of database and uploaded files 8. **Monitor logs** for suspicious activity --- ## Backup Strategy ### Database Backup ```bash # MySQL mysqldump -u username -p database_name > backup_$(date +%Y%m%d).sql # PostgreSQL pg_dump -U username database_name > backup_$(date +%Y%m%d).sql ``` ### Files Backup ```bash # Backup storage directory tar -czf storage_backup_$(date +%Y%m%d).tar.gz /var/www/your-domain.com/src/storage ``` ### Automated Backups Add to crontab: ```bash 0 2 * * * /path/to/backup-script.sh ``` --- **Remember**: The template is designed for quick local development with Docker. Production deployment requires proper server setup and security hardening.