Files
Laravel-Docker-Dev-Template/PRODUCTION_DEPLOYMENT.md
theRADcozaDEV 3fee3917c5 Fix deployment issues: Add db:seed to setup, fix DatabaseSeeder, add production guide
CRITICAL FIXES:
- DatabaseSeeder now calls RolePermissionSeeder to create roles, permissions, and admin user
- setup.sh and setup.bat now run db:seed automatically during setup
- Created PRODUCTION_DEPLOYMENT.md with complete production deployment guide
- Removed duplicate admin user creation from setup scripts

This ensures:
- Admin user (admin@example.com / password) is created automatically
- Roles (admin, editor, viewer) are created during setup
- Permissions are seeded properly
- Production deployments have clear step-by-step instructions
- PHP Redis extension installation documented
- All common deployment issues addressed

The 2-minute setup now truly works out of the box.
2026-03-09 11:42:25 +02:00

9.8 KiB

Production Deployment Guide

Target: Ubuntu 24.04 with Apache/Nginx + PHP-FPM (NO Docker)


Prerequisites on Production Server

1. Install Required Software

# 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

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

cd /var/www
sudo git clone https://your-repo-url.git your-domain.com
cd your-domain.com/src

2. Set Permissions

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

# Composer dependencies
composer install --no-dev --optimize-autoloader

# Node dependencies and build assets
npm install
npm run build

4. Configure Environment

# Copy appropriate .env template
cp .env.mysql .env  # or .env.pgsql or .env.sqlite

# Edit .env
nano .env

Required .env settings:

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

php artisan key:generate --force

6. Run Migrations and Seeders

# 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

# Cache configuration
php artisan config:cache

# Cache routes
php artisan route:cache

# Cache views
php artisan view:cache

# Optimize autoloader
composer dump-autoload --optimize
php artisan storage:link

Web Server Configuration

Option A: Apache with Virtual Host

Create /etc/apache2/sites-available/your-domain.com.conf:

<VirtualHost *:80>
    ServerName your-domain.com
    ServerAlias www.your-domain.com
    DocumentRoot /var/www/your-domain.com/src/public

    <Directory /var/www/your-domain.com/src/public>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/your-domain.com-error.log
    CustomLog ${APACHE_LOG_DIR}/your-domain.com-access.log combined

    # PHP-FPM
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/var/run/php/php8.3-fpm.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>

Enable site and modules:

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:

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:

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)

# 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

Create /etc/systemd/system/laravel-queue.service:

[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:

sudo systemctl enable laravel-queue
sudo systemctl start laravel-queue
sudo systemctl status laravel-queue

Scheduler Setup

Add to crontab:

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

⚠️ IMPORTANT: Change the default admin password immediately after first login!


Troubleshooting

500 Error - Check Logs

# 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

# 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:

SESSION_DOMAIN=.your-domain.com
SESSION_SECURE_COOKIE=true
APP_URL=https://your-domain.com

Clear cache:

php artisan config:clear
php artisan cache:clear

Roles Don't Exist

# Run the seeder
php artisan db:seed --class=RolePermissionSeeder

# Or run all seeders
php artisan db:seed --force

Permission Denied Errors

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

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

# 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

# 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:

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.