Load Balancing for Your Own Site: Multi-Instance Deployment, Health Checks, Session Persistence, and Failover

The trigger for putting load balancing on your own site is daily traffic consistently above 50,000, a single 4-core 8GB machine sitting above 60% CPU for long stretches, or you've already been burned once by "the server died and the whole site went down." The bar for doing it right: at least 2 app...

独立站负载均衡:多实例、健康检查与故障转移Load balancing: multi-instance, health check, failover 反直觉:会话保持不是必须的,无状态化才能真正横向扩展Counter-intuitive: sticky sessions hurt scaling; go stateless 负载均衡器 LBLoad Balancer L4 TCP / L7 HTTPL4 TCP / L7 HTTP 应用实例 AApp Instance A 健康 200 · 权重 50%Healthy, weight 50% 应用实例 BApp Instance B 健康 200 · 权重 50%Healthy, weight 50% 应用实例 CApp Instance C 3次失败 · 自动摘除Unhealthy, drained 健康检查:每10秒请求一次 /health,连续3次失败摘除,连续2次成功恢复Health: GET /health every 10s; 3 fails out, 2 oks in 会话保持:默认关闭,登录态放Redis或JWT;只有老系统实在改不动才开cookie粘性Sticky off; sessions in Redis/JWT 故障转移:单台宕机LB在10秒内摘除,流量自动打到健康实例,用户无感知Failover: LB drains dead node in 10s, user sees nothing 至少跨2台不同物理机/可用区;同机房单机断电=白配Spread across AZs, not one rack
Load balancing: healthy instances take traffic, failed instances get pulled automatically

Load Balancing for Your Own Site: Multi-Instance Deployment, Health Checks, Session Persistence, and Failover

Bottom line first: The trigger for putting load balancing on your own site is daily traffic consistently above 50,000, a single 4-core 8GB machine sitting above 60% CPU for long stretches, or you’ve already been burned once by “the server died and the whole site went down.” The bar for doing it right: at least 2 app instances deployed across availability zones, a health check every 10 seconds, automatic removal after 3 consecutive failures, and failover completed within 10 seconds. The counterintuitive part: sticky sessions are not something you have to turn on. Put login state in Redis or a JWT and make the app truly stateless, and that’s the real prerequisite for scaling to multiple instances. With sticky sessions on, if one machine dies, the users on it still get dropped.

1. When you need load balancing, and when it’s overkill

Load balancing isn’t something you should put in from day one. Under 5,000 daily visits, a single 2-core 4GB VPS running Nginx+PHP+MySQL is plenty, and spending a few hundred bucks on an LB just adds complexity. There are three real signals that you need multiple instances: a single machine’s CPU stays above 60%, you have to take the site down to deploy, or you’ve already lost inquiries because one machine failed. Meet two of the three, and then add an LB.

The typical upgrade path for your own site is: a single VPS running compose (under 10,000 daily visits) → move MySQL to its own machine (30,000 daily visits) → run 2 app instances plus a cloud LB (50,000 daily visits) → 3 to 5 app instances plus autoscaling (over 200,000 daily visits). Don’t skip steps. Put a CDN in front of the LB to absorb static traffic first; that can cut origin load in half. See CDN global acceleration.

Stage Deployment setup App instances Daily traffic reference Added monthly cost
Startup phase Single VPS + compose 1 <10,000 0
Growth Stage 1 app instance + 1 separate DB machine 1 10,000 to 50,000 +150 yuan
Steady stage 2 app instances + cloud LB 2 50,000 to 200,000 + about 100 yuan for the LB
Peak periods 3 to 5 app instances + autoscaling 3 to 5 >200,000 + billed by the hour

2. L4 or L7: which layer should your own site pick?

Load balancing splits into Layer 4 (L4, forwarding by TCP/IP) and Layer 7 (L7, forwarding by HTTP path). L4 performs better and is simpler to configure, but it can only forward by port. L7 can route by domain, path, and header, and it can also handle canary releases and HTTPS offloading. Pick L7 for your own site, because you need to split traffic by path: static files go to the CDN, the API goes to the app, and the admin panel goes to a separate set of instances.

Choosing a load balancer by cloud provider: AWS uses ALB (Application Load Balancer), Alibaba Cloud uses SLB or ALB, and Tencent Cloud uses CLB. They overlap in features, and pricing starts at 100 to 200 yuan per month. One rule of thumb: if all you need is round-robin across two servers, use the cheapest Layer 4 LB. If you need path-based routing, canary releases, or domain-based traffic splitting, go with a Layer 7 LB.

Comparison L4 TCP load balancing L7 HTTP load balancing
Routing basis IP + port Domain / path / header / cookie
Performance Very high, microsecond latency Slightly lower, millisecond latency
HTTPS offloading Not supported Supported, certificate stored on the LB
Canary release Not supported by traffic ratio Supported by weight-based traffic splitting
Best for Simple round-robin across two servers Multi-path routing, canary releases
Monthly cost (reference) About 80 to 120 yuan About 150 to 250 yuan

3. Health checks: how to configure them without killing healthy nodes

Health checks are the core of any LB. Configure them too loosely and dead servers keep receiving traffic; configure them too aggressively and an app that’s just slow to start gets pulled out, triggering a cascade failure. The standard setup for a standalone site: check path /health (returns only 200, no database query, so a slow DB doesn’t bleed into the LB), interval of 10 seconds, remove after 3 consecutive failures, restore after 2 consecutive successes.

The /health endpoint must stay lightweight. A common mistake is having it run a MySQL connection check, so when the database hiccups, the LB pulls every app instance and the whole site returns 503. The right approach: /health returns only the process liveness status, while database health goes through a separate /health/db endpoint that’s monitored but doesn’t affect removal decisions. For monitoring and alert thresholds, see Site monitoring and alerts.

Spec Recommended value Description Common mistake
Check path GET /health No DB query, returns only 200 DB query pulls all nodes
Check interval 10 seconds Balances sensitivity and false positives 1 second is too aggressive
Timeout 5 seconds Exceeding it counts as one failure 1 second is too short
Unhealthy threshold 3 consecutive failures Remove node Remove after 1 failure
Healthy Threshold 2 consecutive successes Restore to pool Restore after 1 success
Response code 200 counts as healthy Any other code counts as a failure Treating 503 as healthy

4. Session persistence: avoid it if you can

When people first run multiple instances, their first reaction is often “if a user logs in on server A, won’t the next request hitting server B log them out?” So they turn on session persistence. It’s a shortcut now that you pay for later. The problem with session persistence: once server A goes down, every user stuck to it gets logged out, which cancels out half the point of failover.

The right approach is to move sessions out of app memory. Use Redis for sessions in PHP, stateless JWT tokens in Node, and keep all login state in one shared store. That way any app instance can handle any user’s request, and losing any single server doesn’t affect logged-in users. Only two cases justify session persistence: a legacy system you can’t change, or a third-party plugin that hard-depends on local sessions.

Session approach Scalability Failure impact Migration cost Recommendation
Local file sessions Poor, sticky sessions required One server down = users logged out Low Not Recommended
Centralized Redis sessions Good Seamless failover Medium, just change the config Recommended
Stateless JWT tokens Best Seamless failover High — requires auth changes Recommended for new projects
LB cookie stickiness Bad If the pinned server dies, users drop off Low — just flip a switch Temporary stopgap only

5. Failover: how to prove your LB actually switches

Configuring the LB doesn’t mean failover works. Many teams never kill a server on purpose after launch, and only find out the LB has no health check or the security group isn’t open when something really dies. Run a failover drill before launch: in production (or staging), manually kill app A and watch whether the LB moves traffic to B within 30 seconds and whether users notice anything.

Another common trap: both app instances sit on the same physical host or the same power rack, so a rack outage takes down both at once. Deploy across at least two availability zones (AZs), or at minimum two different hosts. For the full methodology on backup and recovery drills, see Backup and recovery drills for your site.

Example: a B2B site ran WordPress on a single VPS. One night the host went down for maintenance and lost power, the site was offline for 4 hours, and 3 inquiries from European customers were missed. Afterward they added a second app instance behind a cloud LB, moved sessions to Redis, and deployed across AZs. At the next maintenance window, instance B picked up all traffic automatically and neither customers nor sales noticed. The extra 300 yuan a month for the instance cost far less than the inquiries lost in one outage.

Drill scenario Actions Expected result If it fails
Kill the app process systemctl stop app LB removes it within 30 seconds, traffic shifts to B Check the health check path
Cut the app’s network Block the port with iptables Removed within 10 seconds Check the security group allows the LB subnet
Reboot the server reboot Rejoins the pool automatically after boot Check the startup script and health
Database hiccup Inject a slow query App is not wrongly removed health must not query the DB
Shift all traffic to B Set B’s weight to 100 B handles it alone for 5 minutes Load-test B’s capacity

FAQ

How much daily traffic before you need a load balancer?
There’s no hard number, but if two of these three signals show up, it’s time: a single 4-core 8GB box runs above 60% CPU long-term, releases require downtime, or you’ve already lost inquiries to a single-machine failure. The reality for a DTC site is that one machine plus a CDN is enough up to 50,000 daily visits. Set up the CDN first and move static assets off the origin — origin load can drop by half, and often you don’t need multiple instances at all.
Should you enable session persistence at all?
By default, no. Move sessions to Redis or switch to JWT so the app is stateless, and any instance can handle any request with no user impact when one dies. With sticky sessions on, every user on the dead machine drops off, which defeats the whole point of failover. Only enable cookie stickiness as a temporary stopgap when a legacy system truly can’t be changed away from local session dependencies.
Why shouldn’t health checks query the database?
Database hiccups spread to the load balancer. If /health runs a MySQL query and a slow query takes over 5 seconds, the LB marks that app instance unhealthy. When every instance queries the DB at the same time, they all get marked unhealthy at once and the whole site returns 503. The right approach: /health only reports that the process is alive. DB health goes into separate monitoring metrics and plays no part in the LB’s removal decision.
Can I put both instances on the same physical machine?
No. That defeats the purpose of failover. If that physical machine loses power or the host fails, both instances go down together. Put them in at least two availability zones (AZs), or at minimum on two different hosts. Cloud providers usually let you specify an anti-affinity policy when you create instances. If your budget only covers two, pick different availability zones.
How much do cloud load balancers cost per month?
An L4 LB runs about 80 to 120 yuan per month, an L7 LB about 150 to 250 yuan, plus roughly 0.5 yuan per GB of traffic. A standalone site usually moves a few hundred GB a month, so total LB cost stays under 200 yuan. AWS ALB costs a bit more: about 20 USD per month base fee plus traffic charges. If budget is tight, you can build your own LB with Nginx and save the monthly fee, but you maintain high availability yourself.
How do I verify failover actually works?
Run a live drill before launch. In staging or during a production off-peak window, manually stop the process on app A and hit the LB with curl continuously. Watch whether requests switch to B within 30 seconds with no errors. Then test whether the machine rejoins the pool automatically after a restart. If the drill fails, don’t launch. In a real outage, nobody gives you 30 seconds to troubleshoot.

Final thoughts

For a standalone site, half the value of load balancing is handling traffic and the other half is surviving failures. Most people can calculate the first half; the second half gets ignored. One extra app instance buys you “a machine goes down and users never notice” – and in B2B inquiry scenarios, that’s money.

Three steps to get there. First, make the app stateless and move sessions into Redis. Second, spin up a second instance, put it behind the cloud LB, and configure health checks. Third, run a kill-the-process drill and confirm traffic shifts within 30 seconds. Only after all three steps is a multi-instance architecture truly usable. For the monitoring and alerting that goes with it, see Site monitoring and alerts. For the advanced setup with auto scaling, see Auto Scaling.

One line worth remembering: load balancing isn’t about splitting traffic. It’s about making sure that when any machine goes down, neither users nor inquiries notice a thing.

Crater
Crater Crater experts

Official Organization · Crater

Crater is a go-global services company specializing in WordPress standalone site development + Google SEO + GEO full-funnel growth, serving sectors including smart hardware, industrial equipment, SaaS, and new energy.

Product CapabilitiesMethodology WhitepaperIndustry Report
965articles shared
78knowledge base articles