Leveraging Laravel Octane with Docker Swarm for High-Performance, Auto-Scalable WordPress Headless APIs
Architectural Overview: Laravel Octane, Docker Swarm, and Headless WordPress
This architecture leverages Laravel Octane for blazing-fast PHP execution, Docker Swarm for robust container orchestration and auto-scaling, and a headless WordPress instance as the content source. The goal is to create a highly performant, scalable, and resilient API layer for modern web applications.
We’ll focus on the practical implementation details, including Docker Swarm service definitions, Octane configuration for production, and strategies for managing state and external dependencies.
Docker Swarm Setup and Service Definitions
A Docker Swarm cluster provides the foundation for deploying and managing our services. We’ll define services for the Laravel Octane API, a database (e.g., MySQL), and potentially a caching layer (e.g., Redis).
Docker Swarm Initialization
On your manager node, initialize the Swarm:
docker swarm init --advertise-addr
Join worker nodes to the Swarm using the command provided by docker swarm init.
Docker Compose for Swarm Deployment
We’ll use a docker-compose.yml file to define our services. This file will be deployed to the Swarm.
version: '3.8'
services:
wordpress_db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- wordpress_db_data:/var/lib/mysql
deploy:
replicas: 1 # Typically one primary DB instance
restart_policy:
condition: on-failure
networks:
- app-network
wordpress_app:
image: wordpress:latest # Or a custom image with WP CLI
environment:
WORDPRESS_DB_HOST: wordpress_db:3306
WORDPRESS_DB_USER: ${MYSQL_USER}
WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
volumes:
- wordpress_uploads:/var/www/html/wp-content/uploads
depends_on:
- wordpress_db
deploy:
replicas: 1 # WordPress itself might not need many replicas if only serving media
restart_policy:
condition: on-failure
networks:
- app-network
laravel_api:
build:
context: ./laravel_api # Path to your Laravel Octane project
dockerfile: Dockerfile.prod
environment:
APP_ENV: production
APP_DEBUG: false
APP_URL: http://api.yourdomain.com
DB_HOST: wordpress_db:3306 # Or a dedicated API DB if needed
DB_DATABASE: ${MYSQL_DATABASE}
DB_USERNAME: ${MYSQL_USER}
DB_PASSWORD: ${MYSQL_PASSWORD}
REDIS_HOST: redis_cache
REDIS_PASSWORD: ${REDIS_PASSWORD}
# Octane specific
OCTANE_SERVER: swoole
OCTANE_HOST: 0.0.0.0
OCTANE_PORT: 8000
OCTANE_WORKERS: 4 # Adjust based on CPU cores
OCTANE_MAX_REQUESTS: 1000 # For memory leak prevention
ports:
- "80:8000" # Map host port 80 to container port 8000
volumes:
- ./.env:/app/.env # Mount .env file for configuration
depends_on:
- wordpress_db
- redis_cache
deploy:
replicas: 3 # Auto-scaling for the API
restart_policy:
condition: on-failure
update_config:
parallelism: 1
delay: 10s
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
networks:
- app-network
redis_cache:
image: redis:alpine
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis_cache_data:/data
deploy:
replicas: 1
restart_policy:
condition: on-failure
networks:
- app-network
volumes:
wordpress_db_data:
wordpress_uploads:
redis_cache_data:
networks:
app-network:
driver: overlay
attachable: true
Create a .env file in the root of your Docker Compose project to manage environment variables:
MYSQL_ROOT_PASSWORD=supersecretrootpassword MYSQL_DATABASE=wordpress MYSQL_USER=wpuser MYSQL_PASSWORD=wppassword REDIS_PASSWORD=supersecretredispw
Deploying to Swarm
Navigate to the directory containing your docker-compose.yml and .env files on your Swarm manager node and deploy:
docker stack deploy -c docker-compose.yml my_headless_api
To scale the Laravel API service:
docker service scale my_headless_api_laravel_api=5
Laravel Octane Configuration for Production
Laravel Octane requires specific configuration for production environments, especially when running under a process manager like Swoole or RoadRunner within Docker.
Octane Service Provider and Configuration
Ensure the Octane service provider is registered in your config/app.php (or more appropriately, in a dedicated service provider that’s conditionally loaded for production). The .env variables in the Docker Compose file will handle the server choice and port.
The core Octane configuration is in config/octane.php. Key settings for a Docker Swarm environment include:
return [
/*
|--------------------------------------------------------------------------
| Octane Server
|--------------------------------------------------------------------------
|
| This option controls the Octane server that will be used to serve your
| application. The available options are: "swoole", "roadrunner", "frankenphp",
| and "hyperf". You may also use "none" to disable Octane.
|
*/
'server' => env('OCTANE_SERVER', 'swoole'),
/*
|--------------------------------------------------------------------------
| Octane Host & Port
|--------------------------------------------------------------------------
|
| This option controls the host and port that Octane will listen on.
|
*/
'host' => env('OCTANE_HOST', '0.0.0.0'),
'port' => env('OCTANE_PORT', 8000),
/*
|--------------------------------------------------------------------------
| Octane Workers
|--------------------------------------------------------------------------
|
| This option controls the number of Octane workers that will be started.
| Octane will automatically adjust this value based on the number of CPU
| cores available on your server.
|
*/
'workers' => env('OCTANE_WORKERS', 4),
/*
|--------------------------------------------------------------------------
| Maximum Number Of Requests Per Worker
|--------------------------------------------------------------------------
|
| This option controls the maximum number of requests that each worker
| will process before being automatically restarted. This is useful for
| preventing memory leaks.
|
*/
'max_requests' => env('OCTANE_MAX_REQUESTS', 1000),
// ... other configurations
];
Dockerfile for Production Laravel API
A production-ready Dockerfile for your Laravel Octane application:
# Use an official PHP runtime as a parent image
FROM php:8.2-fpm
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
libzip-dev \
unzip \
libpng-dev \
libjpeg-dev \
libfreetype6 \
libpq-dev \
# Swoole dependencies
build-essential \
autoconf \
libssl-dev \
zlib1g-dev \
pkg-config \
&& rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install gd pdo pdo_mysql zip bcmath sockets
# Install Swoole extension
RUN pecl install swoole \
&& docker-php-ext-enable swoole
# Set working directory
WORKDIR /app
# Copy composer.json and composer.lock
COPY composer.json composer.lock ./
# Install PHP dependencies
RUN composer install --no-dev --optimize-autoloader --no-interaction
# Copy the rest of the application code
COPY . .
# Generate optimized autoload files
RUN composer dump-autoload --optimize --no-dev
# Copy .env file (will be mounted by Docker Compose)
# COPY .env .
# Expose the port Octane will listen on
EXPOSE 8000
# Command to run Octane with Swoole
CMD ["php", "artisan", "octane:start", "--host=0.0.0.0", "--port=8000", "--workers=4", "--max-requests=1000"]
Important Notes for the Dockerfile:
- The
Dockerfile.prodshould be used when building the image for deployment. - The
CMDinstruction directly starts Octane. Docker Swarm’s restart policies will handle container restarts. - The
.envfile is mounted by Docker Compose, so it’s not copied directly in the Dockerfile to avoid baking secrets into the image. - Ensure your
composer.jsonincludeslaravel/octaneand any necessary extensions.
Headless WordPress Integration
The headless WordPress instance serves as the content repository. The Laravel API will fetch data from WordPress via its REST API or GraphQL endpoint (using a plugin like WPGraphQL).
WordPress Setup
Install WordPress in its own container (as defined in the docker-compose.yml). Ensure you have the necessary plugins for headless functionality:
- WP REST API (built-in): For basic content retrieval.
- WPGraphQL: For a more robust and flexible GraphQL API.
- ACF to REST API or ACF for WPGraphQL: If you use Advanced Custom Fields.
Fetching Data in Laravel Octane
Use Laravel’s HTTP client or a dedicated GraphQL client to interact with the WordPress API. For example, fetching posts using the REST API:
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;
// In a controller or service
public function getPosts()
{
$wordpressUrl = env('WORDPRESS_API_URL'); // e.g., http://wordpress.example.com/wp-json/wp/v2/posts
// Use caching to reduce load on WordPress and improve API performance
$posts = Cache::remember('wordpress_posts', 60 * 5, function () use ($wordpressUrl) {
try {
$response = Http::get("{$wordpressUrl}/posts", [
'per_page' => 10,
'_embed' => true, // To get featured image, author, etc.
]);
if ($response->successful()) {
return $response->json();
}
return []; // Return empty array on failure
} catch (\Exception $e) {
// Log the error
report($e);
return [];
}
});
return response()->json($posts);
}
For GraphQL, you would typically use a library like php-graphql-client or similar, configured to point to your WPGraphQL endpoint.
Performance and Scalability Considerations
Octane’s in-memory nature and Docker Swarm’s orchestration capabilities are key to high performance and scalability.
Caching Strategies
Implement aggressive caching within your Laravel API:
- HTTP Client Caching: Cache responses from the WordPress API (as shown above).
- Application-Level Caching: Cache computed data, expensive queries, or frequently accessed resources using Redis.
- Octane’s Application Cache: Octane provides its own cache that persists between requests within a worker’s lifecycle.
Database Connection Pooling
Octane keeps database connections open. Ensure your database server (MySQL) is configured to handle a higher number of concurrent connections than a traditional PHP-FPM setup. For very high loads, consider a dedicated connection pooler like PgBouncer (for PostgreSQL) or ProxySQL (for MySQL), though this adds complexity.
Statelessness and Session Management
Octane workers are long-lived. Avoid storing state in memory that should persist across worker restarts or be shared between requests unless explicitly managed. Use Redis for session storage and other shared state.
In config/session.php, set the driver to Redis:
'driver' => env('SESSION_DRIVER', 'redis'),
Load Balancing and Reverse Proxy
Docker Swarm’s ingress routing mesh handles basic load balancing across your API service replicas. For more advanced control, SSL termination, or custom routing, you can deploy a dedicated reverse proxy like Nginx or Traefik as a Swarm service and route traffic through it.
Example Nginx configuration for SSL termination and routing to the Octane service (assuming Nginx is also running in Swarm):
# Assuming Nginx is deployed as a Swarm service and is the entry point
# This configuration would be part of the Nginx service definition
server {
listen 80;
server_name api.yourdomain.com;
# Redirect HTTP to HTTPS
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name api.yourdomain.com;
ssl_certificate /etc/nginx/ssl/yourdomain.com.crt;
ssl_certificate_key /etc/nginx/ssl/yourdomain.com.key;
# Add other SSL configurations (protocols, ciphers, etc.)
location / {
# Use Docker Swarm service discovery to find the laravel_api service
# The port here is the *internal* port the Octane container listens on (8000)
proxy_pass http://my_headless_api_laravel_api:8000;
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_http_version 1.1;
proxy_set_header Connection ""; # Important for keep-alive connections
}
}
Monitoring and Logging
Implement robust monitoring and logging. Docker Swarm provides basic logging capabilities. For advanced needs, consider a centralized logging solution like ELK stack (Elasticsearch, Logstash, Kibana) or Grafana Loki, integrated with your Swarm.
Monitor key metrics:
- API response times
- Error rates
- CPU and memory usage of Octane containers
- Database connection counts
- WordPress API response times
Conclusion
By combining Laravel Octane’s performance enhancements with Docker Swarm’s orchestration and auto-scaling capabilities, you can build a formidable headless API backend. This architecture provides a solid foundation for demanding applications that require high throughput, low latency, and resilience, all while keeping your content managed within a familiar WordPress environment.