Laravel Deployment: A Step-by-Step Guide for Shared Hosting and VPS

Published on December 04, 2025
Laravel Deployment VPS SharedHosting DevOps ServerManagement

Introduction to Laravel Deployment

Deploying a Laravel application can be intimidating, but with the right approach, it becomes a straightforward process. Whether you're using budget-friendly shared hosting or a powerful VPS, this guide will walk you through every step.

Key Differences:

  • Shared Hosting: Managed environment, limited control, easy setup
  • VPS (Virtual Private Server): Full control, more responsibility, better performance
  • Cloud Platforms (AWS, DigitalOcean): Scalable, flexible, more complex setup

Pre-Deployment Checklist

Production Configuration

# .env.production

APP_NAME="Your App Name"
APP_ENV=production
APP_KEY=base64:your-generated-key-here
APP_DEBUG=false
APP_URL=https://yourdomain.com
APP_TIMEZONE=UTC

LOG_CHANNEL=stack
LOG_LEVEL=error

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_DRIVER=redis
FILESYSTEM_DISK=public
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis

MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=your_smtp_username
MAIL_PASSWORD=your_smtp_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="noreply@yourdomain.com"
MAIL_FROM_NAME="${APP_NAME}"

Part 1: Shared Hosting Deployment

Choosing the Right Shared Hosting

Requirements for Laravel:

  • PHP 8.1 or higher
  • MySQL 5.7+ or MariaDB 10.3+
  • Apache with mod_rewrite or Nginx
  • SSH access (preferred)
  • Composer support
  • Cron job capability

Step-by-Step Shared Hosting Deployment

Step 1: Upload Your Application
# Local preparation
cd /path/to/your/laravel-app

# Install dependencies (excluding dev dependencies)
composer install --optimize-autoloader --no-dev

# Build assets
npm run build

# Create zip for upload (excluding unnecessary files)
zip -r deployment.zip . \
    -x "*.git*" \
    -x "*.env*" \
    -x "node_modules/*" \
    -x "storage/*" \
    -x "tests/*" \
    -x "*.md" \
    -x "*.txt" \
    -x "phpunit.xml" \
    -x "*.zip" \
    -x "*.tar.gz"
Step 2: Configure Shared Hosting

cPanel Setup:

  1. Create Database:
    • Go to MySQL Databases
    • Create new database: yourdb_name
    • Create user with strong password
    • Add user to database with ALL privileges
  2. Upload Files via File Manager:
    • Extract zip to public_html (or subdirectory)
    • Move public folder contents to public_html
    • Update index.php paths
  3. Set Permissions:
    # Via cPanel File Manager or SSH
    chmod -R 755 storage
    chmod -R 755 bootstrap/cache
    chmod 644 .env
Step 3: Configure .env File
# Create .env file via cPanel or upload
APP_NAME="Your App"
APP_ENV=production
APP_KEY=base64:...
APP_DEBUG=false
APP_URL=https://yourdomain.com

DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=cpanelusername_dbname
DB_USERNAME=cpanelusername_dbuser
DB_PASSWORD=your_secure_password

# For shared hosting, use file cache
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_CONNECTION=sync
Step 4: Configure Cron Jobs

In cPanel โ†’ Cron Jobs:

# Run every minute
* * * * * cd /home/username/public_html && php artisan schedule:run >> /dev/null 2>&1

# Daily backup (if using spatie/laravel-backup)
0 2 * * * cd /home/username/public_html && php artisan backup:run --quiet

Part 2: VPS Deployment

Choosing and Setting Up Your VPS

Popular VPS Providers:

  • DigitalOcean - $5/month, excellent documentation
  • Linode - $5/month, good performance
  • Vultr - $6/month, high frequency options
  • AWS Lightsail - $3.50/month, AWS ecosystem

Recommended Specifications:

  • Small Site: 1GB RAM, 1 vCPU, 25GB SSD ($5-10/month)
  • Medium Site: 2GB RAM, 2 vCPU, 50GB SSD ($10-20/month)
  • Large Site: 4GB+ RAM, 4+ vCPU, 80GB+ SSD ($20+/month)

Initial Server Setup

Step 1: Create and Connect to VPS
# Connect via SSH
ssh root@your_server_ip

# Update system
apt update && apt upgrade -y

# Create new user (optional but recommended)
adduser deploy
usermod -aG sudo deploy

# Copy SSH key for new user
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
Step 2: Basic Server Security
# Setup firewall
sudo apt install ufw -y
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh  # or your custom port: sudo ufw allow 2222/tcp
sudo ufw allow http
sudo ufw allow https
sudo ufw enable

# Install fail2ban for brute force protection
sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Step 3: Install Required Software
# Install PHP 8.2 with extensions
sudo apt install software-properties-common -y
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
sudo apt install php8.2-fpm php8.2-common php8.2-mysql php8.2-xml \
php8.2-xmlrpc php8.2-curl php8.2-gd php8.2-imagick php8.2-cli \
php8.2-dev php8.2-imap php8.2-mbstring php8.2-opcache \
php8.2-soap php8.2-zip php8.2-bcmath php8.2-intl -y

# Install Composer
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composer

# Install MySQL
sudo apt install mysql-server -y
sudo mysql_secure_installation

# Install Redis
sudo apt install redis-server -y
sudo systemctl enable redis-server
sudo systemctl start redis-server

# Install Nginx
sudo apt install nginx -y

# Install Node.js and npm
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install nodejs -y

Step-by-Step VPS Deployment

Step 1: Create Deployment Directory
sudo mkdir -p /var/www/yourdomain.com
sudo chown -R deploy:www-data /var/www/yourdomain.com
sudo chmod -R 755 /var/www/yourdomain.com
Step 2: Deploy Your Application
# As deploy user
su - deploy
cd /var/www/yourdomain.com

# Clone your repository
git clone https://github.com/yourusername/yourproject.git .

# Or upload via SCP
# From local machine:
scp -r /path/to/local/laravel deploy@your_server_ip:/var/www/yourdomain.com
Step 3: Install Dependencies
cd /var/www/yourdomain.com

# Install PHP dependencies
composer install --optimize-autoloader --no-dev

# Install npm dependencies and build
npm ci --only=production
npm run build

# Set permissions
sudo chown -R deploy:www-data /var/www/yourdomain.com
sudo chmod -R 775 storage bootstrap/cache
Step 4: Configure Environment
cd /var/www/yourdomain.com
cp .env.example .env

# Generate application key
php artisan key:generate

# Edit .env file
nano .env
# /var/www/yourdomain.com/.env
APP_NAME="Your App"
APP_ENV=production
APP_KEY=base64:...
APP_DEBUG=false
APP_URL=https://yourdomain.com

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_db
DB_USERNAME=laravel_user
DB_PASSWORD=your_secure_password

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
Step 5: Configure Nginx
server {
    listen 80;
    listen [::]:80;
    server_name yourdomain.com www.yourdomain.com;
    root /var/www/yourdomain.com/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
    add_header X-XSS-Protection "1; mode=block";

    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;
    }

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml;

    # Cache static files
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Deny access to sensitive files
    location ~ /\.ht {
        deny all;
    }

    location ~ /\.env {
        deny all;
    }

    location ~ /storage/.*\.php$ {
        deny all;
    }
}

Enable the site:

sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 6: Configure PHP-FPM
sudo nano /etc/php/8.2/fpm/php.ini
; Performance optimization
max_execution_time = 300
max_input_time = 300
memory_limit = 256M
post_max_size = 100M
upload_max_filesize = 100M

; Opcache for better performance
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.fast_shutdown=1

; Security
expose_php = Off
display_errors = Off
log_errors = On
sudo nano /etc/php/8.2/fpm/pool.d/www.conf
; Performance tuning
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500

; User and group
user = deploy
group = www-data

; Listen socket
listen = /var/run/php/php8.2-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

Restart PHP-FPM:

sudo systemctl restart php8.2-fpm
Step 7: Setup SSL with Let's Encrypt
# Install Certbot
sudo apt install certbot python3-certbot-nginx -y

# Get SSL certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

# Auto-renewal test
sudo certbot renew --dry-run
Step 8: Configure Supervisor for Queues
sudo apt install supervisor -y
sudo nano /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=deploy
numprocs=4
redirect_stderr=true
stdout_logfile=/var/www/yourdomain.com/storage/logs/worker.log
stopwaitsecs=3600
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start all
Step 9: Setup Automated Deployment Script
#!/bin/bash

set -e

echo "๐Ÿš€ Starting deployment..."

# Variables
PROJECT_PATH="/var/www/yourdomain.com"
BACKUP_PATH="/var/backups/yourdomain.com"
BRANCH="main"

# Backup current version
echo "๐Ÿ“ฆ Creating backup..."
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
sudo -u deploy cp -r "$PROJECT_PATH" "$BACKUP_PATH/$TIMESTAMP"

# Go to project directory
cd "$PROJECT_PATH"

# Pull latest changes
echo "โฌ‡๏ธ  Pulling latest changes..."
sudo -u deploy git fetch origin
sudo -u deploy git checkout "$BRANCH"
sudo -u deploy git reset --hard origin/"$BRANCH"
sudo -u deploy git pull origin "$BRANCH"

# Install PHP dependencies
echo "๐Ÿ“ฆ Installing PHP dependencies..."
sudo -u deploy composer install --no-dev --optimize-autoloader

# Install npm dependencies and build
echo "๐Ÿ“ฆ Installing npm dependencies..."
sudo -u deploy npm ci --only=production
sudo -u deploy npm run build

# Run database migrations
echo "๐Ÿ—„๏ธ  Running migrations..."
sudo -u deploy php artisan migrate --force

# Clear caches
echo "๐Ÿงน Clearing caches..."
sudo -u deploy php artisan config:cache
sudo -u deploy php artisan route:cache
sudo -u deploy php artisan view:cache
sudo -u deploy php artisan event:cache

# Set permissions
echo "๐Ÿ” Setting permissions..."
sudo chown -R deploy:www-data "$PROJECT_PATH"
sudo chmod -R 775 storage bootstrap/cache

# Restart services
echo "๐Ÿ”„ Restarting services..."
sudo supervisorctl restart all
sudo systemctl reload php8.2-fpm
sudo systemctl reload nginx

echo "โœ… Deployment completed successfully!"
echo "๐ŸŒ Your site is now live at: https://yourdomain.com"

Make it executable:

sudo chmod +x /var/www/yourdomain.com/deploy.sh

Part 3: Automation and CI/CD

GitHub Actions Deployment

# .github/workflows/deploy.yml

name: Deploy to Production

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  tests:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: '8.2'
        extensions: mbstring, xml, ctype, iconv, intl, pdo_mysql, bcmath, redis
    
    - name: Install Dependencies
      run: composer install --prefer-dist --no-progress --no-interaction
    
    - name: Run Tests
      run: vendor/bin/phpunit
  
  deploy:
    needs: tests
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
    
    - name: Setup SSH
      uses: webfactory/ssh-agent@v0.5.3
      with:
        ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
    
    - name: Deploy to Production
      run: |
        ssh -o StrictHostKeyChecking=no deploy@${{ secrets.SERVER_IP }} << 'EOF'
          cd /var/www/yourdomain.com
          git pull origin main
          composer install --no-dev --optimize-autoloader
          npm ci --only=production
          npm run build
          php artisan migrate --force
          php artisan config:cache
          php artisan route:cache
          php artisan view:cache
          sudo supervisorctl restart all
          sudo systemctl reload php8.2-fpm
        EOF

Part 4: Monitoring and Maintenance

Server Monitoring Setup

# Install monitoring tools
sudo apt install htop nload net-tools -y

# Install logrotate for Laravel logs
sudo nano /etc/logrotate.d/laravel
# /etc/logrotate.d/laravel
/var/www/yourdomain.com/storage/logs/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 640 deploy www-data
    sharedscripts
    postrotate
        sudo systemctl reload php8.2-fpm >/dev/null 2>&1 || true
    endscript
}

Part 5: Security Hardening

Server Security Script

#!/bin/bash
# security-hardening.sh

echo "๐Ÿ”’ Starting server security hardening..."

# Update system
echo "๐Ÿ”„ Updating system packages..."
apt update && apt upgrade -y

# Install security tools
echo "๐Ÿ“ฆ Installing security tools..."
apt install -y fail2ban ufw unattended-upgrades logwatch

# Setup firewall
echo "๐Ÿ”ฅ Configuring firewall..."
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow http
ufw allow https
echo "y" | ufw enable

# Secure SSH
echo "๐Ÿ” Securing SSH..."
sed -i 's/#Port 22/Port 2222/' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
systemctl restart sshd

# Setup fail2ban
echo "๐Ÿšจ Configuring fail2ban..."
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

cat >> /etc/fail2ban/jail.local << EOF
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600

[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 3600
EOF

systemctl restart fail2ban

# Set file permissions
echo "๐Ÿ“ Setting secure permissions..."
find /var/www -type d -exec chmod 755 {} \;
find /var/www -type f -exec chmod 644 {} \;
chmod -R 750 /var/www/yourdomain.com/storage
chmod -R 750 /var/www/yourdomain.com/bootstrap/cache
chown -R deploy:www-data /var/www/yourdomain.com

echo "โœ… Security hardening completed!"

Part 6: Troubleshooting Common Issues

Quick Fix Commands

#!/bin/bash
# quick-fixes.sh

echo "๐Ÿ”ง Applying quick fixes..."

# Fix permissions
echo "Setting permissions..."
sudo chown -R deploy:www-data /var/www/yourdomain.com
sudo chmod -R 775 storage bootstrap/cache
sudo chmod 644 .env

# Clear caches
echo "Clearing caches..."
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear

# Re-cache for production
echo "Re-caching for production..."
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Restart services
echo "Restarting services..."
sudo supervisorctl restart all
sudo systemctl reload php8.2-fpm
sudo systemctl reload nginx

echo "โœ… Quick fixes applied!"

Deployment Checklist Summary

Pre-Deployment Checklist

  1. Update .env for production
  2. Set APP_DEBUG=false
  3. Generate application key
  4. Configure database credentials
  5. Set up SSL certificate
  6. Update APP_URL to production domain

Deployment Steps

  1. Upload code to server
  2. Install dependencies (composer install --no-dev)
  3. Build assets (npm run build)
  4. Run migrations (php artisan migrate --force)
  5. Set proper permissions
  6. Configure web server (Nginx/Apache)
  7. Set up queue workers (Supervisor)
  8. Configure cron jobs
  9. Enable firewall (UFW)
  10. Set up monitoring

Performance Optimization Summary

Quick Performance Wins:

  1. Enable Opcache: opcache.enable=1
  2. Use Redis: For cache, session, and queue
  3. Enable Gzip: In Nginx/Apache configuration
  4. Set Cache Headers: For static assets
  5. Use CDN: For static assets (CSS, JS, images)
  6. Optimize Images: Before uploading
  7. Database Indexes: For frequently queried columns
  8. Queue Heavy Tasks: Email sending, file processing
  9. Cache Expensive Queries: Using Redis or Memcached
  10. Enable HTTP/2: For faster loading

Conclusion

Deploying Laravel applications doesn't have to be complicated. Whether you choose shared hosting for simplicity or a VPS for control and performance, following these step-by-step guides will ensure a smooth deployment process.

Key Takeaways:

  • Preparation is key: Complete the pre-deployment checklist
  • Security first: Always secure your server before deployment
  • Automate everything: Use deployment scripts and CI/CD
  • Monitor continuously: Set up monitoring and alerts
  • Regular maintenance: Update dependencies and security patches
  • Backup regularly: Automate backups and test restores
  • Document everything: Keep deployment and troubleshooting notes

Remember: Every deployment is a learning opportunity. Start with a simple setup, monitor performance, and gradually implement more advanced optimizations as your application grows.