• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Optimizing Laravel Forge Deployments with Docker Swarm for High Availability and Scalability

Optimizing Laravel Forge Deployments with Docker Swarm for High Availability and Scalability

Establishing the Foundation: Laravel Forge, Docker, and Swarm

While Laravel Forge excels at provisioning and managing single-server PHP applications, its inherent single-point-of-failure architecture becomes a bottleneck for high-availability and scalable deployments. Integrating Docker and Docker Swarm transforms Forge-managed infrastructure into a resilient, distributed system. This approach leverages Docker’s containerization for consistent environments and Swarm’s orchestration capabilities for managing multiple application instances across a cluster of servers.

The core idea is to shift from deploying a monolithic Laravel application directly onto a Forge-provisioned server to deploying a Dockerized Laravel application onto a Docker Swarm cluster. Forge will then be used to provision the *nodes* of the Swarm cluster, rather than the application itself.

Dockerizing Your Laravel Application

The first critical step is to containerize your Laravel application. This involves creating a `Dockerfile` that defines the environment your application needs to run. For a typical Laravel application, this includes PHP, a web server (like Nginx or Apache), and potentially other services like Redis or a database (though we’ll externalize the database for better scalability).

Here’s a robust `Dockerfile` example for a Laravel application using PHP-FPM and Nginx:

# Use an official PHP runtime as a parent image
FROM php:8.2-fpm

# Set the working directory in the container
WORKDIR /var/www/html

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    unzip \
    libzip-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    nginx \
    supervisor \
    cron \
    && rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install pdo pdo_mysql zip exif pcntl opcache

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Copy application files
COPY . /var/www/html

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

# Permissions
RUN chown -R www-data:www-data /var/www/html && chmod -R 755 /var/www/html

# Nginx configuration
COPY docker/nginx/default.conf /etc/nginx/sites-available/default
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

# Supervisor configuration for PHP-FPM and potentially other services
COPY docker/supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
RUN mkdir -p /var/log/supervisor

# Expose port 80 for Nginx
EXPOSE 80

# Start Supervisor to manage processes
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

You’ll also need to create the corresponding Nginx configuration file (e.g., `docker/nginx/default.conf`) and Supervisor configuration (e.g., `docker/supervisor/supervisord.conf`).

# docker/nginx/default.conf
server {
    listen 80;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/html/public;

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

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php-fpm:9000; # Assuming a service named 'php-fpm' in docker-compose
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }
}
# docker/supervisor/supervisord.conf
[supervisord]
nodaemon=true
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid

[program:php-fpm]
command=php-fpm -D
autostart=true
autorestart=true
user=www-data
stdout_logfile=/var/log/supervisor/php-fpm.log
stderr_logfile=/var/log/supervisor/php-fpm.err.log

[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/nginx.log
stderr_logfile=/var/log/supervisor/nginx.err.log

Next, create a `docker-compose.yml` file to define how your services interact. This will be crucial for local development and for defining your Swarm services later.

# docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: laravel_app
    restart: unless-stopped
    ports:
      - "8000:80" # For local testing
    volumes:
      - .:/var/www/html
    depends_on:
      - db
      - redis
    environment:
      DB_HOST: db
      DB_PORT: 3306
      DB_DATABASE: laravel
      DB_USERNAME: user
      DB_PASSWORD: password
      REDIS_HOST: redis
      REDIS_PORT: 6379

  db:
    image: mysql:8.0
    container_name: laravel_db
    restart: unless-stopped
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: laravel
      MYSQL_USER: user
      MYSQL_PASSWORD: password

  redis:
    image: redis:7.0
    container_name: laravel_redis
    restart: unless-stopped

volumes:
  db_data:

To test locally, navigate to your project root and run: docker-compose up -d. Your Laravel application should be accessible at http://localhost:8000.

Provisioning Docker Swarm Nodes with Laravel Forge

Now, we’ll use Forge to set up the infrastructure for our Swarm. Instead of creating a single server for your application, you’ll create multiple servers that will act as nodes in your Docker Swarm cluster. These servers should ideally be in different availability zones for high availability.

For each server you intend to be a Swarm node:

  • Create a new server in Forge. Choose your preferred Linux distribution (Ubuntu is common).
  • Ensure you have at least one server designated as a “manager” node. For simplicity, we’ll start with a single manager. In a production setup, you’d want multiple manager nodes for quorum and high availability.
  • Once the server is provisioned by Forge, SSH into it.

On the first server (which will be your Swarm manager), install Docker and Docker Compose:

# SSH into your Forge-provisioned server
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker $USER
newgrp docker # Apply group changes without logging out

Initialize the Docker Swarm on this manager node:

docker swarm init --advertise-addr <MANAGER_NODE_IP>

This command will output a `docker swarm join` command. Copy this command; you’ll need it to add worker nodes.

Now, on your other Forge-provisioned servers (which will be worker nodes), install Docker and Docker Compose using the same commands as above. Then, execute the `docker swarm join` command you copied from the manager node. For example:

# On each worker node:
docker swarm join --token <SWMTKN-12345...> <MANAGER_NODE_IP>:2377

Verify that all nodes have joined the swarm by running this command on the manager node:

docker node ls

Deploying the Laravel Application to Docker Swarm

With the Swarm cluster set up, we can now deploy our Dockerized Laravel application. The `docker-compose.yml` file we created earlier can be adapted for Swarm deployment using `docker stack deploy`.

First, we need to ensure our `docker-compose.yml` is Swarm-compatible. Key considerations for Swarm:

  • Remove `container_name` as Swarm assigns unique names.
  • Adjust `ports` for Swarm ingress (e.g., `80:80` if using a load balancer, or `8000:80` for direct access on nodes).
  • Define `networks` explicitly.
  • Use `deploy` directives for scaling, rolling updates, and resource constraints.
  • Externalize sensitive information (database credentials, API keys) using Swarm secrets or environment variables managed outside the compose file.

Let’s refine our `docker-compose.yml` for Swarm:

# docker-compose.yml (for Swarm)
version: '3.8'

services:
  app:
    image: your-dockerhub-username/laravel-app:latest # Push your image to a registry
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: ingress # For load balancing
    environment:
      # Use environment variables or secrets for sensitive data
      DB_HOST: db
      DB_PORT: 3306
      DB_DATABASE: laravel
      DB_USERNAME: user
      DB_PASSWORD: ${DB_PASSWORD} # Example using external variable
      REDIS_HOST: redis
      REDIS_PORT: 6379
    networks:
      - app-network
    deploy:
      replicas: 3 # Start with 3 replicas for HA
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: laravel
      MYSQL_USER: user
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - app-network
    deploy:
      replicas: 1 # Typically one primary DB instance, consider replication strategies separately

  redis:
    image: redis:7.0
    networks:
      - app-network
    deploy:
      replicas: 2 # Redis can be scaled for availability

networks:
  app-network:
    driver: overlay # Overlay network for Swarm

volumes:
  db_data:
    driver: local # Or use a distributed volume driver for production

Before deploying, build your Docker image and push it to a container registry (like Docker Hub, AWS ECR, or Google Container Registry):

# On your local machine or a build server
docker build -t your-dockerhub-username/laravel-app:latest .
docker push your-dockerhub-username/laravel-app:latest

Now, deploy the stack to your Swarm manager node. You’ll need to pass sensitive environment variables. A common approach is to use a `.env` file and load it, or set them directly.

# On your Swarm manager node:
# Create a .env file with your secrets
echo "DB_PASSWORD=your_db_password" >> .env
echo "MYSQL_ROOT_PASSWORD=your_root_password" >> .env

# Deploy the stack
docker stack deploy -c docker-compose.yml --with-registry-auth my-laravel-app

The `–with-registry-auth` flag is important if your registry requires authentication. You might need to log in to your registry first on the manager node: docker login.

Implementing High Availability and Scalability

With the application deployed as a Swarm stack, achieving high availability and scalability becomes a matter of configuration and infrastructure management.

Load Balancing

Docker Swarm’s ingress routing mesh automatically load balances traffic across all nodes for published ports. For more advanced load balancing, especially for SSL termination and more sophisticated routing rules, consider integrating an external load balancer:

  • Cloud Provider Load Balancers: AWS ELB/ALB, Google Cloud Load Balancing, Azure Load Balancer. Configure them to point to the Swarm nodes on the published port (e.g., port 80).
  • HAProxy/Nginx as a dedicated Swarm Load Balancer: Deploy HAProxy or Nginx as a Swarm service itself, configured to route traffic to the `app` service’s ingress network.

If using an external load balancer, you would typically publish the `app` service on a specific port (e.g., `8080:80`) and have the external LB forward traffic to that port on your Swarm nodes. For direct Swarm ingress, publishing on port 80 (`80:80`) is sufficient.

Scaling Services

You can scale your services up or down using the `docker service scale` command or by updating the `replicas` count in your `docker-compose.yml` and re-deploying the stack.

# Scale the app service to 5 replicas
docker service scale my-laravel-app_app=5

# Update docker-compose.yml with replicas: 5 and redeploy
docker stack deploy -c docker-compose.yml my-laravel-app

Database High Availability

The provided `docker-compose.yml` uses a single MySQL instance. For true high availability, you’ll need to implement database replication. This typically involves:

  • Setting up MySQL replication (master-slave or master-master).
  • Using a proxy like ProxySQL or MaxScale to manage read/write splitting and failover.
  • Deploying these database components as Swarm services.

This is a complex topic on its own, but the principle is to manage your database cluster as Swarm services, ensuring the application services can connect to the active database endpoint.

Rolling Updates

Docker Swarm’s `deploy.update_config` directive enables zero-downtime rolling updates. By setting `parallelism` and `delay`, Swarm updates service tasks one by one, ensuring that at least some instances of your application are always available. The `restart_policy` ensures that if a new deployment fails, Swarm can roll back.

Managing Secrets and Configuration

Hardcoding sensitive information is a security risk. Docker Swarm offers built-in secrets management. You can create secrets and mount them into your services.

# Create a secret for the database password
echo "your_db_password" | docker secret create DB_PASSWORD -

# Update docker-compose.yml to use the secret
services:
  app:
    # ... other configurations
    secrets:
      - DB_PASSWORD
  db:
    # ... other configurations
    secrets:
      - MYSQL_ROOT_PASSWORD
      - DB_PASSWORD

secrets:
  DB_PASSWORD:
    external: true
  MYSQL_ROOT_PASSWORD:
    external: true

When you deploy the stack, Swarm will securely distribute these secrets to the nodes where your services are running. The application can then read these secrets from files in /run/secrets/.

Monitoring and Logging

For production environments, robust monitoring and centralized logging are essential. Consider:

  • Logging: Deploy a logging driver (e.g., ELK stack – Elasticsearch, Logstash, Kibana, or Grafana Loki) as a Swarm service to aggregate logs from all containers.
  • Monitoring: Use tools like Prometheus and Grafana, deployed as Swarm services, to collect metrics from your containers and nodes.
  • Health Checks: Implement health checks in your `docker-compose.yml` (`healthcheck` directive) so Swarm can automatically detect and restart unhealthy containers.

Conclusion

By leveraging Laravel Forge for initial server provisioning and then transitioning to Docker Swarm for orchestration, you can build highly available, scalable, and resilient deployments for your Laravel applications. This architectural shift moves beyond single-server management to a distributed, containerized paradigm, enabling your applications to handle increased load and maintain uptime through automated failover and scaling.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Architecting for Resilience: Advanced Strategies for Zero-Downtime Deployments with Laravel, Docker, and AWS ECS
  • Leveraging PHP 8.3 JIT and Advanced Caching Strategies for Sub-Millisecond Laravel API Responses on AWS Lambda
  • Leveraging PHP 8.3 JIT and Vector APIs for Extreme Performance in High-Traffic Laravel Applications: A Deep Dive
  • Optimizing Laravel Forge Deployments with Docker Swarm for High Availability and Scalability
  • Leveraging PHP 8.2’s JIT and Laravel 11’s Octane for Sub-Millisecond API Response Times: A Deep Dive into Performance Tuning

Categories

  • apache (1)
  • AWS (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (664)
  • Desktop Applications (14)
  • DevOps (54)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (50)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (873)
  • Performance & Security Optimization (7)
  • PHP (181)
  • PHP Development (49)
  • Plugins & Themes (244)
  • Programming Languages (10)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (650)
  • SEO & Growth (492)
  • Server (118)
  • Softwares (1)
  • Ubuntu (9)
  • Uncategorized (350)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (95)
  • WordPress Plugin Development (728)
  • WordPress Theme Development (357)

Recent Posts

  • Architecting for Resilience: Advanced Strategies for Zero-Downtime Deployments with Laravel, Docker, and AWS ECS
  • Leveraging PHP 8.3 JIT and Advanced Caching Strategies for Sub-Millisecond Laravel API Responses on AWS Lambda
  • Leveraging PHP 8.3 JIT and Vector APIs for Extreme Performance in High-Traffic Laravel Applications: A Deep Dive

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (873)
  • WordPress Plugin Development (728)
  • Debugging & Troubleshooting (664)
  • Security & Compliance (650)
  • SEO & Growth (492)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala