Every November, the same story plays out. A Magento 2 store that comfortably handles 200 concurrent visitors suddenly gets 5,000. The database connection pool exhausts. Varnish cache miss rates spike because products are being updated mid-sale. The checkout times out. The payment gateway returns 502s. And by the time the engineering team rolls back the promotion, the brand has lost six figures in revenue and a significant chunk of customer trust.
Peak events — Black Friday, Cyber Monday, flash sales, product launches, viral campaigns — don't just test your infrastructure. They expose every shortcut, every unoptimized query, every misconfigured cache, and every missing monitor you've been getting away with during normal traffic.
In this post, I'll walk you through a structured preparation plan that starts 8-12 weeks before your peak event and covers infrastructure, caching, database, queues, monitoring, and incident response.
Start 8-12 Weeks Before: The Audit Phase
Peak preparation is not a last-minute activity. You need at least 8-12 weeks to identify issues, implement fixes, test under load, and roll back changes that don't work.
1. Baseline Your Current Performance
Before you optimize anything, measure everything. You need a baseline to compare against.
# Capture key metrics for 7 days during normal traffic
# — p95 and p99 response times per route type
# — Database CPU, memory, connections, slow query count
# — Redis hit/miss ratio and evictions
# — Varnish hit rate and n_lru_nuked
# — PHP-FPM process count and queue depth
# — Bandwidth and requests per second
Use Magento's built-in profiler, New Relic, or Blackfire to capture per-route performance. Pay special attention to:
- Product page TTFB — this is your highest-traffic page type
-
Checkout AJAX endpoints —
rest/V1/guest-carts/*andrest/V1/carts/mine/* - Category page load — especially layered navigation
- Search queries — especially if using Elasticsearch/OpenSearch
2. Run a Load Test Against Production-Like Staging
If you don't have a staging environment that mirrors production, create one. A load test against undersized infrastructure tells you nothing about how production will behave.
Use k6, Gatling, or Locust to simulate:
- Normal load — your average daily peak (e.g., 200 concurrent users)
- 1.5x normal — moderate spike
- 3x normal — flash sale scenario
- 5x normal — Black Friday worst case
- Sustained load — 30+ minutes at peak, not just 2-minute bursts
// k6 example — Black Friday simulation
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 200 }, // ramp up
{ duration: '10m', target: 200 }, // sustained normal peak
{ duration: '2m', target: 1000 }, // flash sale spike
{ duration: '5m', target: 1000 }, // sustained spike
{ duration: '2m', target: 2500 }, // Black Friday peak
{ duration: '10m', target: 2500 }, // sustained BF
{ duration: '3m', target: 0 }, // ramp down
],
};
export default function () {
// Mix of browsing patterns
const res = http.get('https://your-store.com/best-selling-category');
check(res, { 'status 200': r => r.status === 200 });
sleep(1);
}
Identify your breaking point — the concurrency level where response times degrade non-linearly. That's your ceiling. Everything you do from here should raise that ceiling.
Infrastructure Scaling: Pre-Scale, Don't Auto-Scale
Auto-scaling is reactive. By the time your auto-scaler detects a traffic spike and provisions new instances, the damage is already done. For peak events, pre-scale.
3. Right-Size Your Web Nodes
Calculate the number of PHP-FPM workers you need:
workers_needed = (peak_concurrent_users × avg_response_time) / (php_fpm_workers_per_node × nodes)
If you expect 2,500 concurrent users, average response time is 0.8 seconds, and each node runs 30 PHP-FPM workers:
workers_needed = (2500 × 0.8) / 30 = 67 workers across all nodes
With 8 PHP-FPM workers per node, you need at least 9 nodes. With 30 workers per node (tuned), you need 3 nodes. Tune PHP-FPM first, then calculate node count.
4. Pre-Provision Extra Nodes
Don't wait for auto-scaling. 24 hours before your peak event, manually scale up:
# If using AWS Auto Scaling Groups
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name magento-prod-asg \
--min-size 5 \
--max-size 10 \
--desired-capacity 5
# If using Kubernetes
kubectl scale deployment magento-web --replicas=8
After the event, scale back down to your normal baseline.
5. Database Capacity Check
Your database is the single point of failure. Before a peak event:
-
Check connection pool headroom — if you're at 70% of
max_connectionsduring normal traffic, you'll exhaust it during peak. Increasemax_connectionsand ensure PHP-FPMpm.max_children× total web nodes + cron workers + admin workers <max_connections. - Verify replication lag — if you use read replicas, replication lag should be under 1 second. If it's above 5 seconds, banner pricing and inventory will be stale.
- Pre-warm the InnoDB buffer pool — run your most frequent queries against the largest tables to load them into memory:
-- Force-load key tables into buffer pool
SELECT COUNT(*) FROM catalog_product_entity_int WHERE 1=0;
SELECT COUNT(*) FROM catalog_product_index_price WHERE 1=0;
SELECT COUNT(*) FROM url_rewrite WHERE 1=0;
SELECT COUNT(*) FROM catalog_category_product_index WHERE 1=0;
Caching Strategy: Maximize Hit Rate Before the Rush
6. Varnish: Audit and Pre-Warm
Varnish is your first line of defense. During peak events, your Varnish hit rate should be above 90%. If it's below 80%, you're leaking database queries.
# Check current hit rate
varnishstat -1 | grep cache_hit,cache_miss,cache_hit_grace
# Calculate hit rate
# hit_rate = cache_hit / (cache_hit + cache_miss)
If hit rate is low, investigate:
- Are product saves flushing too much cache? — Magento's cache tags can be overly aggressive. Check if indexers are running on schedule.
- Are you using grace mode? — Serve stale content while fresh content is generated:
sub vcl_backend_response {
set beresp.grace = 6h;
}
sub vcl_recv {
if (req.url ~ "/checkout" || req.url ~ "/customer") {
return (pass);
}
set req.grace = 6h;
}
Pre-warm the cache 2 hours before the event:
#!/bin/bash
# warm-cache.sh — crawl sitemap and cache all URLs
SITEMAP="https://your-store.com/sitemap.xml"
URLS=$(curl -s $SITEMAP | grep -oP '(?<=<loc>)[^<]+')
for url in $URLS; do
curl -s -o /dev/null -w "%{http_code} %{time_total}s $url\n" "$url"
done
echo "Cache warmed: $(echo $URLS | wc -w) URLs"
Run this from a server inside your VPC (so Varnish sees internal traffic) or from a CDN edge location.
7. Redis: Check Memory and Eviction Rate
Redis serves both session data and cache. During peak, session write volume explodes and cache evictions can trigger thundering herds.
# Check Redis memory and evictions
redis-cli INFO memory | grep used_memory_human
redis-cli INFO stats | grep evicted_keys
redis-cli INFO stats | grep keyspace_misses
If evicted_keys is growing during normal traffic, you need more Redis memory. For peak events:
-
Increase
maxmemory— give Redis at least 2x its normal allocation - Split cache and sessions — use separate Redis instances for FPC cache vs sessions. Session writes should never evict page cache entries.
-
Set
maxmemory-policy—allkeys-lrufor cache,volatile-lrufor sessions (only evict keys with TTL)
8. Full Page Cache Hole-Punching Audit
Every hole-punch in your FPC is a database query. During peak events, hole-punched blocks can cause 3-5x more load. Audit your cacheable=false layout blocks:
# Find all non-cacheable blocks
grep -rn 'cacheable="false"' app/code/ vendor/ --include="*.xml"
Each one is a route to bypass Varnish. During peak events:
- Disable personalized blocks if they're not critical (e.g., "Recently Viewed Products")
- Use JavaScript widgets to load personalized content client-side instead of hole-punching
- Set
ttlvalues on cache blocks to ensure they expire gracefully
Database and Indexer Preparation
9. Schedule Indexers Off-Peak
Indexers running during your peak event is a recipe for disaster. They cause cache flushes, lock tables, and consume CPU.
# Check indexer schedule
bin/magento indexer:status
# Set all indexers to "Update by Schedule" if not already
bin/magento indexer:set-mode schedule
# Ensure cron runs indexers at low-traffic times
# In crontab or cron_groups, schedule for 2-5 AM
Before the event: run a full reindex and cache flush 12 hours before peak. Then disable scheduled indexers during peak hours if your catalog doesn't change during the sale:
# Pause indexers (Magento 2.4+)
bin/magento indexer:set-mode schedule
# Or disable specific indexers that aren't needed during the event
10. Clean Up Abandoned Carts and Logs
bloated quote table means slower checkouts. Before peak:
-- Delete abandoned carts older than 30 days
DELETE FROM quote WHERE customer_is_guest = 1
AND updated_at < DATE_SUB(NOW(), INTERVAL 30 DAY)
AND is_active = 1 AND items_count = 0;
-- Clean quote_item orphaned records
DELETE FROM quote_item WHERE quote_id NOT IN (SELECT entity_id FROM quote);
-- Truncate changelogs (safe when indexers are up to date)
TRUNCATE TABLE catalog_product_price_cl;
TRUNCATE TABLE cataloginventory_stock_status_cl;
TRUNCATE TABLE catalogrule_product_cl;
Also truncate the report_event, report_viewed_product_index, and customer_visitor tables if they're large.
Queue and Async Processing
11. Configure RabbitMQ for Peak Throughput
If you're using RabbitMQ for async operations (order processing, email sending, inventory updates), tune it before peak:
// env.php — increase consumer prefetch and workers
'queue' => [
'consumers' => [
'max_concurrent' => 5,
' prefetch_count' => 20
]
]
Start extra consumers before the event:
# Start additional consumers
bin/magento queue:consumers:start async.operations.all --max-concurrent=5 &
bin/magento queue:consumers:start inventory.source.items.cleanup &
bin/magento queue:consumers:start media.storage.cleanup &
Monitor queue depth during the event — if any queue backs up, you're not processing fast enough:
# RabbitMQ queue depth monitoring
rabbitmqctl list_queues name messages consumers
12. Move Non-Critical Work to Async
During peak, every millisecond counts. Move these operations to async processing:
- Order confirmation emails — queue instead of inline
- Customer notifications — queue instead of inline
- Analytics event tracking — batch and queue
- Image resizing — pre-generate all needed sizes before the event
- Sitemap generation — disable during peak
Monitoring and Incident Response
13. Set Up Real-Time Alerting
You need alerts that fire before the customer notices, not after. Set thresholds:
| Metric | Warning | Critical |
|---|---|---|
| Response time p95 | > 1.5s | > 3s |
| Varnish hit rate | < 85% | < 70% |
| DB CPU | > 70% | > 90% |
| Redis evictions/sec | > 100 | > 500 |
| PHP-FPM max children reached | > 0 | > 0 |
| Checkout error rate | > 0.5% | > 2% |
| RabbitMQ queue depth | > 100 | > 500 |
| DB slow queries/min | > 20 | > 50 |
Use whatever monitoring stack you have — New Relic, Datadog, Prometheus + Grafana — but make sure alerts go to a pager, not just a dashboard nobody watches.
14. Prepare a Runbook
Write a one-page runbook that any team member can follow. Include:
-
How to put the site in maintenance mode —
bin/magento maintenance:enable -
How to flush cache —
bin/magento cache:flushandvarnishadm ban 'req.url ~ "."' -
How to disable a problematic module —
bin/magento module:disable Vendor_Module - How to scale up nodes — the exact commands
- How to enable read-only mode — disable checkout and show a banner
- How to roll back a deploy — the exact CI/CD commands
- Contact list — hosting provider, CDN, payment gateway, DBA, dev team
15. Communication Protocol
During a peak event, communication is as important as engineering:
- Create a dedicated Slack/Teams channel — "black-friday-war-room"
- Assign roles — one person owns monitoring, one owns infrastructure, one owns customer comms
- Set check-in cadence — every 15 minutes during peak, every 30 minutes otherwise
- Have a roll-back decision maker — one person who can call "roll back" without a meeting
The Day Before: Final Checklist
Run through this checklist 24 hours before your peak event:
- [ ] All web nodes pre-scaled to peak capacity
- [ ] Varnish cache pre-warmed
- [ ] Full reindex completed and indexers paused
- [ ] Database:
max_connectionsverified, buffer pool pre-warmed, slow query log enabled - [ ] Redis memory increased, cache and sessions split
- [ ] RabbitMQ consumers started and queue depth baseline zero
- [ ] Non-critical cron jobs disabled (reports, sitemaps, analytics)
- [ ] Abandoned carts cleaned, log tables truncated
- [ ] Monitoring dashboards open, alerts configured
- [ ] Runbook printed, war-room channel created
- [ ] CDN origin shield enabled, rate limiting configured
- [ ] Payment gateway rate limits confirmed
- [ ] CDN edge locations purged for any recently changed content
- [ ] Backup taken, recovery tested
The Day After: Post-Event Review
Within 48 hours after the event, hold a blameless post-mortem:
- Compare actual metrics to predictions — were you faster or slower than expected?
- Document every incident — what happened, when, how it was resolved, time to detect, time to resolve
- Identify near-misses — things that almost broke but didn't (yet)
- Update your runbook — add anything you learned
- Scale back down — don't forget to reduce your infrastructure
- Clean up — re-enable cron jobs, indexers, and any features you disabled
Conclusion
Peak traffic preparation is not about adding more servers. It's about removing bottlenecks, maximizing cache efficiency, and having a plan for when things go wrong. The stores that survive Black Friday without a hitch are the ones that started preparing in July.
The audit phase catches configuration issues. The scaling phase gives you headroom. The caching phase keeps your database idle. The monitoring phase catches problems before customers do. And the runbook ensures that when things break — because something always breaks — your team reacts in minutes, not hours.
Start now. Don't wait until November.
Top comments (0)