Orchestrating Microservices with Kubernetes: A Deep Dive into PHP 8.2, Laravel Octane, and AWS EKS for Scalable WordPress Headless
Architectural Overview: Decoupling WordPress for Hyperscale
Traditional WordPress deployments, while robust for many use cases, often struggle under extreme load or when integrated into complex, distributed systems. The monolithic nature of WordPress, particularly its reliance on a request-response cycle that re-initializes the entire application on each hit, becomes a bottleneck. Our approach leverages a headless WordPress instance purely as a content management microservice, exposing its data via REST or GraphQL. A separate, high-performance Laravel Octane application, powered by PHP 8.2, acts as the API gateway and presentation layer, orchestrating data from WordPress and potentially other services. All of this is containerized and orchestrated on AWS EKS, providing a resilient, scalable, and observable foundation.
Headless WordPress as a Content Microservice on EKS
The WordPress instance in this architecture is stripped of its frontend responsibilities. It serves solely as a content repository, accessible via its native REST API or enhanced with GraphQL plugins like WPGraphQL. Containerizing WordPress requires careful consideration of persistent storage for uploads and the database connection. We’ll deploy WordPress as a StatefulSet or a standard Deployment with an attached PersistentVolumeClaim (PVC) for the wp-content/uploads directory, backed by Amazon EFS or EBS CSI. The database will be an external Amazon RDS instance for managed high availability.
Here’s a simplified Dockerfile for a production-ready WordPress container, leveraging PHP 8.2 FPM:
# Use the official WordPress image as a base
FROM wordpress:6.4.3-php8.2-fpm-alpine
# Install necessary PHP extensions for common WordPress plugins and performance
RUN apk add --no-cache \
libzip-dev \
libpng-dev \
jpeg-dev \
freetype-dev \
icu-dev \
git \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) gd pdo_mysql opcache zip intl exif
# Configure PHP-FPM for production
COPY docker/php-fpm.conf /usr/local/etc/php-fpm.d/www.conf
COPY docker/opcache.ini /usr/local/etc/php/conf.d/opcache.ini
# Install Composer for plugin management (if needed, e.g., for Bedrock-style WP)
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Copy custom wp-config.php or entrypoint scripts
COPY wp-config-production.php /var/www/html/wp-config.php
# Expose port 9000 for PHP-FPM
EXPOSE 9000
# Set entrypoint to the default WordPress entrypoint
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["php-fpm"]
The php-fpm.conf would include settings like pm = dynamic, pm.max_children, pm.start_servers, and pm.min_spare_servers tuned for your EKS node resources. opcache.ini should enable and optimize OPcache for performance.
Laravel Octane: The High-Performance API Gateway and Presentation Layer
Laravel Octane dramatically boosts application performance by keeping your application in memory, processing requests using long-lived processes via Swoole or RoadRunner. This eliminates the framework bootstrap overhead on every request, making it ideal for an API gateway that frequently interacts with the WordPress backend. PHP 8.2 further enhances this with JIT compilation improvements and better memory management.
Our Octane application will serve as the primary entry point for frontend requests. It will fetch data from the headless WordPress instance (e.g., http://wordpress-service/wp-json/wp/v2/posts), process it, and render the final output (e.g., a React/Vue SPA served by Octane, or server-side rendered content). This separation ensures that WordPress remains focused on content, while Octane handles the high-traffic presentation logic.
A production-ready Dockerfile for a Laravel Octane application using RoadRunner:
# Stage 1: Builder
FROM composer:2.6 as composer
WORKDIR /app
# Copy composer.json and composer.lock to leverage Docker cache
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
# Copy the rest of the application code
COPY . .
# Build assets (if applicable, e.g., for a Blade-rendered frontend)
# RUN npm install && npm run prod
# Stage 2: Production Runtime
FROM php:8.2-fpm-alpine
# Install system dependencies
RUN apk add --no-cache \
git \
libzip-dev \
libpng-dev \
jpeg-dev \
freetype-dev \
icu-dev \
oniguruma-dev \
libxml2-dev \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) gd pdo_mysql opcache zip intl bcmath pcntl exif soap sockets
# Install RoadRunner binary
ARG RR_VERSION=2.12.3
RUN wget https://github.com/roadrunner-server/roadrunner/releases/download/v${RR_VERSION}/rr-v${RR_VERSION}-linux-amd64.tar.gz -O /tmp/rr.tar.gz \
&& tar -xzf /tmp/rr.tar.gz -C /usr/local/bin \
&& rm /tmp/rr.tar.gz \
&& chmod +x /usr/local/bin/rr
WORKDIR /var/www/html
# Copy application code from builder stage
COPY --from=composer /app /var/www/html
# Configure PHP-FPM and OPcache
COPY docker/php-fpm.conf /usr/local/etc/php-fpm.d/www.conf
COPY docker/opcache.ini /usr/local/etc/php/conf.d/opcache.ini
# Set correct permissions
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache
# Expose RoadRunner port (default 8000)
EXPOSE 8000
# Start RoadRunner
CMD ["rr", "serve", "-c", ".rr.yaml"]
The .rr.yaml configuration file for RoadRunner would define the PHP worker pool and other settings:
version: "2.7"
server:
command: "php /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --port=8000"
env:
APP_ENV: production
APP_DEBUG: "false"
APP_URL: "${APP_URL}" # Injected via Kubernetes ConfigMap/Secret
WORDPRESS_API_URL: "http://wordpress-service/wp-json" # Internal Kubernetes service name
http:
address: 0.0.0.0:8000
middleware: ["compress", "static"]
static:
dir: "public"
forbid: [".env", "composer.json", "composer.lock", "package.json", "package-lock.json", "webpack.mix.js", "artisan"]
rpc:
listen: 0.0.0.0:6001
logs:
mode: production
level: info
channels:
default:
output: stdout
Kubernetes (AWS EKS) Deployment Strategy
Deploying these microservices on AWS EKS provides robust orchestration, auto-scaling, and high availability. We’ll use separate Deployments for WordPress and Laravel Octane, exposing them via Services and an Ingress controller.
1. WordPress Deployment and Service
For WordPress, we need a Deployment, a Service, and a PersistentVolumeClaim (PVC) for uploads. We’ll assume an AWS RDS instance for the database, configured via Kubernetes Secrets.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wordpress-uploads-pvc
labels:
app: wordpress
spec:
accessModes:
- ReadWriteMany # Use ReadWriteMany for EFS, ReadWriteOnce for EBS
storageClassName: efs-sc # Or 'gp2', 'gp3' for EBS CSI
resources:
requests:
storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
replicas: 2
selector:
matchLabels:
app: wordpress
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: your-ecr-repo/wordpress:latest # Replace with your ECR image
ports:
- containerPort: 9000
env:
- name: WORDPRESS_DB_HOST
valueFrom:
secretKeyRef:
name: wordpress-db-secret
key: db_host
- name: WORDPRESS_DB_USER
valueFrom:
secretKeyRef:
name: wordpress-db-secret
key: db_user
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: wordpress-db-secret
key: db_password
- name: WORDPRESS_DB_NAME
valueFrom:
secretKeyRef:
name: wordpress-db-secret
key: db_name
- name: WORDPRESS_TABLE_PREFIX
value: wp_
- name: WORDPRESS_DEBUG
value: "0"
volumeMounts:
- name: wordpress-uploads
mountPath: /var/www/html/wp-content/uploads
volumes:
- name: wordpress-uploads
persistentVolumeClaim:
claimName: wordpress-uploads-pvc
---
apiVersion: v1
kind: Service
metadata:
name: wordpress-service
labels:
app: wordpress
spec:
ports:
- port: 80
targetPort: 9000
protocol: TCP
selector:
app: wordpress
type: ClusterIP # Internal service, not exposed directly
The wordpress-db-secret would be created from AWS Secrets Manager using an external secrets operator or manually:
kubectl create secret generic wordpress-db-secret \ --from-literal=db_host=your-rds-endpoint.rds.amazonaws.com \ --from-literal=db_user=admin \ --from-literal=db_password=your_db_password \ --from-literal=db_name=wordpress
2. Laravel Octane Deployment, Service, and HPA
The Laravel Octane application will be deployed similarly, but without persistent storage for the application itself (it’s stateless). We’ll add a Horizontal Pod Autoscaler (HPA) for dynamic scaling based on CPU utilization.
apiVersion: apps/v1
kind: Deployment
metadata:
name: octane-app
labels:
app: octane-app
spec:
replicas: 2 # Start with a base number of replicas
selector:
matchLabels:
app: octane-app
template:
metadata:
labels:
app: octane-app
spec:
containers:
- name: octane-app
image: your-ecr-repo/octane-app:latest # Replace with your ECR image
ports:
- containerPort: 8000
env:
- name: APP_URL
value: "https://your-domain.com" # External URL
- name: WORDPRESS_API_URL
value: "http://wordpress-service/wp-json" # Internal Kubernetes service name
resources:
requests:
cpu: "200m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1024Mi"
---
apiVersion: v1
kind: Service
metadata:
name: octane-app-service
labels:
app: octane-app
spec:
ports:
- port: 80
targetPort: 8000
protocol: TCP
selector:
app: octane-app
type: ClusterIP # Internal service
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: octane-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: octane-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # Scale up if CPU utilization exceeds 70%
3. Ingress for External Access
To expose the Laravel Octane application to the internet, we’ll use the AWS Load Balancer Controller (formerly ALB Ingress Controller). This will provision an Application Load Balancer (ALB) in front of our Octane service.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: octane-app-ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}'
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:REGION:ACCOUNT_ID:certificate/YOUR_CERT_ID # Replace with your ACM ARN
alb.ingress.kubernetes.io/healthcheck-path: /health # Define a health check endpoint in your Octane app
alb.ingress.kubernetes.io/success-codes: "200"
labels:
app: octane-app
spec:
rules:
- host: your-domain.com # Replace with your domain
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ssl-redirect # This is a dummy service for the redirect action
port:
name: use-annotation
- path: /
pathType: Prefix
backend:
service:
name: octane-app-service
port:
number: 80
Ensure the AWS Load Balancer Controller is installed and configured in your EKS cluster with appropriate IAM permissions to create ALBs, Target Groups, and Security Groups.
Database and Storage Considerations
- WordPress Database: Always use a managed service like Amazon RDS (Aurora MySQL or standard MySQL) for production WordPress databases. This offloads operational overhead, provides automated backups, replication, and scaling.
- WordPress Uploads (
wp-content/uploads):- Amazon EFS CSI Driver: For shared storage across multiple WordPress pods, EFS is ideal. The
efs-scStorageClass will dynamically provision EFS volumes. - Amazon S3 Offload: For ultimate scalability and cost-efficiency, consider plugins that offload WordPress media to S3. This eliminates the need for persistent volumes for uploads within Kubernetes, making WordPress pods truly stateless and easier to scale.
- Amazon EFS CSI Driver: For shared storage across multiple WordPress pods, EFS is ideal. The
- Laravel Octane Storage: The Octane application should be entirely stateless. Any session data, cache, or temporary files should be stored in external services like Amazon ElastiCache (Redis) or S3, not on local pod storage.
Observability and Monitoring
In a microservices architecture on EKS, robust observability is paramount. Integrate the following:
- Metrics: Deploy Prometheus and Grafana within your EKS cluster. Use the Kubernetes API to discover pods and scrape metrics from both WordPress (e.g., via a custom exporter or Nginx access logs) and Laravel Octane (e.g., using Prometheus client libraries in Laravel, or RoadRunner metrics endpoint).
- Logging: Centralize logs from all pods to Amazon CloudWatch Logs using Fluent Bit. Configure log groups for WordPress and Octane, enabling easy searching and analysis.
- Tracing: Implement distributed tracing using OpenTelemetry or AWS X-Ray. Instrument your Laravel Octane application to trace requests as they flow from the Ingress, through Octane, to the WordPress REST API, and potentially other services.
Example Prometheus scrape configuration for Octane (assuming RoadRunner exposes metrics on /metrics):
- job_name: 'octane-app'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
action: keep
regex: octane-app
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
target_label: __address__
replacement: $1:$2
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__meta_kubernetes_namespace]
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_pod_name]
target_label: kubernetes_pod_name
This configuration assumes your Octane pods are annotated with prometheus.io/scrape: "true" and prometheus.io/port: "8000".
Conclusion
By decoupling WordPress into a headless content microservice and pairing it with a high-performance Laravel Octane API gateway on AWS EKS, we achieve a highly scalable, resilient, and maintainable architecture. This setup addresses the inherent limitations of monolithic WordPress for demanding applications, providing the flexibility to scale components independently and leverage modern cloud-native practices. The combination of PHP 8.2’s performance enhancements, Octane’s long-running process model, and Kubernetes’ orchestration capabilities creates a powerful foundation for next-generation headless WordPress deployments.