I Thought My Nginx Config Was Fine. Then Black Friday Hit.
Alright, real story. No BS.
We had a client — small e-commerce shop, nothing crazy, like 2k visitors a day normally. I set up their Nginx, Cloudflare in front, everything looked good. I even tested with Apache Bench. 500 concurrent users? No problem. I was feeling good about myself.
Then Black Friday. 8am. Traffic went from 2k to like 18k in 20 minutes. And the site just... died. 502s everywhere. CPU was only at 40%. Memory was fine. I was like, what the hell?
What was actually wrong
It wasn't the backend. It wasn't Cloudflare. It was this:
# nginx.conf - the default I left in
events {
worker_connections 512;
}
512. That's the default. With 4 workers, that's only ~2k concurrent connections. And with keep-alive + websockets + images, you burn through that super fast.
I bumped it to 4096, reloaded, and boom — site came back in like 10 seconds. I felt like an idiot, but also relieved.
My current production template
This is what I actually use now on every new server. Copy it if you want:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 20M;
# Gzip - don't skip this
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml;
# Rate limit - saves you from bots
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
include /etc/nginx/sites-enabled/*;
}
3 things I check first now
1. worker_connections — If it's still 512, fix it.
2. client_max_body_size — Default is 1M. Your users will try to upload a 5MB image and get a 413. Set it to 20M.
3. proxy_read_timeout — If your backend is slow, default 60s will kill long requests. I use 120s for API servers.
Anyway, lesson learned. Don't trust defaults. Test with real traffic, not just ab -n 1000.
— Kev