• 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 » Beyond Basic Containers: Mastering Kubernetes for High-Availability Laravel Deployments on AWS EKS

Beyond Basic Containers: Mastering Kubernetes for High-Availability Laravel Deployments on AWS EKS

Establishing a Robust EKS Cluster for Laravel

Deploying a high-availability Laravel application on AWS EKS requires more than just a basic container setup. We need to architect for resilience, scalability, and efficient resource utilization. This begins with a well-configured EKS cluster. For production environments, we’ll leverage managed Node Groups for automated scaling and patching, and ensure proper networking with VPC CNI.

First, let’s define the core EKS cluster configuration. We’ll use `eksctl`, the official CLI for Amazon EKS, for its simplicity and adherence to best practices.

EKS Cluster Provisioning with `eksctl`

Create a cluster configuration file (e.g., `eks-cluster.yaml`) to define your EKS cluster. This example sets up a cluster with a managed Node Group, enabling auto-scaling and specifying instance types suitable for web applications.

Key considerations here include:

  • region: Your AWS region.
  • version: Kubernetes version. Keep it updated.
  • nodeGroups: Defines the worker nodes. We’re using a managed Node Group for ease of management.
  • desiredCapacity, minSize, maxSize: Configure Auto Scaling Group parameters for your nodes.
  • instanceType: Choose instance types that balance cost and performance (e.g., t3.medium or m5.large).
  • privateNetworking: Crucial for security. Nodes should not be directly exposed to the public internet.
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: laravel-prod-cluster
  region: us-east-1
  version: "1.28" # Specify your desired Kubernetes version

managedNodeGroups:
  - name: laravel-workers
    instanceType: t3.medium
    desiredCapacity: 2
    minSize: 1
    maxSize: 5
    volumeSize: 80 # GiB
    ssh:
      allow: false # Disable SSH for managed nodes in production
    privateNetworking: true
    tags:
      nodegroup: laravel-workers
      environment: production
    iam:
      withAddonPolicies:
        autoScaler: true # Enable IAM policy for cluster-autoscaler
        cloudWatch: true # Enable IAM policy for CloudWatch logging
        albIngress: true # Enable IAM policy for AWS Load Balancer Controller

To provision the cluster, execute the following command:

eksctl create cluster -f eks-cluster.yaml

This process can take 15-20 minutes. Once complete, `eksctl` will update your ~/.kube/config file to point to the new cluster.

Containerizing the Laravel Application

A well-structured Dockerfile is essential for efficient and secure containerization. For Laravel, we typically need PHP, an web server (Nginx or Apache), and potentially other extensions. Using a multi-stage build is a best practice to keep the final image lean.

Optimized Dockerfile for Laravel

This Dockerfile uses an official PHP image, installs necessary extensions, copies application code, runs composer install, and then uses a lean Nginx image for serving static assets and proxying to PHP-FPM.

# Stage 1: Build the application
FROM php:8.2-fpm AS builder

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    unzip \
    libzip-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    libssl-dev \
    libonig-dev \
    libxml2-dev \
    libicu-dev \
    libxslt1-dev \
    zlib1g-dev \
    acl \
    vim \
    cron \
    supervisor \
    && rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg && docker-php-ext-install gd
RUN docker-php-ext-install pdo pdo_mysql zip intl bcmath opcache sockets xsl

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

# Set working directory
WORKDIR /var/www/html

# Copy application files
COPY . .

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

# Clear cache
RUN rm -rf var/cache/* var/log/* bootstrap/cache/*

# Copy compiled assets (if using Laravel Mix/Vite)
# RUN npm install && npm run build

# Stage 2: Production image with Nginx
FROM nginx:alpine

# Install PHP-FPM and required extensions for runtime
RUN apk add --no-cache \
    php82 \
    php82-fpm \
    php82-pdo \
    php82-pdo_mysql \
    php82-zip \
    php82-intl \
    php82-bcmath \
    php82-opcache \
    php82-sockets \
    php82-xsl \
    libzip \
    libpng \
    libjpeg-turbo \
    freetype2 \
    openssl \
    oniguruma \
    libxml2 \
    icu-data-full \
    libxslt \
    zlib \
    acl \
    vim \
    cronie \
    supervisor

# Copy application code from builder stage
COPY --from=builder /var/www/html /var/www/html

# Copy Nginx configuration
COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf

# Copy PHP-FPM configuration
COPY docker/php-fpm/www.conf /etc/php82/php-fpm.d/www.conf

# Copy Supervisor configuration
COPY docker/supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf

# Create necessary directories and set permissions
RUN mkdir -p /var/www/html/storage/framework/sessions \
    && mkdir -p /var/www/html/storage/framework/views \
    && mkdir -p /var/www/html/storage/framework/cache \
    && mkdir -p /var/www/html/bootstrap/cache \
    && chown -R www-data:www-data /var/www/html/storage \
    && chown -R www-data:www-data /var/www/html/bootstrap/cache

# Expose port 80
EXPOSE 80

# Start supervisor to manage Nginx and PHP-FPM
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

You’ll also need corresponding Nginx and PHP-FPM configuration files. For example, docker/nginx/default.conf:

server {
    listen 80;
    server_name localhost;
    root /var/www/html/public;

    index index.php index.html index.htm;

    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 php-fpm service name
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }

    # Serve static assets directly from storage/app/public if configured
    location /storage/ {
        alias /var/www/html/storage/app/public/;
        expires 30d;
        add_header Cache-Control "public";
    }
}

And docker/php-fpm/www.conf (simplified):

[global]
error_log = /var/log/php-fpm.log
daemonize = no

[www]
user = www-data
group = www-data
listen = 9000
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 5
pm.process_idle_timeout = 10s
request_terminate_timeout = 60s

Finally, docker/supervisor/supervisord.conf to manage Nginx and PHP-FPM processes:

[supervisord]
nodaemon=true
user=root

[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autorestart=true
priority=10

[program:php-fpm]
command=/usr/sbin/php-fpm82 --nodaemonize --fpm-config /etc/php82/php-fpm.conf
autorestart=true
priority=20
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

Build and push your Docker image to Amazon ECR (Elastic Container Registry).

# Configure AWS CLI and Docker for ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin [YOUR_AWS_ACCOUNT_ID].dkr.ecr.us-east-1.amazonaws.com

# Create ECR repository (if it doesn't exist)
aws ecr create-repository --repository-name laravel-app --region us-east-1 --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE

# Build and push the image
docker build -t laravel-app:latest .
docker tag laravel-app:latest [YOUR_AWS_ACCOUNT_ID].dkr.ecr.us-east-1.amazonaws.com/laravel-app:latest
docker push [YOUR_AWS_ACCOUNT_ID].dkr.ecr.us-east-1.amazonaws.com/laravel-app:latest

Kubernetes Manifests for High Availability

Now, let’s define the Kubernetes resources. We’ll need Deployments for our application, Services for internal and external access, an Ingress for HTTP routing, PersistentVolumes for stateful data, and potentially Jobs for migrations and scheduled tasks.

Deployment for Laravel Application

The Deployment manages stateless application pods. We’ll configure readiness and liveness probes, resource requests/limits, and horizontal pod autoscaling (HPA).

apiVersion: apps/v1
kind: Deployment
metadata:
  name: laravel-app-deployment
  labels:
    app: laravel-app
spec:
  replicas: 3 # Start with 3 replicas for HA
  selector:
    matchLabels:
      app: laravel-app
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1 # Allow one pod to be unavailable during updates
      maxSurge: 1       # Allow one extra pod to be created
  template:
    metadata:
      labels:
        app: laravel-app
    spec:
      containers:
      - name: laravel-app
        image: [YOUR_AWS_ACCOUNT_ID].dkr.ecr.us-east-1.amazonaws.com/laravel-app:latest # Your ECR image
        ports:
        - containerPort: 80
        env:
        - name: APP_ENV
          value: "production"
        - name: APP_LOG_LEVEL
          value: "info"
        - name: DB_HOST
          valueFrom:
            secretKeyRef:
              name: laravel-db-secrets
              key: DB_HOST
        - name: DB_PORT
          valueFrom:
            secretKeyRef:
              name: laravel-db-secrets
              key: DB_PORT
        - name: DB_DATABASE
          valueFrom:
            secretKeyRef:
              name: laravel-db-secrets
              key: DB_DATABASE
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: laravel-db-secrets
              key: DB_USERNAME
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: laravel-db-secrets
              key: DB_PASSWORD
        - name: CACHE_DRIVER
          value: "redis"
        - name: SESSION_DRIVER
          value: "redis"
        - name: QUEUE_CONNECTION
          value: "redis"
        readinessProbe:
          httpGet:
            path: /healthz # A simple health check endpoint in Laravel
            port: 80
          initialDelaySeconds: 15
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        livenessProbe:
          httpGet:
            path: /healthz
            port: 80
          initialDelaySeconds: 30
          periodSeconds: 20
          timeoutSeconds: 5
          failureThreshold: 5
        resources:
          requests:
            cpu: "200m"
            memory: "256Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
      # Optional: If using persistent storage for logs or uploads
      # volumes:
      # - name: storage-volume
      #   persistentVolumeClaim:
      #     claimName: laravel-storage-pvc
      # volumeMounts:
      # - name: storage-volume
      #   mountPath: /var/www/html/storage

Create a simple health check route in your Laravel application (e.g., in routes/web.php or routes/api.php):

Route::get('/healthz', function () {
    // Basic check: ensure database connection is possible
    try {
        DB::connection()->getPdo();
        return response('OK', 200);
    } catch (\Exception $e) {
        return response('Database connection failed', 500);
    }
});

Horizontal Pod Autoscaler (HPA)

Configure HPA to automatically scale the number of application pods based on CPU utilization.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: laravel-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: laravel-app-deployment
  minReplicas: 2 # Minimum number of pods
  maxReplicas: 10 # Maximum number of pods
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70 # Scale up when CPU utilization reaches 70%
  # Optional: Scale based on memory
  # - type: Resource
  #   resource:
  #     name: memory
  #     target:
  #       type: Utilization
  #       averageUtilization: 70

Service for Internal Communication

A ClusterIP Service provides a stable internal IP address for pods managed by the Deployment.

apiVersion: v1
kind: Service
metadata:
  name: laravel-app-service
spec:
  selector:
    app: laravel-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: ClusterIP

Ingress for External Access

We’ll use the AWS Load Balancer Controller to provision an Application Load Balancer (ALB) that routes external traffic to our Laravel application. Ensure the controller is installed in your EKS cluster.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: laravel-ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip # Use IP mode for better performance with EKS
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:[YOUR_AWS_ACCOUNT_ID]:certificate/[YOUR_CERTIFICATE_ID] # Replace with your ACM certificate ARN
    alb.ingress.kubernetes.io/ssl-redirect: "true" # Redirect HTTP to HTTPS
spec:
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: laravel-app-service
                port:
                  number: 80

Note: Replace [YOUR_AWS_ACCOUNT_ID] and [YOUR_CERTIFICATE_ID] with your actual AWS account ID and ACM certificate ARN. Ensure the AWS Load Balancer Controller is installed and configured correctly in your EKS cluster.

Managing State and Background Jobs

Laravel applications often rely on databases, caching, and queues. For high availability, these services should also be managed for resilience.

Database and Cache with AWS RDS and ElastiCache

Instead of running databases or Redis within Kubernetes pods (which is generally discouraged for stateful services in production), leverage managed AWS services:

  • Database: Use Amazon RDS (e.g., Aurora PostgreSQL or MySQL). Configure it for Multi-AZ deployment for high availability.
  • Cache/Queue: Use Amazon ElastiCache for Redis. Configure a Redis cluster for high availability.

Store the connection details (host, port, username, password, database name) in Kubernetes Secrets. Create a secret named laravel-db-secrets:

apiVersion: v1
kind: Secret
metadata:
  name: laravel-db-secrets
type: Opaque
data:
  DB_HOST: [BASE64_ENCODED_RDS_ENDPOINT]
  DB_PORT: [BASE64_ENCODED_RDS_PORT]
  DB_DATABASE: [BASE64_ENCODED_DB_NAME]
  DB_USERNAME: [BASE64_ENCODED_DB_USERNAME]
  DB_PASSWORD: [BASE64_ENCODED_DB_PASSWORD]
  REDIS_HOST: [BASE64_ENCODED_ELASTICACHE_ENDPOINT]
  REDIS_PORT: [BASE64_ENCODED_ELASTICACHE_PORT]

To encode values, use:

echo -n "your_value" | base64

Redis Queue Workers

For background job processing, deploy dedicated worker pods. These can be managed by a separate Deployment and Service.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: laravel-worker-deployment
  labels:
    app: laravel-worker
spec:
  replicas: 2 # Start with 2 replicas for HA
  selector:
    matchLabels:
      app: laravel-worker
  template:
    metadata:
      labels:
        app: laravel-worker
    spec:
      containers:
      - name: laravel-worker
        image: [YOUR_AWS_ACCOUNT_ID].dkr.ecr.us-east-1.amazonaws.com/laravel-app:latest # Same image, different command
        command: ["php", "artisan", "queue:work", "--tries=3", "--timeout=300"] # Command to run queue worker
        envFrom:
        - secretRef:
            name: laravel-db-secrets # Inherit DB and Redis credentials
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
          limits:
            cpu: "300m"
            memory: "256Mi"
      restartPolicy: Always

Ensure your config/queue.php and .env files are correctly configured to use Redis and the credentials from the secrets.

Database Migrations and Scheduled Tasks

Running database migrations and scheduled tasks (cron jobs) requires careful orchestration within Kubernetes.

Running Migrations with Kubernetes Jobs

Use Kubernetes Jobs to run one-off tasks like database migrations. This ensures the migration runs to completion before new application versions are deployed.

apiVersion: batch/v1
kind: Job
metadata:
  name: laravel-migration-job
spec:
  template:
    spec:
      containers:
      - name: laravel-migration
        image: [YOUR_AWS_ACCOUNT_ID].dkr.ecr.us-east-1.amazonaws.com/laravel-app:latest # Same image
        command: ["php", "artisan", "migrate", "--force"] # --force is crucial for production
        envFrom:
        - secretRef:
            name: laravel-db-secrets # Inherit DB credentials
      restartPolicy: Never # Or OnFailure, depending on desired behavior
  backoffLimit: 4 # Number of retries before marking the job as failed

You can trigger this job manually using kubectl apply -f migration-job.yaml or integrate it into your CI/CD pipeline. For automated deployments, consider using a tool like Argo CD or Flux CD, which can manage the application of migration jobs before deploying new application versions.

Scheduled Tasks with CronJobs

Laravel’s scheduler can be managed using Kubernetes CronJobs. This approach replaces traditional cron daemons on individual servers.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: laravel-scheduler
spec:
  schedule: "*/5 * * * *" # Run every 5 minutes
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: laravel-scheduler
            image: [YOUR_AWS_ACCOUNT_ID].dkr.ecr.us-east-1.amazonaws.com/laravel-app:latest # Same image
            command: ["php", "artisan", "schedule:run"]
            envFrom:
            - secretRef:
                name: laravel-db-secrets # Inherit DB credentials
          restartPolicy: OnFailure
  concurrencyPolicy: Forbid # Prevents concurrent runs if a previous job is still running
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1

This CronJob will execute php artisan schedule:run every 5 minutes, which in turn triggers any defined scheduled commands in your Laravel application.

Monitoring, Logging, and Security Best Practices

A production-ready deployment requires robust monitoring, centralized logging, and strong security practices.

Centralized Logging with CloudWatch Container Insights

Leverage AWS CloudWatch Container Insights to aggregate logs from your EKS pods. This involves deploying the CloudWatch agent as a DaemonSet.

Ensure your EKS cluster has the necessary IAM permissions for the CloudWatch agent. You can typically enable this during cluster creation with `eksctl` using the cloudWatch: true option in the Node Group configuration.

Configure your Laravel application to log to stdout and stderr. The CloudWatch agent will then collect these logs.

Monitoring with Prometheus and Grafana

For more detailed application and cluster metrics, consider deploying Prometheus and Grafana within your EKS cluster. You can use the Prometheus Operator for easier management.

Expose metrics from your Laravel application (e.g., using a package like prometheus-php/client) and configure Prometheus to scrape them. Grafana can then be used to visualize these metrics.

Security Considerations

  • Network Policies: Implement Kubernetes Network Policies to restrict traffic between pods, enforcing the principle of least privilege.
  • Secrets Management: Use Kubernetes Secrets for sensitive information. For enhanced security, integrate with AWS Secrets Manager or HashiCorp Vault.
  • IAM Roles for Service Accounts (IRSA): Grant specific AWS permissions to your Kubernetes pods using IRSA, avoiding the need to embed AWS credentials in your application.
  • Image Scanning: Regularly scan your Docker images for vulnerabilities using ECR’s built-in scanning or third-party tools.
  • Resource Limits: Always define CPU and memory requests and limits for your pods to prevent resource starvation and ensure stability.

By combining these advanced Kubernetes patterns with AWS managed services, you can build a highly available, scalable, and resilient Laravel deployment on EKS.

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

  • Leveraging PHP 8.3 JIT and Vector API for Extreme Performance Gains in Laravel Microservices
  • Beyond Basic Containers: Mastering Kubernetes for High-Availability Laravel Deployments on AWS EKS
  • Leveraging PHP 8.3 JIT and Vector API for Extreme Performance Gains in Laravel Applications
  • Beyond the Basics: Advanced Docker Multi-Stage Builds and CI/CD Integration for Scalable PHP Microservices
  • Optimizing Laravel Forge & AWS Deployments for Sub-Second WordPress Headless API Response Times

Categories

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

Recent Posts

  • Leveraging PHP 8.3 JIT and Vector API for Extreme Performance Gains in Laravel Microservices
  • Beyond Basic Containers: Mastering Kubernetes for High-Availability Laravel Deployments on AWS EKS
  • Leveraging PHP 8.3 JIT and Vector API for Extreme Performance Gains in Laravel Applications

Top Categories

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

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