When configuring hundreds of virtual hosts, long subdomains, or wildcard routing in Nginx, running nginx -t or reloading the service may abruptly fail with one of the following error messages:
nginx: [emerg] could not build the server_names_hash,
you should increase server_names_hash_bucket_size: 32
or:
nginx: [emerg] could not build the server_names_hash,
you should increase either server_names_hash_max_size: 512
or server_names_hash_bucket_size: 64
This error prevents Nginx from reloading or booting, dropping incoming traffic if a system restart is attempted. In this guide, we break down the internal mechanics of Nginx's static hash table allocation, explain how CPU L1 cache-line sizing dictates bucket limits, and provide the exact directives needed to resolve the error across Debian, Ubuntu, RHEL, and Docker environments.
[!TIP] Proactive Infrastructure Verification If your web servers fail to reload or silently drop virtual host routing due to hash collisions, test your endpoints and DNS mappings immediately using our free DNS Lookup Tool, HTTP Header Checker, and Ping Test Tool.
The Internal Mechanics of Nginx Hash Tables
Nginx uses static hash tables (ngx_hash_t) to achieve near-$\mathcal{O}(1)$ lookup performance for hostnames, MIME types, variables, and request headers. Instead of evaluating server names sequentially with regular expressions on every incoming request, Nginx builds an optimized in-memory lookup table during startup.
How Nginx Allocates Server Names
When Nginx parses the configuration files during initialization, it executes ngx_hash_init() and organizes domain names into three separate hash tables:
- Exact Hostnames: Full domains such as
api.example.comorapp.internal.domain. - Leading Wildcard Hostnames: Domains starting with a wildcard, such as
*.example.com(stored in reverse order, e.g.,com.example.*). - Trailing Wildcard Hostnames: Domains ending with a wildcard, such as
mail.*. - Regular Expressions: Evaluated sequentially only if no exact or wildcard match is found.
Each bucket in the hash table stores the hash key, the pointer to the name string, and the pointer to the corresponding virtual host configuration structure (ngx_http_core_srv_conf_t).
+-------------------------------------------------------------------+
| Nginx Memory Hash Table |
+-------------------------------------------------------------------+
| Bucket 0: [ Key | Name Ptr | Conf Ptr ] -> [ Key | Ptr | Conf ] |
| Bucket 1: [ Key | Name Ptr | Conf Ptr ] |
| Bucket 2: [ Empty ] |
| Bucket 3: [ Key | Name Ptr | Conf Ptr ] -> [ Key | Ptr | Conf ] |
| ... |
| Bucket N: [ Key | Name Ptr | Conf Ptr ] |
+-------------------------------------------------------------------+
^ ^
|-- server_names_hash_max_size (Table Limit) |-- server_names_hash_bucket_size (Bucket Capacity)
Why Hash Construction Fails
The hash generation algorithm calculates the total number of buckets required based on the total number of names and their length. Hash construction fails when:
- Bucket Overflow: The longest domain name (including null terminator and structural overhead) exceeds the memory capacity of a single bucket (
server_names_hash_bucket_size). - Table Saturation: The hash table experiences too many hash collisions, and the total number of buckets exceeds
server_names_hash_max_size.
Mathematically, if $L_{\max}$ represents the byte length of the longest configured domain string, $S_{\text{header}}$ represents the 8-byte Nginx hash element header, and $B$ represents server_names_hash_bucket_size, the minimum condition for a single key to fit within a bucket is:
$$\text{Bucket Capacity} \ge L_{\max} + S_{\text{header}} + \text{sizeof}(\text{void}*)$$
If this inequality is violated, Nginx terminates initialization immediately with an emergency log.
Step 1: Diagnose the Exact Failure Vector
Before modifying directives, identify whether your failure is driven by an excessively long domain name or an aggregate table saturation.
1. Run Syntax Verification
Execute the configuration test to read the diagnostic output:
sudo nginx -t
Example failure output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: [emerg] could not build the server_names_hash, you should increase server_names_hash_bucket_size: 64
nginx: configuration file /etc/nginx/nginx.conf test failed
2. Identify the Longest Domain Names
Audit all configured server_name entries across /etc/nginx/ to inspect long hostnames or unintended concatenation syntax errors:
# Extract all server_name declarations and sort by length
grep -rohP 'server_name\s+[^;]+;' /etc/nginx/ | \
sed -e 's/server_name//g' -e 's/;//g' | \
tr -s ' ' '\n' | \
awk '{ print length, $0 }' | \
sort -n -r | \
head -n 20
Common configuration mistakes uncovered by this command include:
- A missing semicolon
;between server blocks, causing Nginx to treat multiple domains as a single 150-character string. - Automatically generated AWS ELB, Kubernetes ingress, or CloudFront CNAME aliases that exceed standard 32-byte or 64-byte boundaries.
3. Check CPU L1 Cache Line Size
For optimal memory throughput, server_names_hash_bucket_size should always be an integer power of two ($32, 64, 128$) and aligned with the host CPU's L1 data cache line size:
# Check L1 Data Cache Line Size on Linux
getconf LEVEL1_DCACHE_LINESIZE
On modern x86_64 and ARM64 servers, this command typically outputs 64. Setting the bucket size to equal or double this value prevents cache-line splitting during memory access.
Step 2: Configure Hash Table Directives
Nginx hash directives must be declared inside the top-level http {} context in /etc/nginx/nginx.conf. Defining them inside a server {} or location {} block will trigger a directive placement error.
1. Update nginx.conf
Open /etc/nginx/nginx.conf in your preferred editor:
sudo nano /etc/nginx/nginx.conf
Locate the http block and define or increase server_names_hash_bucket_size and server_names_hash_max_size:
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# -----------------------------------------------------------
# Server Names Hash Optimization
# -----------------------------------------------------------
# Increase bucket size to accommodate long FQDNs (powers of 2: 64, 128)
server_names_hash_bucket_size 64;
# Increase maximum table size if managing hundreds of vhosts (512, 1024, 2048)
server_names_hash_max_size 1024;
# Connection and I/O settings
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
2. Sizing Recommendations by Fleet Scale
| Fleet / Domain Scale | Typical Host Count | Recommended bucket_size | Recommended max_size |
|---|---|---|---|
| Standard Server | 1 – 20 vhosts | 64 | 512 (default) |
| SaaS Multi-Tenant | 20 – 200 custom domains | 64 or 128 | 1024 |
| Large API Gateway / CDN Edge | 200 – 1000+ vhosts | 128 | 2048 or 4096 |
| Automated Cloud Staging | Long AWS/GCP dynamic subdomains | 128 | 1024 |
Step 3: Test and Apply Configuration
After updating the directives:
1. Validate Configuration Syntax
sudo nginx -t
Expected successful output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
If Nginx still reports an error requesting a larger bucket size (e.g., you should increase server_names_hash_bucket_size: 128), double the value from 64 to 128 in nginx.conf and re-test.
2. Reload Nginx Without Dropping Connections
Apply the changes gracefully without interrupting active TLS sessions:
# Systemd systems (Ubuntu, Debian, RHEL, Rocky)
sudo systemctl reload nginx
# Direct signal reload
sudo nginx -s reload
Edge Case: Docker & Automated Reverse Proxies
In containerized environments (such as Docker Compose, Kubernetes Ingress, or Traefik/Nginx sidecars), dynamically generated configuration templates often omit hash tuning directives.
Docker Compose Example
Mount a custom nginx.conf snippet or supply the directive in your base image:
services:
reverse-proxy:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./conf.d:/etc/nginx/conf.d:ro
Ensure ./nginx.conf sets server_names_hash_bucket_size 128; inside the http {} block before loading ./conf.d/*.conf.
Hash Table Configuration Directives Comparison
| Directive | Context | Default Value | Purpose |
|---|---|---|---|
server_names_hash_bucket_size | http | 32 / 64 (Arch/OS dependent) | Maximum memory byte size allocated per hash bucket. Must increase when individual domain names are long. |
server_names_hash_max_size | http | 512 | Total number of buckets in the hash table. Must increase when total number of virtual hosts causes hash collisions. |
types_hash_bucket_size | http | 64 | Hash bucket size for MIME type mappings (mime.types). |
variables_hash_max_size | http | 1024 | Maximum table size for Nginx runtime variables. |
Enterprise Monitoring & Infrastructure Resilience with Pingzo
Preventing silent configuration crashes and virtual host routing failures requires continuous external synthetic monitoring. When Nginx fails to reload or drops specific virtual hosts due to misconfigured routing, Pingzo detects the outage within seconds.
+-----------------------------------------------------------------------------+
| Pingzo Global Edge Network |
+-----------------------------------------------------------------------------+
| |
[ Multi-Region HTTP/S Checks ] [ Multi-Domain SSL & DNS Checks ]
| |
v v
+-----------------------------------------------------------------------------+
| Your Nginx Reverse Proxy / Load Balancer Cluster |
| (Alerting on 502, 500, Hash Mismatch, or Dead Virtual Hosts) |
+-----------------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------------+
| Instant Alerts via WhatsApp, Slack, & Telegram |
+-----------------------------------------------------------------------------+
Pingzo Monitoring Plans
| Feature | Free | Starter ($5/mo) | Pro ($12/mo) | Agency ($29/mo) |
|---|---|---|---|---|
| Check Frequency | 15 minutes | 5 minutes | 2 minutes | 1 minute |
| Monitors | 1 monitor | 10 monitors | Unlimited | Unlimited |
| Multi-Location Testing | Global | Global | Global | Global |
| SSL & DNS Audits | Basic | Advanced | Real-Time | Real-Time + Custom Root |
| Alert Channels | Email, Telegram, WhatsApp, Slack, Discord | All Channels + Multi-Recipient | Webhooks, SMS, White-Label Dashboards | |
| Multi-Tenant Management | No | No | No | Yes (Multi-Team) |
Deploy full-stack synthetic uptime monitoring and multi-host validation today at Pingzo.
Stop Finding Out About Outages from Angry Users
Get instant WhatsApp & Discord alerts the second your API, website, or server goes down. Setup in 30 seconds with 60-second checks.