How to Deploy a Laravel Application to Production with Docker and Nginx Reverse Proxy

How to Deploy a Laravel Application to Production with Docker and Nginx Reverse Proxy

Here's a complete guide for deploying a Laravel application to production using Docker and Nginx with SSL. This is based on my recent deployment experience that I'd like to share with everyone.

Prerequisites

  • Basic knowledge of Docker, Laravel, and Nginx
  • A server or hosting provider that supports Docker
  • Your Laravel application code
  • Domain name (optional but recommended)

Project Overview

This deployment setup includes:

  • Laravel application
  • PHP 8.3 with FPM
  • Nginx as a web server with reverse proxy
  • SSL configuration for secure HTTPS connections
  • Docker containerization for consistent deployment

Step 1: Create Your Dockerfile

The Dockerfile is the foundation of your deployment:

Create Dockerfile in to you project file

FROM ubuntu:latest

# Update and set timezone
RUN apt update && apt upgrade -y
ENV TZ=Asia/Phnom_Penh
RUN apt-get update && \
    apt-get install -y tzdata && \
    ln -fs /usr/share/zoneinfo/Asia/Phnom_Penh /etc/localtime && \
    dpkg-reconfigure -f noninteractive tzdata

# Install dependencies
RUN apt install -y libonig-dev libxml2-dev imagemagick nano
RUN apt-get update && apt-get install -y --no-install-recommends --quiet \
    git \
    libzip-dev \
    zip \
    curl \
    sudo \
    unzip \
    ffmpeg \
    # Additional dependencies for image processing
    libxpm-dev \
    libwebp-dev \
    libgd-dev \
    zlib1g-dev \
    libbz2-dev \
    libpng-dev \
    libjpeg-dev \
    libicu-dev \
    libmcrypt-dev \
    build-essential \
    libreadline-dev \
    libfreetype6-dev \
    libmagickwand-dev \
    libjpeg-turbo8-dev \
    g++

# Install Nginx
RUN apt install -y nginx

# Install PHP 8.3 and extensions
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends \
    php8.3-fpm \
    php8.3-mysql \
    php8.3-imagick \
    php8.3-bcmath \
    php8.3-ctype \
    php8.3-fileinfo \
    php8.3-mbstring \
    php8.3-pdo \
    php8.3-tokenizer \
    php8.3-curl \
    php8.3-zip \
    php8.3-gd \
    php8.3-xml \
    php8.3-bz2 \
    composer \
    && rm -rf /var/lib/apt/lists/*

# Configure PHP for production use
RUN sed -i "s/^max_execution_time\s*=.*/max_execution_time = 600/" /etc/php/8.3/fpm/php.ini \
    && sed -i "s/^max_input_time\s*=.*/max_input_time = 600/" /etc/php/8.3/fpm/php.ini \
    && sed -i "s/^memory_limit\s*=.*/memory_limit = 1024M/" /etc/php/8.3/fpm/php.ini \
    && sed -i 's/upload_max_filesize\s*=.*/upload_max_filesize = 512M/' /etc/php/8.3/fpm/php.ini \
    && sed -i 's/post_max_size\s*=.*/post_max_size = 512M/' /etc/php/8.3/fpm/php.ini

# Install Node.js and npm for frontend assets
RUN apt-get update && apt-get install -y nodejs npm && rm -rf /var/lib/apt/lists/*

# Generate self-signed SSL certificate
RUN apt-get update && apt-get install -y openssl && rm -rf /var/lib/apt/lists/* && \
    mkdir -p /ssl && \
    openssl genrsa -out /ssl/localhost.key 2048 && \
    openssl req -new -key /ssl/localhost.key -out /ssl/localhost.csr -subj "/C=US/ST=State/L=City/O=Organization/OU=Department/CN=localhost" && \
    openssl x509 -req -days 365 -in /ssl/localhost.csr -signkey /ssl/localhost.key -out /ssl/localhost.crt

# Copy application code and set up Laravel
WORKDIR /ssl
COPY . /project_file_name
WORKDIR /project_file_name
COPY .env.production .env # can use another env

# Install dependencies and build assets
RUN composer install --no-dev --optimize-autoloader
RUN php artisan key:generate
RUN npm install
RUN npm run build

# Configure Nginx
COPY localhost.conf /etc/nginx/sites-available/localhost.conf
RUN ln -s /etc/nginx/sites-available/localhost.conf /etc/nginx/sites-enabled && \
    rm /etc/nginx/sites-enabled/default

# Forward Nginx logs to Docker logs
RUN ln -sf /dev/stdout /var/log/nginx/access.log && \
    ln -sf /dev/stderr /var/log/nginx/error.log

# Set correct permissions
RUN chown -R www-data:www-data /project_file_name && \
    chmod -R 775 /project_file_name/storage /project_file_name/bootstrap/cache

# Optimize Laravel for production
RUN php artisan optimize
RUN php artisan config:cache
RUN php artisan route:cache
RUN php artisan view:cache
RUN php artisan event:cache
RUN php artisan cache:clear

EXPOSE 8000

# Start PHP-FPM and Nginx
CMD service php8.3-fpm start && nginx -g 'daemon off;'

replace project_file_name with your name file part

Step 2: Configure Nginx

Local Server Configuration

Create a localhost.conf file in your project:

server {
    listen 8000 ssl;
    server_name localhost;
    root /project_file_name/public;
    index index.php index.html;
    
    # SSL Certificate
    ssl_certificate /ssl/localhost.crt;
    ssl_certificate_key /ssl/localhost.key;

    # Increase max upload file size
    client_max_body_size 512M;

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

    # Error handling
    error_page 404 /index.php;

    # PHP-FPM configuration
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTPS on;
        # Timeout settings for PHP-FPM
        fastcgi_connect_timeout 600s;
        fastcgi_send_timeout 600s;
        fastcgi_read_timeout 600s;
    }

    # Security configurations
    location ~ /\.(?!well-known).* {
        deny all;
        access_log off;
        log_not_found off;
    }

    # Protect sensitive directories
    location ~* /(storage|.env) {
        deny all;
    }

    # Optimize static files handling
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|otf|eot|ttc|mp4|webm|ogg|ogv|htm|html|txt|xml|json)$ {
        expires max;
        log_not_found off;
        access_log off;
    }
}

replace project_file_name with your name file part

create a localhost_reverse.conf file in to your project file:

server {
    server_name you_domain_name or IP;  # Replace with your domain

    # Limit the size of client requests
    client_max_body_size 512M;

    # Gzip Compression
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml+rss text/javascript image/svg+xml;
    gzip_min_length 256;
    gzip_comp_level 5;
    gzip_vary on;
    gzip_disable "msie6";

    # Timeout settings
    proxy_connect_timeout 600s;
    proxy_read_timeout 600s;
    proxy_send_timeout 600s;
    client_body_timeout 600s;
    client_header_timeout 600s;

    # Proxy to the Docker container
    location / {
        proxy_pass https://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_cache_bypass $http_upgrade;
    }

    # Logs
    access_log /var/log/nginx/project_file_name_access.log;
    error_log /var/log/nginx/project_file_name_error.log;

    listen 80;
}

replace project_file_name with your name file part

Step 3: Prepare Your Laravel Application

  1. Prepare your production environment file:
    • Create a .env.prodution file based on your .env.example
    • Configure database credentials, app URL, etc. for production
  2. Make sure your Laravel application is ready for production:
    • Test all functionality locally
    • Ensure all migrations work correctly

Step 4: Building and Deploying

  1. Build your Docker image:
    • docker build -t my-laravel-app .
  2. Run your Docker container:
    • docker run -p 8000:8000 \
          --name=my-laravel-app \
          --restart=always \
          -d my-laravel-app
  3. reverse proxy configuration, place it on your host machine's Nginx configuration directory and restart Nginx:
    • sudo cp localhost_reverse.conf /etc/nginx/sites-available/you-domain-name
      sudo ln -s /etc/nginx/sites-available/you-domain-name /etc/nginx/sites-enabled
      sudo nginx -t
      sudo systemctl restart nginx

Step 5: Post-Deployment Tasks

  1. Set up a proper SSL certificate (recommended for production):
    • Use Let's Encrypt for free SSL certificates
    • Replace the self-signed certificates in the Docker container
  2. Configure backups for your database and uploaded files
  3. Set up monitoring for your application

Security Considerations

  • The configurations include several security practices:
    • Hidden files protection
    • Protection of sensitive directories like storage and .env files
    • SSL implementation
    • Properly configured file permissions
  • Consider adding:
    • Rate limiting for API endpoints
    • Web Application Firewall (WAF)
    • Regular security audits

Conclusion

This deployment setup provides a production-ready environment for your Laravel application. The Docker containerization ensures consistent deployment across different environments.

Happy deploying!

Ros socheath

Ros socheath

Web developer