VPS Tutorials
Enterprise VPS High-Availability Architecture Design (2026): A Practical Roadmap from Single Point to Usable
Many people want to jump straight to "dual-machine hot standby/multi-active" and end up spending a lot of money while still panicking during outages. The most critical part of enterprise-grade high availability isn't the architecture diagram—it's whether you can implement it in order. This article lays it out along a rollback-friendly roadmap: L0 first completes security/backup/monitoring, L1 makes the app stateless, L2 adds two nodes + LB, L3 removes the single point at the entry. It also includes minimal HAProxy/Keepalived configuration examples, database primary-replica and recovery drill checklists.

When you're building enterprise-grade high availability (HA), the most common pitfall isn't "not knowing the technology"—it's getting the order wrong.
Many teams jump straight to drawing dual-machine hot standby and multi-active setups, and the result is:
- The business still has single points (e.g., file uploads, local sessions, a single database)
- Monitoring and alerting aren't done well, so when an outage happens you have to rely on users to report it
- You have backups but no recovery drills, and only when something really goes wrong do you discover the backups are useless
This article doesn't deal in mysticism, nor does it sell a "perfect architecture." I'm writing to a more realistic goal:
Help you raise availability step by step in a rollback-friendly way.
I've seen plenty of "fake requirements":
- The business actually allows maintenance in the middle of the night, but people are scared by the words "enterprise-grade"
- The user base isn't large, and a single machine + backup + monitoring is enough
- The biggest risk isn't an outage, but accidental deletion of the database, expired certificates, or a full disk
You can quickly judge with two questions:
- How long can you accept a single failure lasting? (RTO)
- How much data can you accept losing? (RPO)
If you haven't written RTO/RPO into a document yet, don't rush into HA.
A quick reminder: before doing HA, calculate the "time cost" clearly. Many projects aren't brought down by servers—they're brought down by maintenance costs.
I suggest splitting HA into four levels and delivering by level.
The goal of this step is:
- A single machine can also run stably
- When something goes wrong, you can locate it
- Data can be recovered
The minimum three-piece set:
- Security: SSH/firewall/Fail2Ban
- Backup: automatic backup + ability to restore
- Monitoring: know when a service is down + disk is full + a certificate is about to expire
Corresponding "copy-the-homework links":
- VPS Security Hardening Guide (SSH + Firewall + Fail2Ban)
- VPS Automatic Backup Solution (Rclone)
- VPS Monitoring and Alerting (Prometheus + Grafana)
If you haven't even finished L0, don't rush to talk about L2/L3.
To do HA, the first thing to solve isn't "add two more machines," but:
- Whether the application can scale horizontally.
The most common statelessness pitfalls:
- Sessions stored in local memory (switch to another machine and users get logged out)
- File uploads written to local disk (switch to another machine and the files can't be found)
- Scheduled tasks running on a particular machine (after a switch, they run twice or are missed)
Typical solutions:
- Put sessions in Redis (or use stateless JWT, but control the risks)
- Put files in object storage (S3/R2) or shared storage
- Use a "unique scheduling" mechanism for scheduled tasks (leader election / a separate job node)
At this step, you should at least achieve:
- Two application nodes (App A / App B)
- One entry (LB) doing health checks and forwarding
Minimum implementable architecture:
- Client → LB → App A / App B
- App → DB (a single database is fine for now, but you need backups and recovery drills)
Don't make health checks "port is open = alive." It's best to have a real /healthz endpoint:
- Check that the process is alive
- Check whether dependencies (DB/Redis) are available (at least do a shallow check)
You can first use the most plain configuration to build the traffic-switching capability.
Below is a minimal runnable example (just change the ports to your own):
# /etc/haproxy/haproxy.cfg(示例)
frontend fe_http
bind :80
default_backend be_app
backend be_app
balance roundrobin
option httpchk GET /healthz
http-check expect status 200
server app_a 10.0.0.11:8080 check
server app_b 10.0.0.12:8080 check
The verification idea is simple:
- Stop the service on
app_a(or make/healthzreturn 500) - Access the LB and see whether requests automatically fall to
app_b
Many people finish L2 and think they have HA.
But if the LB itself is a single machine, then it's the new single point.
Common approaches are:
- Dual LB + VRRP (Keepalived) floating VIP
- Or use a cloud provider's managed LB (less hassle, but you have to accept the rules and costs)
The most common pitfall here is called split-brain:
- Both nodes think they are the master
- The same VIP gets "grabbed back and forth"
The key to solving split-brain isn't "adding more configuration," but:
- Having clear arbitration/priority
- Having health checks and isolation (fencing) policies
- Failure drills must cover scenarios like "network partition" (don't just drill shutdowns)
Below is a common way to write it (an example, only the core structure).
Master node (MASTER):
# /etc/keepalived/keepalived.conf(示例)
vrrp_script chk_haproxy {
script "pidof haproxy"
interval 2
fall 3
rise 2
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 110
advert_int 1
authentication {
auth_type PASS
auth_pass CHANGE_ME
}
virtual_ipaddress {
10.0.0.100/24
}
track_script {
chk_haproxy
}
}
On the backup node (BACKUP), change state to BACKUP, lower priority (e.g., 100), and keep everything else the same.
You need to verify two things:
- If
haproxyreally goes down, will the VIP float to the other machine - In network partition/split-brain scenarios, will the VIP "jump back and forth"
In self-built VPS scenarios, fencing often can't be 100% perfect, but at least you should:
- Bind "entry floating" to health checks
- Clarify who has the right to become MASTER on failure
- Write drills into the process (don't rely on memory)
The database is often the hardest part of HA and the easiest to crash.
I'll give a practical principle:
- First make it recoverable (backup + drills)
- Then do primary-replica/read-write separation
- Only finally consider multi-master/distributed (unless you really need it)
- Single primary (Primary)
- One read-only replica (Replica)
- Automatic backup + regular recovery drills
- Monitor basic metrics like "replication lag/disk/connections/slow queries"
You need to ask yourself two questions:
- How large is your write volume really?
- Can you accept "read-only/write-only for a short time" as a degradation strategy?
Many businesses can absolutely:
- Switch to read-only first on failure
- Restore the service first
- Then slowly fix consistency
That's more realistic than pursuing "never down."
Don't expect to discuss things on the spot when a failure happens.
I suggest you write the shortest usable Runbook (even 5 lines is fine):
- Who is the owner when a failure occurs (a name/a group)
- How to determine whether the Primary is really down (don't just look at ping)
- When to trigger a switchover (what conditions must be met)
- The rollback strategy after switchover (what to do if the new Primary has problems)
- How to backfill data/fix consistency after the incident
Once you write this Runbook, your "high availability" is already better than most teams'.
High availability isn't written in documents—it's practiced.
I suggest you do at least these 6 drills (in order):
- Kill the application process: can the LB switch traffic away
- Disconnect the application node from the network: can health checks detect it
- Database goes down: can the service degrade (clearly return errors/read-only)
- Disk fills up: can monitoring alert in advance
- Certificate expires: can it alert 7 days in advance
- "Misoperation": can a dropped table/deleted file be restored from backup
| Scenario | Trigger method | Expected behavior | Actual behavior | RTO | Retrospective/improvement |
|---|---|---|---|---|---|
| App A goes down | kill / stop service | Automatic switchover | |||
| LB goes down | stop keepalived | VIP floats | |||
| DB goes down | stop db | Degrade/read-only |
If you want to verify at the network level (packet loss/jitter/detours), you can run a full mtr process to make it clear (and it's easier to convince yourself/your colleagues when something goes wrong):
I'll end with a piece of advice that isn't very "architect-like":
- First get L0 to "recoverable, alertable, diagnosable"
- Then do statelessness
- Then two nodes + LB
- Only finally make the entry/database highly available
With every step you take, you should be able to answer one question:
After this step, how much less will I panic during an outage?
If you can answer that, you're on the right path.