Skip to main content

Overview

Deploying GB App to production requires careful attention to security, performance, and reliability. This guide covers essential production configuration and best practices.

Pre-Deployment Checklist

1

Environment Configuration

  • Set APP_ENV=production
  • Set APP_DEBUG=false
  • Generate unique APP_KEY
  • Configure production database credentials
  • Set up production mail server
  • Configure Power BI API credentials
  • Set up LDAP connection (if using)
  • Configure proper APP_URL
2

Security Hardening

  • Use HTTPS (SSL/TLS certificate)
  • Strong database passwords
  • Secure .env file permissions (chmod 600)
  • Disable directory listing
  • Configure firewall rules
  • Set up fail2ban or similar
  • Enable CSRF protection
  • Configure secure session cookies
3

Performance Optimization

  • Enable OPcache
  • Configure Redis for cache/sessions
  • Set up queue workers
  • Optimize Composer autoloader
  • Cache configuration and routes
  • Minimize and version assets
  • Configure CDN (if applicable)
4

Backup Strategy

  • Set up automated database backups
  • Configure file storage backups
  • Test backup restoration
  • Document backup procedures
5

Monitoring

  • Set up application logging
  • Configure error tracking (Sentry, Bugsnag)
  • Monitor server resources
  • Set up uptime monitoring
  • Configure alerts

Environment Configuration

Production .env Settings

.env
# Application
APP_NAME="GB App"
APP_ENV=production
APP_KEY=base64:your-generated-key-here
APP_DEBUG=false
APP_URL=https://powerbi.yourdomain.com

# Logging
LOG_CHANNEL=daily
LOG_LEVEL=warning
LOG_DEPRECATIONS_CHANNEL=null

# Database
DB_CONNECTION=mysql
DB_HOST=production-db-server.internal
DB_PORT=3306
DB_DATABASE=gb_app_production
DB_USERNAME=gb_app_user
DB_PASSWORD=strong-secure-password-here

# Cache and Sessions
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis

# Redis
REDIS_HOST=redis.internal
REDIS_PASSWORD=redis-secure-password
REDIS_PORT=6379

# Mail
MAIL_MAILER=smtp
MAIL_HOST=smtp.yourdomain.com
MAIL_PORT=587
MAIL_USERNAME=[email protected]
MAIL_PASSWORD=mail-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="[email protected]"
MAIL_FROM_NAME="${APP_NAME}"

# Power BI
POWERBI_CLIENT_ID=your-azure-app-id
POWERBI_CLIENT_SECRET=your-azure-app-secret
POWERBI_GRANT_TYPE=client_credentials
POWERBI_RESOURCE=https://analysis.windows.net/powerbi/api

# LDAP
LDAP_LOGGING=false
LDAP_CONNECTION=default
LDAP_HOST=dc.yourdomain.com
LDAP_USERNAME=cn=ldapbind,ou=ServiceAccounts,dc=yourdomain,dc=com
LDAP_PASSWORD=ldap-bind-password
LDAP_PORT=636
LDAP_BASE_DN=dc=yourdomain,dc=com
LDAP_TIMEOUT=5
LDAP_SSL=true
LDAP_TLS=false

Security Hardening

SSL/TLS Configuration

Always use HTTPS in production.

Nginx SSL Configuration

nginx/default.conf
server {
    listen 80;
    server_name powerbi.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name powerbi.yourdomain.com;

    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    client_max_body_size 100M;
    
    root /var/www/html/public;
    index index.php;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param HTTPS on;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

File Permissions

# Set proper ownership
sudo chown -R www-data:www-data /var/www/html

# Set directory permissions
find /var/www/html -type d -exec chmod 755 {} \;

# Set file permissions
find /var/www/html -type f -exec chmod 644 {} \;

# Secure storage and cache
chmod -R 775 /var/www/html/storage
chmod -R 775 /var/www/html/bootstrap/cache

# Secure .env file
chmod 600 /var/www/html/.env
chown www-data:www-data /var/www/html/.env

Database Security

-- Create dedicated database user
CREATE USER 'gb_app_user'@'app-server-ip' IDENTIFIED BY 'strong-password';

-- Grant only necessary privileges
GRANT SELECT, INSERT, UPDATE, DELETE ON gb_app_production.* TO 'gb_app_user'@'app-server-ip';

-- No CREATE, DROP, or ALTER privileges
FLUSH PRIVILEGES;

Firewall Configuration

# Allow SSH
sudo ufw allow 22/tcp

# Allow HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Allow MySQL from app server only
sudo ufw allow from app-server-ip to any port 3306

# Enable firewall
sudo ufw enable

Performance Optimization

PHP OPcache Configuration

Add to 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

Laravel Optimization

# Cache configuration
php artisan config:cache

# Cache routes
php artisan route:cache

# Cache views
php artisan view:cache

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

# Clear unnecessary caches first if needed
php artisan optimize:clear

Redis Configuration

Update .env for Redis caching:
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=secure-redis-password
REDIS_PORT=6379
REDIS_DB=0
REDIS_CACHE_DB=1
Install Redis:
docker compose exec app apt-get install redis-server
Or add Redis service to docker-compose.yml:
redis:
    image: redis:7-alpine
    container_name: gb-app-redis
    command: redis-server --requirepass your-redis-password
    ports:
        - "6379:6379"
    volumes:
        - redis_data:/data
    restart: unless-stopped

volumes:
    db_data:
    redis_data:

Queue Workers

Set up queue workers for background jobs:
# Install supervisor
apt-get install supervisor

# Create supervisor config
cat > /etc/supervisor/conf.d/gb-app-worker.conf <<EOF
[program:gb-app-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/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/html/storage/logs/worker.log
stopwaitsecs=3600
EOF

# Reload supervisor
supervisorctl reread
supervisorctl update
supervisorctl start gb-app-worker:*

Database Optimization

-- Add indexes for common queries
ALTER TABLE user_reports ADD INDEX idx_user_id (user_id);
ALTER TABLE user_reports ADD INDEX idx_report_id (report_id);
ALTER TABLE reports ADD INDEX idx_group_report (group_id, report_id);

-- Optimize tables
OPTIMIZE TABLE users, reports, user_reports;

Monitoring and Logging

Application Logging

Configure daily log rotation:
.env
LOG_CHANNEL=daily
LOG_LEVEL=warning
Log rotation with logrotate:
cat > /etc/logrotate.d/gb-app <<EOF
/var/www/html/storage/logs/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 www-data www-data
    sharedscripts
    postrotate
        php /var/www/html/artisan cache:clear > /dev/null 2>&1
    endscript
}
EOF

Error Tracking

Integrate error tracking services:

Sentry Integration

composer require sentry/sentry-laravel
.env
SENTRY_LARAVEL_DSN=https://your-sentry-dsn
SENTRY_TRACES_SAMPLE_RATE=0.2

Server Monitoring

Install monitoring tools:
# Install htop for process monitoring
apt-get install htop

# Install iotop for disk I/O monitoring
apt-get install iotop

# Install netstat for network monitoring
apt-get install net-tools

Uptime Monitoring

Set up external uptime monitoring:
  • UptimeRobot
  • Pingdom
  • StatusCake
  • New Relic

Backup Strategy

Database Backups

#!/bin/bash
# /usr/local/bin/backup-db.sh

BACKUP_DIR="/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
FILENAME="gb_app_${DATE}.sql.gz"

mkdir -p $BACKUP_DIR

mysqldump -h db -u root -ppasswordr GBapp | gzip > "$BACKUP_DIR/$FILENAME"

# Keep only last 30 days
find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete

echo "Backup completed: $FILENAME"
Schedule with cron:
crontab -e

# Daily backup at 2 AM
0 2 * * * /usr/local/bin/backup-db.sh >> /var/log/backup.log 2>&1

File Backups

Backup storage directory:
#!/bin/bash
# /usr/local/bin/backup-files.sh

BACKUP_DIR="/backups/files"
DATE=$(date +%Y%m%d_%H%M%S)
FILENAME="gb_app_storage_${DATE}.tar.gz"

mkdir -p $BACKUP_DIR

tar -czf "$BACKUP_DIR/$FILENAME" -C /var/www/html storage

# Keep only last 7 days
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete

Backup to S3

# Install AWS CLI
apt-get install awscli

# Configure AWS credentials
aws configure

# Sync backups to S3
aws s3 sync /backups s3://your-backup-bucket/gb-app/ --delete

Deployment Process

Zero-Downtime Deployment

#!/bin/bash
# deploy.sh

set -e

echo "Starting deployment..."

# Pull latest code
git pull origin main

# Install dependencies
composer install --optimize-autoloader --no-dev
npm install
npm run build

# Run migrations
php artisan migrate --force

# Clear and cache
php artisan optimize:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Restart queue workers
php artisan queue:restart

# Reload PHP-FPM
sudo systemctl reload php8.2-fpm

echo "Deployment completed successfully!"

Health Check Endpoint

Create a health check endpoint:
routes/web.php
Route::get('/health', function () {
    return response()->json([
        'status' => 'ok',
        'timestamp' => now(),
        'database' => DB::connection()->getPdo() ? 'connected' : 'disconnected',
    ]);
});

Scaling Considerations

Horizontal Scaling

For multiple application servers:
  1. Shared Storage: Use NFS or S3 for storage/app
  2. Centralized Cache: Use Redis cluster
  3. Session Storage: Use Redis or database
  4. Load Balancer: Nginx, HAProxy, or cloud load balancer

Database Scaling

  1. Read Replicas: Configure read replicas for reports
  2. Connection Pooling: Use ProxySQL or PgBouncer
  3. Query Optimization: Add indexes, optimize slow queries

Troubleshooting Production Issues

Check Application Logs

tail -f storage/logs/laravel.log

Check Web Server Logs

tail -f /var/log/nginx/error.log
tail -f /var/log/nginx/access.log

Check PHP-FPM Logs

tail -f /var/log/php8.2-fpm.log

Monitor Resource Usage

# CPU and Memory
htop

# Disk usage
df -h
du -sh /var/www/html/*

# Disk I/O
iotop

# Network
netstat -tuln

Next Steps

Build docs developers (and LLMs) love