Building a High Availability (HA) Architecture for Headless CMS Strapi on Hetzner VPS with HAProxy
Introduction to High Availability for Headless CMS
In modern web development, the headless content management system (CMS) has become the backbone of omnichannel digital experiences. Among the leading open-source solutions, Strapi stands out for its flexibility, developer-friendly ecosystem, and robust API capabilities. However, as your digital application scales, a standard single-instance deployment becomes a critical single point of failure (SPOF). If the server hosting your CMS goes down, your frontend applications lose access to their data, disrupting user experience and potentially harming your business reputation.
To mitigate this risk, enterprise-grade applications require a High Availability (HA) architecture. An HA architecture ensures that your system remains operational and accessible even if individual infrastructure components fail. In this comprehensive guide, we will explore how to architect, configure, and deploy a highly available Strapi environment utilizing three Hetzner Cloud VPS nodes, combined with the industry-standard HAProxy load balancer.
The Architecture Blueprint: 3 VPS Nodes + HAProxy
Before diving into configuration files, it is crucial to understand the structural layout of our infrastructure. To achieve true high availability without over-complicating the network topology, we utilize a three-node setup alongside a dedicated load balancing mechanism. This structure balances cost-efficiency with fault tolerance, making Hetzner Cloud an ideal provider due to its high-performance hardware and competitive pricing.
Component Breakdown
- Load Balancing Layer (HAProxy): Acts as the traffic cop. It accepts incoming client API requests and distributes them intelligently across the backend Strapi nodes. For absolute high availability, this layer should ideally utilize a floating IP or redundant HAProxy pairs with Keepalived.
- Application Layer (Strapi Nodes): Three identical Hetzner Cloud VPS instances running Strapi. These instances operate in a stateless manner, meaning they do not store persistent application data locally.
- Database Layer (Clustered PostgreSQL/MySQL): Strapi relies heavily on its database. Running a single database on one of the nodes defeats the purpose of HA. Therefore, we use a clustered database setup (such as PostgreSQL with Patroni or Managed PostgreSQL with high availability enabled) accessible by all three Strapi instances.
- Shared Storage Layer (Asset Synchronization): Since Strapi allows users to upload media files, these assets must be instantly accessible by all nodes. We achieve this by using an external S3-compatible object storage provider (like Hetzner Object Storage or AWS S3) via the Strapi provider plugin.
Step 1: Setting Up the Infrastructure on Hetzner
To begin, provision three Cloud VPS instances within the Hetzner Cloud Console. For production workloads, it is recommended to choose at least the CX22 or CPX22 instances featuring dedicated AMD EPYC or Intel Xeon vCPUs. Ensure all instances are placed within the same Hetzner Private Network (vSwitch) to facilitate secure, low-latency communication between nodes without exposing internal traffic to the public internet.
Security Note: Always configure your Hetzner Firewalls to strictly restrict public access to ports like 1337 (Strapi default) and 5432 (Database default), allowing traffic only from your HAProxy load balancer and designated management IPs.
Step 2: Configuring the External Database and Storage
A fundamental rule of scaling Strapi horizontally is statelessness. Strapi instances must not hold unique local states. If a user uploads an image or edits a schema, that change must reflect across all instances seamlessly.
Database Connection
Configure all three Strapi instances to point to your centralized, highly available database cluster. In your Strapi environment file (.env), ensure the database host environment variable points to the internal cluster IP:
DATABASE_HOST=10.0.0.50
DATABASE_PORT=5432
DATABASE_NAME=strapi_prod
DATABASE_USERNAME=strapi_user
DATABASE_PASSWORD=your_secure_password
DATABASE_SSL=trueCentralized Asset Uploads
By default, Strapi saves uploaded media to the local /public/uploads directory. In a multi-node setup, an image uploaded to Node 1 would be missing on Node 2 and Node 3. To fix this, install the AWS S3 upload provider plugin for Strapi:
npm install @strapi/provider-upload-aws-s3 --saveThen, update your config/plugins.js file to offload all media uploads to a centralized object storage bucket, ensuring uniform asset delivery regardless of which node handles the request.
Step 3: Deploying Strapi Across the Node Cluster
With the database and storage externalized, you can now deploy your Strapi application codebase to all three Hetzner VPS instances. It is best practice to use a process manager like PM2 to ensure the Strapi process automatically restarts if it crashes.
When running multiple instances of Strapi against the same database, you must be careful with database migrations. If all three instances attempt to run migrations simultaneously during a deployment, it can lead to database locks or corruption. To avoid this, implement a sequential deployment pipeline where Node 1 builds and runs migrations first, followed by the rolling restart of Node 2 and Node 3.
Step 4: Configuring HAProxy for Load Balancing
Now that your three Strapi instances are running smoothly on the private network, it is time to configure HAProxy to distribute traffic among them. Install HAProxy on your dedicated load balancer node:
sudo apt update
sudo apt install haproxy -yOpen the HAProxy configuration file located at /etc/haproxy/haproxy.cfg and append the following configuration block to define the frontend entry point and backend server pool:
frontend strapi_frontend
bind *:80
bind *:443 ssl crt /etc/ssl/certs/your_domain.pem
mode http
default_backend strapi_backend
backend strapi_backend
mode http
balance roundrobin
option httpchk GET /_health
http-check expect status 200
cookie SERVERID insert indirect nocache
server strapi-node-1 10.0.0.11:1337 check inter 2000 fall 3 rise 2
server strapi-node-2 10.0.0.12:1337 check inter 2000 fall 3 rise 2
server strapi-node-3 10.0.0.13:1337 check inter 2000 fall 3 rise 2Key Configuration Highlights:
- balance roundrobin: This algorithm distributes requests sequentially across our three nodes.
- option httpchk: HAProxy continuously polls the
/_healthendpoint of each Strapi instance. If a node stops responding, HAProxy automatically routes traffic away from it within seconds. - cookie SERVERID: Enables session persistence (sticky sessions). While Strapi APIs are largely stateless, sticky sessions ensure that the admin panel dashboard experience remains smooth and consistent for content editors.
Step 5: Testing Failover and Redundancy
An HA architecture is only valid if it successfully handles failures. To validate your setup, perform a chaos engineering test. Log into Node 1 and manually stop the Strapi service:
pm2 stop allMonitor your HAProxy stats page or run a continuous curl loop against your API endpoint. You will observe that HAProxy detects the failure of Node 1 almost instantly and seamlessly shifts 100% of the traffic to Node 2 and Node 3. Your end-users and frontend web applications will experience absolutely zero downtime.
Conclusion
Building a High Availability architecture for Strapi on Hetzner Cloud using HAProxy is an excellent investment for business-critical applications. By separating your application logic into stateless nodes, utilizing centralized databases, leveraging object storage, and controlling traffic flow via HAProxy, you create an environment capable of handling high traffic surges and infrastructure faults gracefully. This configuration not only maximizes your application uptime but also provides a scalable foundation that can easily expand to four or more nodes as your business grows.
