Building a High Availability (HA) Architecture for Headless CMS Strapi on Hetzner VPS with HAProxy
Introduction to Enterprise-Grade Headless CMS Architectures
In the modern digital landscape, content delivery must be seamless, lightning-fast, and above all, uninterrupted. As enterprises increasingly decouple their front-end presentation layers from backend content management systems, the reliability of the Headless CMS becomes paramount. Strapi has emerged as an industry favorite due to its developer-first approach, customizable API, and robust plugin ecosystem.
However, running a default Strapi installation on a single virtual private server (VPS) introduces a critical vulnerability: a single point of failure (SPOF). If that specific server crashes, undergoes maintenance, or experiences network degradation, your entire digital ecosystem goes dark. To mitigate this risk, enterprise applications require a High Availability (HA) architecture. This technical blueprint demonstrates how to construct a resilient, load-balanced Strapi cluster utilizing three cost-effective Hetzner VPS instances orchestrated by HAProxy.
The Core Architectural Components
Before diving into the configuration, it is essential to understand the structural blueprint of our multi-node environment. To ensure true high availability, we distribute the workload and separate concerns across specialized infrastructure layers:
- Load Balancing Layer (HAProxy): Acts as the single entry point for all incoming API traffic, intelligently routing requests to healthy Strapi application nodes.
- Application Layer (3x Hetzner VPS): Three identical, stateless virtual servers running Node.js and Strapi. By utilizing three nodes instead of two, we ensure quorum and maintain adequate capacity even if one server completely fails.
- Database Layer: A centralized database decoupled from the application servers. For production environments, a managed database service (like Hetzner's Managed PostgreSQL) or a replicated database cluster is mandatory.
- Shared Asset Storage: Since local file systems are isolated per VPS, media uploads must be externalized using an S3-compatible object storage provider or a distributed file system like GlusterFS.
Step 1: Preparing the Infrastructure on Hetzner Cloud
Hetzner Cloud offers exceptional performance-to-price ratios, making it ideal for scalable architectures. To begin, provision three Cloud Routed (or Shared vCPU) instances running Ubuntu 22.04 LTS or 24.04 LTS. Label these instances strapi-node-01, strapi-node-02, and strapi-node-03.
For optimal security and reduced latency, create a Hetzner Private Network (vSwitch) and attach all three instances to it. This allows the nodes to communicate internally using private IP addresses (e.g., 10.0.0.11, 10.0.0.12, and 10.0.0.13), completely isolated from the public internet.
Security Best Practice: Configure Hetzner Cloud Firewalls to block all public traffic to the Strapi application nodes, allowing traffic only on port 1337 originating from your load balancer's IP address.
Step 2: Configuring the Centralized Database and Media Storage
A common pitfall when scaling Strapi horizontally is forgetting that the application must remain entirely stateless. If Node 1 modifies a local file or holds local database state, Node 2 and Node 3 will immediately fall out of sync.
1. Relational Database Migration
Ensure your Strapi config/database.js is pointed toward a centralized PostgreSQL or MySQL cluster. Do not use SQLite, as it is file-based and incompatible with multi-instance clustering. Configure your connection pool size carefully to prevent the three concurrent Strapi instances from exhausting the database's max connection limits.
2. Externalizing the Media Library
By default, Strapi saves uploaded media assets to the /public/uploads directory. In an HA setup, an asset uploaded via Node 1 would be missing on Node 2. To resolve this, install the official Strapi S3 provider plugin:
npm install @strapi/provider-upload-aws-s3 --saveUpdate your config/plugins.js to stream all media uploads directly to an external object storage bucket (such as AWS S3, Cloudflare R2, or MinIO). This ensures assets are globally accessible regardless of which backend node processes the request.
Step 3: Deploying and Synchronizing Strapi across Nodes
To maintain identical application behavior, all three Hetzner nodes must run the exact same version of your custom Strapi code. The most efficient methodology is leveraging Docker or process managers like PM2 combined with a CI/CD pipeline (e.g., GitHub Actions).
When running multiple instances, database migrations can cause race conditions if all nodes boot simultaneously. To prevent this, apply the following deployment workflow:
NODE_ENV=production npm run build.strapi-node-01 first. Let it execute database schema migrations and validation.strapi-node-02 and strapi-node-03.Step 4: Implementing HAProxy as the Reverse Proxy and Load Balancer
With three independent Strapi applications running on port 1337 within your private network, you need a mechanism to present them as a single cohesive API to your front-end applications. HAProxy (High Availability Proxy) is an industry-standard, high-performance TCP/HTTP load balancer perfectly suited for this role.
You can deploy HAProxy on a dedicated fourth Hetzner VPS or utilize a highly available pair with Keepalived. Below is an enterprise-grade configuration snippet for/etc/haproxy/haproxy.cfg:frontend strapi_incoming
bind *:80
bind *:443 ssl crt /etc/ssl/certs/yourdomain.pem
mode http
option forwardfor
http-request set-header X-Forwarded-Proto https if { ssl_fc }
default_backend strapi_cluster
backend strapi_cluster
mode http
balance roundrobin
option httpchk GET /_health
http-check expect status 200
cookie SERVERID insert indirect nocache
server strapi-01 10.0.0.11:1337 check inter 2s rise 2 fall 3
server strapi-02 10.0.0.12:1337 check inter 2s rise 2 fall 3
server strapi-03 10.0.0.13:1337 check inter 2s rise 2 fall 3Deep Dive into the HAProxy Configuration:
- balance roundrobin: This algorithm distributes traffic evenly across the three VPS nodes sequentially.
- option httpchk GET /_health: HAProxy continuously polls Strapi's built-in health-check endpoint. If a node becomes unresponsive due to a crash or out-of-memory error, HAProxy transparently drops it from the rotation within 6 seconds (3 failed checks * 2s interval).
- cookie SERVERID: Enables session persistence (sticky sessions) if your admin dashboard requires users to stay attached to a specific backend server during long content-authoring sessions.
Step 5: Rigorous Failover Testing
An architecture is only truly "Highly Available" once it has been proven under duress. After configuring HAProxy and verifying that traffic routes correctly to all nodes, execute a controlled chaos engineering test:
Connect to strapi-node-01 via SSH and forcefully terminate the Strapi process or stop the Docker container. Open your web browser's developer tools and continuously refresh the Strapi Admin UI or execute API queries. You will observe that HAProxy immediately flags Node 1 as down and redirects 100% of the traffic to Node 2 and Node 3. The end-user experiences zero downtime, zero 502 Bad Gateway errors, and complete operational continuity.
Conclusion: Scalability and Peace of Mind
Building a high availability architecture for Headless CMS Strapi on Hetzner VPS using HAProxy requires meticulous planning around statelessness, centralized databases, and object storage. However, the dividends it pays are immense. By decoupling your layers and eliminating single points of failure, you guarantee a highly performant, resilient infrastructure capable of handling massive traffic spikes and infrastructure anomalies effortlessly.
As your content needs grow, this architecture allows you to scale horizontally even further—simply spin up a fourth or fifth Hetzner VPS, copy the deployment script, and append the private IP address to your HAProxy backend configuration.
