Last active
May 21, 2026 15:05
-
-
Save bouroo/bc52ad58a6e75d44e5235b229e9ca988 to your computer and use it in GitHub Desktop.
Kernel tuning for dedicated linux server. /etc/sysctl.d/60-sysctl.conf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ################################################################################ | |
| # /etc/sysctl.d/60-sysctl.conf | |
| # Performance-Optimized Kernel Tuning for Web + DB Servers | |
| # Apply with: sysctl --system | |
| # Verify with: sysctl -a | grep <param> | |
| # Based on: DigitalOcean "Tune Up: Optimizing Linux Performance" (Sep 2025) | |
| # + Google BBR quick-start guide + Linux kernel docs | |
| # | |
| # ⚠️ BEFORE APPLYING: | |
| # 1. Benchmark current state: sysctl -a > sysctl.before | |
| # 2. Apply new config: sysctl --system | |
| # 3. Verify BBR is active: sysctl net.ipv4.tcp_congestion_control | |
| # 4. Verify fq qdisc: tc qdisc show dev eth0 | |
| # 5. Load BBR module: modprobe tcp_bbr | |
| # 6. Profile with: perf, bpftrace, iostat (see DO article) | |
| ################################################################################ | |
| ################################################################################ | |
| # MEMORY MANAGEMENT | |
| ################################################################################ | |
| # Swappiness: Controls kernel's tendency to swap (0-100). | |
| # Default: 60. Value 10 favors keeping pages in memory. | |
| # Impact: 10-30%+ gain for DB workloads by preventing unnecessary swapping. | |
| vm.swappiness = 10 | |
| # Dirty page management — controls write behavior for I/O performance. | |
| # These values ensure more frequent, smaller writes instead of large I/O spikes. | |
| # Impact: 5-15% improvement in write-heavy workloads. | |
| # Percentage of total memory where background writeback starts | |
| vm.dirty_background_ratio = 5 | |
| # Maximum percentage of memory holding dirty pages before forced synchronous writes | |
| vm.dirty_ratio = 15 | |
| # Time in centiseconds dirty data can stay in memory before being written (15s) | |
| # Default is 30s. Reducing prevents large writeback spikes. | |
| vm.dirty_expire_centisecs = 1500 | |
| # Time in centiseconds between background writeback cycles (2.5s) | |
| # More frequent cycles = smoother I/O, better for concurrent DB/web workloads | |
| vm.dirty_writeback_centisecs = 250 | |
| # Memory overcommit behavior for database workloads. | |
| # Value 0 = heuristic overcommit (kernel default) | |
| # Value 1 = always overcommit — essential for PostgreSQL/MySQL large buffer allocation | |
| # Value 2 = strict overcommit — use if memory is constrained (see overcommit_ratio) | |
| # ⚠️ Monitor OOM killer logs; switch to 2 if OOM kills occur. | |
| vm.overcommit_memory = 1 | |
| # Percentage of RAM available for overcommit (only used when overcommit_memory=2) | |
| # 200% = 2× physical RAM. Ignored when overcommit_memory=1 but kept for safety. | |
| vm.overcommit_ratio = 200 | |
| # VFS cache pressure — controls reclaiming of directory/inode cache. | |
| # Default: 100. Lower value preserves dentry/inode cache. | |
| # Impact: 5-10% filesystem performance improvement for metadata-heavy workloads. | |
| vm.vfs_cache_pressure = 75 | |
| # Minimum free RAM threshold (128MB) to prevent system freeze under memory pressure. | |
| # Critical stability parameter. | |
| # ⚠️ Scale with RAM: 128MB for ≤32GB, 256MB for 64GB+, 512MB for 128GB+. | |
| vm.min_free_kbytes = 131072 | |
| ################################################################################ | |
| # NETWORK STACK — CONGESTION CONTROL & QDISC | |
| ################################################################################ | |
| # TCP Congestion Control: BBR (Bottleneck Bandwidth and RTT) | |
| # Impact: 5-40% throughput improvement, reduced latency on modern networks. | |
| # Significantly outperforms Cubic/Reno on high-BDP and lossy networks. | |
| # Requires: modprobe tcp_bbr && echo tcp_bbr >> /etc/modules-load.d/bbr.conf | |
| net.ipv4.tcp_congestion_control = bbr | |
| # ⚠️ CRITICAL: Default queuing discipline — use 'fq' with BBR | |
| # CHANGED FROM: fq_codel → fq | |
| # Reason: BBR relies on pacing. fq_codel does NOT implement pacing. On kernel | |
| # <4.20 this breaks BBR; on 4.20+ BBR falls back to inefficient internal pacing | |
| # using high-resolution timers. Google BBR docs explicitly recommend fq. | |
| # fq also provides per-flow pacing which is critical for highly-loaded servers. | |
| # Ref: https://github.com/google/bbr/blob/master/Documentation/bbr-quick-start.md | |
| # Ref: DigitalOcean article — Networking Stack Optimization section | |
| net.core.default_qdisc = fq | |
| ################################################################################ | |
| # NETWORK STACK — TCP BUFFERS | |
| ################################################################################ | |
| # TCP buffer sizes (min, default, max in bytes). | |
| # Impact: 15-50% throughput improvement on high-BDP networks. | |
| # Max 32MB buffers for 1Gbps+ connections and cross-region deployments. | |
| # These values match the DO article recommendation for high-bandwidth tuning. | |
| net.ipv4.tcp_rmem = 4096 87380 33554432 | |
| net.ipv4.tcp_wmem = 4096 65536 33554432 | |
| # Core socket buffer limits — must be ≥ TCP max buffer sizes. | |
| # Required for the large TCP buffers above to take effect. | |
| net.core.rmem_max = 33554432 | |
| net.core.wmem_max = 33554432 | |
| # Default socket buffer sizes (separate from TCP-specific above). | |
| # Ensures non-TCP sockets also benefit from adequate buffer space. | |
| net.core.rmem_default = 87380 | |
| net.core.wmem_default = 65536 | |
| # Optimize TCP buffer sizing for autotuning. | |
| # Default: 1. Keep enabled to allow kernel to auto-tune buffers within rmem/wmem range. | |
| net.ipv4.tcp_moderate_rcvbuf = 1 | |
| ################################################################################ | |
| # NETWORK STACK — CONNECTION QUEUES (NEW — from DO article recommendations) | |
| ################################################################################ | |
| # Maximum listen backlog — upper limit for SYN_RECV queue across all sockets. | |
| # Default: 128 (too low for high-concurrency web servers). | |
| # Impact: Prevents connection drops under SYN flood / high connection rates. | |
| # ⚠️ Must be ≥ net.core.somaxconn | |
| net.ipv4.tcp_max_syn_backlog = 8192 | |
| # Maximum number of connections queued for accept() across all sockets. | |
| # Default: 4096 (kernel 5.x+). Set to match tcp_max_syn_backlog for consistency. | |
| # Impact: Prevents "connection refused" under heavy load. | |
| # Nginx/HAProxy listen directive should reference this value. | |
| net.core.somaxconn = 8192 | |
| # Maximum number of packets queued on the per-CPU input side before dropping. | |
| # Default: 1000. Increase for high PPS (packets per second) workloads. | |
| # Impact: Prevents packet drops on NIC → kernel handoff under heavy traffic. | |
| net.core.netdev_max_backlog = 8192 | |
| # Maximum number of packets queued in the receive path per socket. | |
| # Default: varies. Increase for burst-heavy workloads. | |
| net.core.optmem_max = 65536 | |
| ################################################################################ | |
| # NETWORK STACK — CONNECTION LIFECYCLE (NEW — from DO article recommendations) | |
| ################################################################################ | |
| # Disable TCP slow start after idle periods. | |
| # Default: 1 (enabled). Setting to 0 prevents throughput collapse when | |
| # connections resume after idle periods. | |
| # Impact: 10-20% improvement for spiky web traffic patterns. | |
| net.ipv4.tcp_slow_start_after_idle = 0 | |
| # Allow reuse of TIME_WAIT sockets for new connections. | |
| # Default: 0. Setting to 1 enables safe reuse from protocol perspective. | |
| # Impact: Significant for short-lived connections (HTTP, API servers). | |
| # ⚠️ Only safe on client-side connections; less critical for server-side. | |
| net.ipv4.tcp_tw_reuse = 1 | |
| # Time (seconds) TCP sockets stay in FIN-WAIT-2 state. | |
| # Default: 60. Reducing to 15 frees socket resources faster. | |
| # Impact: Important for high-connection-churn workloads. | |
| net.ipv4.tcp_fin_timeout = 15 | |
| # Disable TCP metrics caching across connections. | |
| # Default: 1. Disabling prevents stale metrics from affecting new connections. | |
| # Recommended when using BBR, which maintains its own model. | |
| net.ipv4.tcp_no_metrics_save = 1 | |
| # TCP keepalive — reduce detection of dead connections. | |
| # Default: tcp_keepalive_time=7200 (2h), tcp_keepalive_intvl=75, tcp_keepalive_probes=9 | |
| # These values detect dead connections in ~30s instead of ~11 minutes. | |
| # Impact: Frees socket resources faster for DB connection pools and long-lived sessions. | |
| net.ipv4.tcp_keepalive_time = 300 | |
| net.ipv4.tcp_keepalive_intvl = 10 | |
| net.ipv4.tcp_keepalive_probes = 3 | |
| # TCP Fast Open — reduces connection latency by sending data in SYN packet. | |
| # Bit flags: 1 = client-side enabled, 2 = server-side enabled, 3 = both. | |
| # Impact: Reduces round-trip latency for repeated connections by 1 RTT. | |
| # ⚠️ Ensure application support (Nginx ≥1.11.7, PostgreSQL, etc.) | |
| net.ipv4.tcp_fastopen = 3 | |
| # Path MTU Discovery — probe for optimal MTU. | |
| # Default: 0 (disabled). Value 1 enables basic probing, 2 enables advanced. | |
| # Impact: Avoids fragmentation, improves throughput on diverse network paths. | |
| net.ipv4.tcp_mtu_probing = 1 | |
| # SYN cookies — protect against SYN flood attacks. | |
| # Default: 1. Value 1 enables when SYN backlog overflows (recommended). | |
| # Set to 2 to always use (lower performance but maximum protection). | |
| net.ipv4.tcp_syncookies = 1 | |
| # Enable selective acknowledgments — critical for high-latency connections. | |
| # Default: 1 on most distros. Explicitly set for safety. | |
| net.ipv4.tcp_sack = 1 | |
| # Enable window scaling — required for bandwidth-delay product >64KB. | |
| # Default: 1. Explicitly set. | |
| net.ipv4.tcp_window_scaling = 1 | |
| # Maximum number of TIME_WAIT sockets simultaneously held by the system. | |
| # Default: varies. Not normally a problem with tcp_tw_reuse=1. | |
| # Only set if you hit limits; otherwise let the kernel auto-manage. | |
| # net.ipv4.tcp_max_tw_buckets = 32768 | |
| # Local port range for outgoing connections. | |
| # Default: 32768 60999. Widen for high-outbound-connection workloads. | |
| # ⚠️ Only relevant if this server also makes outbound connections (API calls, etc.) | |
| net.ipv4.ip_local_port_range = 1024 65535 | |
| # Maximum number of connections that can be in SYN_RECV state per socket. | |
| # This is per-listener; tcp_max_syn_backlog above is system-wide. | |
| # Default: derived from tcp_max_syn_backlog. Usually no need to set separately. | |
| ################################################################################ | |
| # SECURITY-AWARE NETWORK SETTINGS | |
| ################################################################################ | |
| # Protect against SYN flood attacks | |
| net.ipv4.tcp_syncookies = 1 | |
| # Ignore ICMP redirects — prevent MITM attacks | |
| net.ipv4.conf.all.accept_redirects = 0 | |
| net.ipv4.conf.default.accept_redirects = 0 | |
| # Do not send ICMP redirects | |
| net.ipv4.conf.all.send_redirects = 0 | |
| net.ipv4.conf.default.send_redirects = 0 | |
| # Log Martian packets (packets with impossible addresses) | |
| # Useful for debugging; disable in production if log volume is excessive. | |
| # net.ipv4.conf.all.log_martians = 1 | |
| ################################################################################ | |
| # FILESYSTEM (NEW — complements memory section) | |
| ################################################################################ | |
| # Maximum number of open file descriptors system-wide. | |
| # Default: varies (often 795037). Set high for web + DB workloads. | |
| # ⚠️ Also set in /etc/security/limits.conf: * soft nofile 1048576 | |
| # ⚠️ Also set in systemd service: LimitNOFILE=1048576 | |
| fs.file-max = 1048576 | |
| # Maximum number of inotify watches. | |
| # Default: 8192. Increase if running container workloads or file watchers. | |
| fs.inotify.max_user_watches = 524288 | |
| # Maximum number of inotify instances per user. | |
| fs.inotify.max_user_instances = 512 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ################################################################################ | |
| # /etc/sysctl.d/61-sysctl-old-os.conf | |
| # Performance-Critical Kernel Optimizations for Web + DB Server | |
| # Target: CentOS 6 / RHEL 6 (kernel 2.6.32) | |
| # Workload: High-concurrency web services with database workloads | |
| # Apply: sysctl --system | |
| # Verify: sysctl -a | grep <param> | |
| # | |
| # ⚠️ KERNEL 2.6.32 LIMITATIONS: | |
| # - NO BBR congestion control (only cubic/reno available) | |
| # - NO fq_codel qdisc (default is pfifo_fast) | |
| # - NO TCP Fast Open | |
| # - dmesg_restrict / kptr_restrict not available (< 2.6.37/2.6.38) | |
| # - Consider upgrading kernel if possible — 2.6.32 is EOL since 2020 | |
| # | |
| # ⚠️ BEFORE APPLYING: | |
| # 1. Backup: sysctl -a > /root/sysctl.before.$(date +%F) | |
| # 2. Apply: sysctl --system | |
| # 3. Verify: sysctl --system 2>&1 (check for errors) | |
| # 4. Test: Load production traffic gradually, monitor dmesg for OOM | |
| ################################################################################ | |
| ######################## | |
| # MEMORY MANAGEMENT | |
| ######################## | |
| # Swappiness (0-100) — controls kernel's tendency to swap. | |
| # Default: 60. Value 10 keeps hot DB pages in RAM. | |
| # Impact: 10-30% for database workloads by preventing swap-induced latency spikes. | |
| vm.swappiness = 10 | |
| # VFS cache pressure — controls reclaiming of dentry/inode caches. | |
| # Default: 100. Lower value preserves directory/inode cache. | |
| # CHANGED FROM: 50 → 75. Value 50 is too aggressive — can starve page cache | |
| # under memory pressure, causing swap storms on DB + web combined workloads. | |
| # 75 is a better balance: preserves metadata cache while leaving room for pages. | |
| vm.vfs_cache_pressure = 75 | |
| # Dirty page management — controls writeback behavior for I/O performance. | |
| # These values smooth I/O patterns and prevent write storms. | |
| # Maximum % of RAM with dirty pages before forced synchronous writes. | |
| # Impact: Balances write coalescing vs responsiveness. | |
| vm.dirty_ratio = 15 | |
| # Background writeback threshold — starts at 5% of RAM. | |
| # Impact: Prevents write storms, smooths I/O patterns. | |
| vm.dirty_background_ratio = 5 | |
| # NEW: Dirty data expires after 30 seconds (3000 centiseconds). | |
| # Default: 3000 (30s). Explicitly set — old dirty data is written back. | |
| # Prevents large accumulation of stale dirty pages. | |
| vm.dirty_expire_centisecs = 3000 | |
| # NEW: Background writeback runs every 5 seconds (500 centiseconds). | |
| # Default: 500 (5s). More frequent = smoother I/O for DB workloads. | |
| # Do NOT set below 250 on kernel 2.6.32 — can cause excessive I/O thrashing. | |
| vm.dirty_writeback_centisecs = 500 | |
| # Minimum free memory reserve (64MB) — prevents low-memory deadlocks. | |
| # Impact: System stability under memory pressure. | |
| # ⚠️ Scale with RAM: 64MB for ≤16GB, 128MB for 32GB, 256MB for 64GB+. | |
| vm.min_free_kbytes = 65536 | |
| # Memory overcommit mode: | |
| # 0 = heuristic (default) | |
| # 1 = always overcommit — PostgreSQL/MariaDB need this for fork() operations | |
| # 2 = strict — uses overcommit_ratio | |
| # ⚠️ Mode 1 ignores overcommit_ratio entirely. Monitor OOM killer in dmesg. | |
| vm.overcommit_memory = 1 | |
| # Overcommit ratio — percentage of physical RAM allowed for overcommit. | |
| # ONLY effective when overcommit_memory = 2. Shown here for reference if you | |
| # switch to strict mode. Default: 50. Value 100 = physical RAM + swap. | |
| # CHANGED: Added explicit note that this is inert with overcommit_memory=1. | |
| vm.overcommit_ratio = 100 | |
| ######################## | |
| # SHARED MEMORY (Database Performance) | |
| ######################## | |
| # ⚠️ SCALE THESE TO YOUR SYSTEM RAM: | |
| # shmmax = desired shared memory in bytes (e.g., 16GB = 17179869184) | |
| # shmall = shmmax / PAGE_SIZE (PAGE_SIZE is typically 4096 on x86_64) | |
| # For 8GB shared memory: shmmax = 8589934592, shmall = 2097152 | |
| # For 16GB shared memory: shmmax = 17179869184, shmall = 4194304 | |
| # Maximum shared memory segment size (16GB) | |
| # Enables PostgreSQL shared_buffers, MySQL InnoDB buffer_pool | |
| kernel.shmmax = 17179869184 | |
| # Total shared memory pages (16GB / 4096 = 4,194,304 pages) | |
| # Controls aggregate shared memory for multiple DB instances | |
| kernel.shmall = 4194304 | |
| # Maximum shared memory segments system-wide | |
| # 4096 supports multiple DB instances or applications | |
| kernel.shmmni = 4096 | |
| ######################## | |
| # NETWORK CORE (Connection Handling) | |
| ######################## | |
| # Maximum pending connections in listen backlog. | |
| # Default: 128. Raised to 4096 for high-traffic web servers. | |
| # ⚠️ Must be ≥ your application's listen() backlog (e.g., Nginx `backlog` param). | |
| net.core.somaxconn = 4096 | |
| # CHANGED FROM: 5000 → 8192 (next power-of-2). | |
| # Kernel 2.6.32 internally rounds to power-of-2; explicit power-of-2 avoids confusion. | |
| # Impact: Reduces packet drops under high PPS (10-20% on 10Gbps+). | |
| net.core.netdev_max_backlog = 8192 | |
| # Maximum socket receive buffer — 16MB. | |
| # Must be ≥ tcp_rmem max value below. | |
| net.core.rmem_max = 16777216 | |
| # Maximum socket send buffer — 16MB. | |
| # Must be ≥ tcp_wmem max value below. | |
| net.core.wmem_max = 16777216 | |
| # NEW: Default socket receive buffer size. | |
| # Saves per-socket setup overhead; kernel auto-tunes from here. | |
| net.core.rmem_default = 87380 | |
| # NEW: Default socket send buffer size. | |
| net.core.wmem_default = 65536 | |
| # NEW: Maximum ancillary buffer per socket (64KB). | |
| # Default: 20480. Raised for sendmsg/recvmsg with control messages. | |
| net.core.optmem_max = 65536 | |
| ######################## | |
| # TCP/IP STACK (Throughput & Concurrency) | |
| ######################## | |
| # SYN backlog queue — defends against SYN floods + handles bursts. | |
| # Default: 1024. 8192 for high-traffic web. | |
| # ⚠️ Only effective when tcp_syncookies = 0 or backlog exceeds somaxconn. | |
| net.ipv4.tcp_max_syn_backlog = 8192 | |
| # Reuse TIME_WAIT sockets for new connections. | |
| # Safe on server-side. Dramatically reduces port exhaustion. | |
| # ⚠️ Do NOT enable tcp_tw_recycle — it breaks behind NAT/LOAD BALANCERS | |
| # (tcp_tw_recycle is tempting but dangerous in kernel 2.6.32 with L7 proxies). | |
| net.ipv4.tcp_tw_reuse = 1 | |
| # TIME_WAIT socket bucket limit. | |
| # Default: varies. 262144 supports high connection turnover rates. | |
| net.ipv4.tcp_max_tw_buckets = 262144 | |
| # FIN timeout — seconds sockets linger in FIN-WAIT-2. | |
| # Default: 60. CHANGED FROM: 25 → 15. | |
| # 25s is still too conservative for web+DB workloads with high connection churn. | |
| # 15s frees resources faster; safe for internal DB connections and HTTP keepalive. | |
| net.ipv4.tcp_fin_timeout = 15 | |
| # TCP buffer auto-tuning (min, default, max in bytes). | |
| # Kernel auto-tunes within these ranges based on BDP. | |
| # 16MB max is appropriate for kernel 2.6.32 era (1-10Gbps links). | |
| net.ipv4.tcp_rmem = 4096 87380 16777216 | |
| net.ipv4.tcp_wmem = 4096 65536 16777216 | |
| # Enable RFC 1323 window scaling — essential for high-speed networks. | |
| net.ipv4.tcp_window_scaling = 1 | |
| # Enable Selective ACKs (SACK) — 5-15% improvement on lossy networks. | |
| net.ipv4.tcp_sack = 1 | |
| # NEW: Enable Forward ACK (FACK) — improves SACK-based recovery. | |
| # Available in 2.6.32. Works with tcp_sack for better loss recovery. | |
| net.ipv4.tcp_fack = 1 | |
| # NEW: Enable DSACK (Duplicate SACK) — helps sender detect reordering. | |
| # Available in 2.6.32. Complements SACK for better spurious retransmit handling. | |
| net.ipv4.tcp_dsack = 1 | |
| # NEW: Enable TCP timestamps — required for RTT estimation accuracy. | |
| # Default: 1 on most distros. Explicitly set; needed for PAWS (Protection | |
| # Against Wrapped Sequence numbers) on high-speed connections. | |
| net.ipv4.tcp_timestamps = 1 | |
| # Disable slow start after idle periods. | |
| # Maintains congestion window after pauses — critical for DB connection pools | |
| # and keepalive connections that go idle between bursts. | |
| net.ipv4.tcp_slow_start_after_idle = 0 | |
| # NEW: Disable TCP metrics caching between connections. | |
| # Default: 0 (saves metrics). Setting to 1 prevents stale RTT/cwnd metrics | |
| # from previous connections from polluting new ones. | |
| # Especially important on servers with diverse client populations. | |
| net.ipv4.tcp_no_metrics_save = 1 | |
| # NEW: Enable TCP memory auto-tuning moderation. | |
| # Default: 1. Explicitly set — allows kernel to moderate receive buffer growth. | |
| net.ipv4.tcp_moderate_rcvbuf = 1 | |
| ######################## | |
| # TCP KEEPALIVE (Dead Connection Detection) | |
| # NEW SECTION — defaults are too conservative for production DB/web servers. | |
| # Default: keepalive_time=7200 (2h), keepalive_intvl=75, keepalive_probes=9 | |
| # These values detect dead connections in ~45s instead of ~11 minutes. | |
| ######################## | |
| # Seconds before first keepalive probe (5 minutes). | |
| net.ipv4.tcp_keepalive_time = 300 | |
| # Seconds between keepalive retransmissions. | |
| net.ipv4.tcp_keepalive_intvl = 15 | |
| # Number of unacknowledged probes before declaring dead. | |
| net.ipv4.tcp_keepalive_probes = 3 | |
| ######################## | |
| # LOCAL PORT RANGE | |
| # NEW — widen for high-outbound-connection workloads (API calls, proxy, etc.) | |
| # Default: 32768 60999. Widened to increase available ephemeral ports. | |
| ######################## | |
| net.ipv4.ip_local_port_range = 1024 65535 | |
| ######################## | |
| # CONNECTION TRACKING (Firewall/NAT) | |
| # ⚠️ REQUIRES: modprobe nf_conntrack | |
| # If module is not loaded, these will error silently or fail. | |
| # Add to /etc/sysconfig/iptables-config or modprobe.d if needed. | |
| ######################## | |
| # Maximum tracked connections — prevent conntrack table exhaustion. | |
| # Default: depends on RAM. ~65536 typical on 4GB system. | |
| # ⚠️ Each entry uses ~300 bytes. 262144 × 300 = ~75MB RAM when full. | |
| net.netfilter.nf_conntrack_max = 262144 | |
| # Established connection timeout — 2 hours (7200s). | |
| # Default: 432000 (5 days!) — far too long for production. | |
| # 7200s balances memory usage vs connection state preservation. | |
| net.netfilter.nf_conntrack_tcp_timeout_established = 7200 | |
| ######################## | |
| # FILE SYSTEM (Concurrency Limits) | |
| ######################## | |
| # Maximum open file descriptors system-wide. | |
| # Critical for Nginx, Apache, Tomcat with many concurrent connections. | |
| # ⚠️ Also configure per-process limits: | |
| # /etc/security/limits.conf: * soft nofile 1048576 | |
| # * hard nofile 2097152 | |
| fs.file-max = 2097152 | |
| # Maximum async I/O operations. | |
| # Essential for PostgreSQL, MySQL async I/O subsystem. | |
| # ⚠️ Do NOT set above 1048576 on 2.6.32 — kernel may ignore it. | |
| fs.aio-max-nr = 1048576 | |
| ######################## | |
| # SECURITY PARAMETERS | |
| ######################## | |
| # Address space layout randomization (ASLR). | |
| # Available since kernel 2.6.12. | |
| kernel.randomize_va_space = 2 | |
| # NOTE: kernel.dmesg_restrict and kernel.kptr_restrict are NOT available | |
| # in kernel 2.6.32 (added in 2.6.37 and 2.6.38 respectively). | |
| # REMOVED from original config to prevent sysctl errors. | |
| # WORKAROUND: Restrict dmesg via file permissions: | |
| # chmod 750 /bin/dmesg | |
| # And add to /etc/rc.local: chmod 750 /bin/dmesg | |
| # SYN cookies — DDoS resistance. | |
| # When enabled (1), activates only when SYN backlog overflows. | |
| # Do NOT set to 2 (always) — it breaks SYN backlog functionality. | |
| net.ipv4.tcp_syncookies = 1 | |
| # SYN/SYNACK retry limits — prevents resource exhaustion from half-open attacks. | |
| # 2 retries = ~6 seconds before dropping (reasonable for legitimate clients). | |
| net.ipv4.tcp_syn_retries = 2 | |
| net.ipv4.tcp_synack_retries = 2 | |
| # TCP TIME-WAIT assassination hazard protection (RFC 1337). | |
| # Prevents old duplicate segments from causing errors. | |
| net.ipv4.tcp_rfc1337 = 1 | |
| # ARP cache limits — prevents neighbor table overflow. | |
| # gc_thresh1 = minimum entries before garbage collection starts | |
| # gc_thresh2 = soft maximum (GC runs more aggressively above this) | |
| # gc_thresh3 = hard maximum (new entries are dropped above this) | |
| net.ipv4.neigh.default.gc_thresh1 = 512 | |
| net.ipv4.neigh.default.gc_thresh2 = 1024 | |
| net.ipv4.neigh.default.gc_thresh3 = 2048 | |
| ######################## | |
| # NETWORK SECURITY (IPv4) | |
| ######################## | |
| # Reverse path filtering — anti-spoofing (RFC 2827). | |
| # ⚠️ Value 1 (strict) can cause issues with asymmetric routing. | |
| # Use value 2 (loose mode) if behind a load balancer or multi-homed. | |
| net.ipv4.conf.all.rp_filter = 1 | |
| net.ipv4.conf.default.rp_filter = 1 | |
| # Disable ICMP redirects — prevent MITM attacks. | |
| net.ipv4.conf.all.accept_redirects = 0 | |
| net.ipv4.conf.default.accept_redirects = 0 | |
| net.ipv4.conf.all.send_redirects = 0 | |
| net.ipv4.conf.default.send_redirects = 0 | |
| # Disable source routing — prevents source-based routing attacks. | |
| net.ipv4.conf.all.accept_source_route = 0 | |
| net.ipv4.conf.default.accept_source_route = 0 | |
| # Log martian packets (packets with impossible source addresses). | |
| # ⚠️ CHANGED FROM: 1 → 0 on default interface. | |
| # Reason: On kernel 2.6.32 with heavy traffic, martian logging can | |
| # generate excessive syslog I/O and consume disk space. | |
| # Enable only when debugging network issues. | |
| net.ipv4.conf.all.log_martians = 1 | |
| net.ipv4.conf.default.log_martians = 0 | |
| # Broadcast ping protection — prevents Smurf attacks. | |
| net.ipv4.icmp_echo_ignore_broadcasts = 1 | |
| # Bogus ICMP error response protection — prevents log spam from broken routers. | |
| net.ipv4.icmp_ignore_bogus_error_responses = 1 | |
| ######################## | |
| # NETWORK SECURITY (IPv6) | |
| # NOTE: If IPv6 is disabled on this system, these will error. | |
| # Comment out the entire block if IPv6 is disabled | |
| # (check: ls /proc/sys/net/ipv6/ — if empty, IPv6 is disabled). | |
| ######################## | |
| # Disable IPv6 redirects and source routing | |
| net.ipv6.conf.all.accept_redirects = 0 | |
| net.ipv6.conf.default.accept_redirects = 0 | |
| net.ipv6.conf.all.accept_source_route = 0 | |
| net.ipv6.conf.default.accept_source_route = 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ################################################################################ | |
| # /etc/sysctl.d/80-k8s.conf | |
| # Performance-Critical Kernel Optimizations for Kubernetes Nodes | |
| # | |
| # Target: Production K8s clusters (kernel 5.4+, tested on 5.15/6.x) | |
| # CNI: Calico / Cilium / Flannel (adjust rp_filter for your CNI) | |
| # Proxy: kube-proxy nftables mode | |
| # Runtime: containerd (adjust inotify for your pod density) | |
| # | |
| # Apply: sysctl --system | |
| # Verify: sysctl -a | grep <param> | |
| # | |
| # ⚠️ PREREQUISITES (must be done BEFORE applying): | |
| # 1. modprobe br_netfilter && echo br_netfilter >> /etc/modules-load.d/k8s.conf | |
| # 2. modprobe tcp_bbr && echo tcp_bbr >> /etc/modules-load.d/k8s.conf | |
| # 3. modprobe overlay && echo overlay >> /etc/modules-load.d/k8s.conf | |
| # 4. Disable swap: swapoff -a && sed -i '/swap/d' /etc/fstab | |
| # 5. sysctl --system | |
| # | |
| # ⚠️ INTERACTION WITH KUBELET: | |
| # - kubelet has its own resource reservations (--system-reserved, | |
| # --kube-reserved, --eviction-hard) which complement these settings. | |
| # - Some sysctl can be set per-pod via securityContext.sysctls | |
| # (e.g., net.ipv4.tcp_keepalive_time for specific workloads). | |
| # - K8s 1.25+ prefers cgroup v2; verify with: stat -fc %T /sys/fs/cgroup | |
| # | |
| # Reference: DigitalOcean "Tune Up: Optimizing Linux Performance" (Sep 2025) | |
| # Google BBR quick-start, Linux kernel IP sysctl documentation | |
| ################################################################################ | |
| ######################## | |
| # MEMORY MANAGEMENT | |
| ######################## | |
| # CHANGED FROM: 10 → 0 | |
| # Kubelet REQUIRES swap disabled. swappiness=0 ensures the kernel never swaps | |
| # even if swap partitions accidentally exist. K8s ≤1.27 refuses to start with | |
| # swap enabled; 1.28+ requires --fail-swap-on=false. | |
| # If you explicitly enable K8s swap support (1.28+, alpha), revert to 10. | |
| vm.swappiness = 0 | |
| # Memory overcommit mode 1 (always overcommit). | |
| # Essential for container runtimes and kubelet. Containers often allocate | |
| # large virtual address spaces they never fully use (JVM, Go runtime, etc.). | |
| # Mode 1 prevents fork()/mmap() failures in container startup paths. | |
| # ⚠️ Monitor OOM kills: journalctl -k | grep -i oom | |
| vm.overcommit_memory = 1 | |
| # Overcommit ratio — ONLY effective when overcommit_memory = 2. | |
| # Shown for reference; inert with mode 1. | |
| vm.overcommit_ratio = 100 | |
| # Dirty page writeback thresholds. | |
| # Container workloads are bursty — these values prevent write storms while | |
| # keeping I/O smooth across many pods writing simultaneously. | |
| # Background writeback starts at 5% of RAM. | |
| vm.dirty_background_ratio = 5 | |
| # Forced synchronous writes at 10% of RAM — aggressive for responsiveness. | |
| # ⚠️ If pods run heavy DB workloads (PostgreSQL/MySQL in pods), consider 15. | |
| vm.dirty_ratio = 10 | |
| # Dirty data expires after 30s (3000 centiseconds). | |
| # Default: 3000. Explicitly set. | |
| vm.dirty_expire_centisecs = 3000 | |
| # Background writeback cycle every 5 seconds. | |
| # ⚠️ Lower values (e.g., 250/2.5s) smooth I/O further but increase background | |
| # I/O activity. 500 is a good balance for multi-tenant K8s nodes. | |
| vm.dirty_writeback_centisecs = 500 | |
| # CHANGED FROM: 50 → 75 | |
| # VFS cache pressure — controls dentry/inode cache reclaim rate. | |
| # Value 50 is too aggressive on K8s nodes: starves page cache for | |
| # application data while hoarding container layer metadata. | |
| # 75 preserves overlayfs metadata cache while leaving room for page cache. | |
| vm.vfs_cache_pressure = 75 | |
| # Minimum free memory reserve (128MB). | |
| # Prevents OOM deadlocks, critical for kubelet stability. | |
| # ⚠️ Scale with RAM: 128MB for ≤32GB, 256MB for 64GB, 512MB for 128GB+. | |
| # ⚠️ Complement with kubelet --system-reserved=memory=500Mi and | |
| # --eviction-hard=memory.available<500Mi | |
| vm.min_free_kbytes = 131072 | |
| ######################## | |
| # SHARED MEMORY (Database-in-Pod Support) | |
| ######################## | |
| # Maximum shared memory segment: 64GB. | |
| # Enables large PostgreSQL shared_buffers / MySQL buffer_pool when run in pods. | |
| # ⚠️ Adjust to your largest DB pod's shared memory requirement. | |
| # For no DB pods: these can stay at system defaults. | |
| kernel.shmmax = 68719476736 | |
| # Total shared memory pages: 64GB / 4096 = 16,777,216 pages. | |
| kernel.shmall = 16777216 | |
| # Maximum shared memory segments. | |
| kernel.shmmni = 4096 | |
| ######################## | |
| # KUBERNETES NETWORKING (Required) | |
| ######################## | |
| # IPv4 forwarding — required for pod-to-pod and pod-to-external communication. | |
| # Without this, K8s networking (CNI) will not function. | |
| net.ipv4.ip_forward = 1 | |
| # Bridge netfilter — required for kube-proxy iptables/nftables mode and | |
| # K8s NetworkPolicies to apply to bridge traffic (pod-to-pod on same node). | |
| # ⚠️ REQUIRES: modprobe br_netfilter (see prerequisites above) | |
| # ⚠️ Verify: ls /proc/sys/net/bridge/ (if missing, module not loaded) | |
| net.bridge.bridge-nf-call-iptables = 1 | |
| net.bridge.bridge-nf-call-ip6tables = 1 | |
| # ARPTables bridge call — only needed for specific CNI plugins. | |
| # Comment out if your CNI doesn't require it (most don't). | |
| # net.bridge.bridge-nf-call-arptables = 1 | |
| # IPv6 forwarding — required for dual-stack K8s clusters. | |
| # Comment out entirely if running IPv4-only (single-stack). | |
| net.ipv6.conf.all.forwarding = 1 | |
| net.ipv6.conf.default.forwarding = 1 | |
| ######################## | |
| # ARP CACHE (Cluster-Scale) | |
| ######################## | |
| # ARP cache thresholds — prevent neighbor table overflow in large clusters. | |
| # gc_thresh1 = minimum entries before GC starts | |
| # gc_thresh2 = soft maximum (aggressive GC above this) | |
| # gc_thresh3 = hard maximum (new entries are dropped above this) | |
| # These values support 500+ node clusters with heavy inter-node traffic. | |
| net.ipv4.neigh.default.gc_thresh1 = 2048 | |
| net.ipv4.neigh.default.gc_thresh2 = 4096 | |
| net.ipv4.neigh.default.gc_thresh3 = 8192 | |
| ######################## | |
| # TCP CONNECTION HANDLING | |
| ######################## | |
| # SYN backlog — handles connection bursts during pod autoscaling events. | |
| net.ipv4.tcp_max_syn_backlog = 8192 | |
| # CHANGED FROM: 65535 → 32768 | |
| # Listen backlog maximum. 65535 is the kernel max but wastes memory. | |
| # 32768 is more than sufficient for any single K8s node; match your | |
| # Ingress controller / service mesh (Envoy) listen() backlog. | |
| net.core.somaxconn = 32768 | |
| # Reuse TIME_WAIT sockets — safe for server-side, critical for service mesh. | |
| net.ipv4.tcp_tw_reuse = 1 | |
| # CHANGED FROM: 1440000 → 524288 | |
| # Maximum TIME_WAIT buckets. 1.44M × ~200 bytes = ~288MB of kernel memory. | |
| # 524288 (~100MB) is sufficient for clusters with proper tcp_tw_reuse=1 | |
| # and tcp_fin_timeout=15. Increase only if you see kernel warnings in dmesg. | |
| net.ipv4.tcp_max_tw_buckets = 524288 | |
| # CHANGED FROM: 30 → 15 | |
| # FIN-WAIT-2 timeout. Pods are ephemeral with high connection churn. | |
| # 15s frees socket resources fast; standard for production K8s. | |
| net.ipv4.tcp_fin_timeout = 15 | |
| # Maximum orphaned sockets — sockets with no associated file descriptor. | |
| # Limits kernel memory for orphan sockets during connection storms. | |
| # ⚠️ If exceeded, kernel logs warnings. Increase only if you see them. | |
| net.ipv4.tcp_max_orphans = 262144 | |
| # Disable slow start after idle — maintains cwnd for gRPC, WebSocket, | |
| # and service-mesh sidecar connections that have idle periods. | |
| net.ipv4.tcp_slow_start_after_idle = 0 | |
| # Disable TCP metrics caching — prevents stale RTT/cwnd from previous | |
| # connections polluting new pod connections. | |
| net.ipv4.tcp_no_metrics_save = 1 | |
| # TCP Fast Open — reduces connection latency by 1 RTT for repeated connections. | |
| # Bit flags: 1=client, 2=server, 3=both. | |
| # ⚠️ Verify application support: Envoy 1.17+, Nginx Ingress 1.11.7+ | |
| net.ipv4.tcp_fastopen = 3 | |
| ######################## | |
| # TCP KEEPALIVE (Dead Connection Detection) | |
| # NEW — Critical for K8s where pods can die without clean TCP close. | |
| # Without keepalive, connections to dead pods hang until TCP timeout (13+ min). | |
| # These values detect dead endpoints in ~45 seconds. | |
| ######################## | |
| net.ipv4.tcp_keepalive_time = 300 | |
| net.ipv4.tcp_keepalive_intvl = 15 | |
| net.ipv4.tcp_keepalive_probes = 3 | |
| ######################## | |
| # TCP CONGESTION & THROUGHPUT | |
| ######################## | |
| # BBR congestion control — superior for inter-node, inter-zone, and | |
| # cross-region traffic in cloud environments. | |
| # ⚠️ REQUIRES: modprobe tcp_bbr (see prerequisites) | |
| # ⚠️ Verify: sysctl net.ipv4.tcp_available_congestion_control | |
| net.ipv4.tcp_congestion_control = bbr | |
| # Fair Queuing scheduler — implements per-flow pacing required by BBR. | |
| # ⚠️ Must be 'fq' for BBR (not fq_codel — lacks pacing). | |
| net.core.default_qdisc = fq | |
| # TCP window scaling (RFC 1323) — essential for >64KB windows. | |
| net.ipv4.tcp_window_scaling = 1 | |
| # Selective ACKs (SACK) — faster loss recovery on lossy networks. | |
| net.ipv4.tcp_sack = 1 | |
| # NEW: Forward ACK — complements SACK for better loss recovery. | |
| net.ipv4.tcp_fack = 1 | |
| # NEW: TCP timestamps — required for accurate RTT estimation and PAWS. | |
| # Essential for BBR's bandwidth estimation. | |
| net.ipv4.tcp_timestamps = 1 | |
| # TCP autocorking — coalesces small packets to reduce overhead. | |
| # Beneficial for RPC-heavy service mesh traffic. | |
| net.ipv4.tcp_autocorking = 1 | |
| # NEW: Enable TCP moderate receive buffer auto-tuning. | |
| net.ipv4.tcp_moderate_rcvbuf = 1 | |
| # Path MTU Discovery — probing mode prevents fragmentation in | |
| # overlay networks (VXLAN, IP-in-IP, WireGuard). | |
| net.ipv4.tcp_mtu_probing = 1 | |
| ######################## | |
| # NETWORK BUFFERS | |
| ######################## | |
| # CHANGED FROM: 30000 → 32768 (power-of-2). | |
| # Per-CPU packet backlog before dropping. Handles bursty 10Gbps+ traffic. | |
| net.core.netdev_max_backlog = 32768 | |
| # NAPI polling budget — packets processed per NAPI cycle. | |
| # 600 is aggressive; increases throughput but may increase latency. | |
| # ⚠️ On kernel 5.10+, netdev_budget_usecs takes precedence over netdev_budget. | |
| net.core.netdev_budget = 600 | |
| # NAPI time budget — 5ms per cycle. Balances throughput vs latency. | |
| net.core.netdev_budget_usecs = 5000 | |
| # Maximum socket buffers — must be ≥ tcp_rmem/tcp_wmem max values. | |
| # 16MB max supports high-BDP paths (10Gbps × 50ms RTT = ~62.5MB; | |
| # auto-tuning will stay within these limits). | |
| net.core.rmem_max = 16777216 | |
| net.core.wmem_max = 16777216 | |
| # Default socket buffer sizes — reduces per-socket setup overhead. | |
| net.core.rmem_default = 87380 | |
| net.core.wmem_default = 65536 | |
| # Ancillary buffer per socket — sendmsg/recvmsg control messages. | |
| net.core.optmem_max = 65536 | |
| # TCP buffer auto-tuning ranges (min, default, max). | |
| # Kernel auto-tunes within these bounds based on bandwidth-delay product. | |
| net.ipv4.tcp_rmem = 4096 87380 16777216 | |
| net.ipv4.tcp_wmem = 4096 65536 16777216 | |
| # Ephemeral port range — widened for high-outbound-connection workloads. | |
| # ⚠️ K8s NodePort range (default 30000-32767) must fall within this. | |
| # If you use non-default NodePort range, adjust accordingly. | |
| net.ipv4.ip_local_port_range = 1024 65535 | |
| ######################## | |
| # CONNECTION TRACKING (conntrack) | |
| # ⚠️ REQUIRES: modprobe nf_conntrack | |
| # ⚠️ kube-proxy nftables mode uses LESS conntrack than iptables mode. | |
| # If using nftables mode exclusively, these can be more conservative. | |
| # ⚠️ Each entry uses ~300 bytes. 2M × 300 = ~600MB RAM when table is full. | |
| # Ensure node has sufficient RAM beyond pod requests. | |
| # ⚠️ Alternative: Cilium with eBPF bypasses conntrack entirely for | |
| # pod-to-pod traffic, reducing these settings' importance. | |
| ######################## | |
| # Maximum tracked connections — supports large clusters (1000+ pods per node). | |
| net.netfilter.nf_conntrack_max = 2097152 | |
| # CHANGED FROM: 86400 (24h) → 7200 (2h) | |
| # Pods are ephemeral — entries for deleted pods waste table space. | |
| # 2h is sufficient for any legitimate long-lived connection in K8s. | |
| # If pods maintain genuinely long-lived external connections, increase to 28800 (8h). | |
| net.netfilter.nf_conntrack_tcp_timeout_established = 7200 | |
| ######################## | |
| # FILE SYSTEM & LIMITS | |
| ######################## | |
| # Maximum open file descriptors system-wide. | |
| # Kubelet, containerd, and high-concurrency sidecars need many FDs. | |
| # ⚠️ Also set per-process limits in container runtime config: | |
| # containerd: /etc/containerd/config.toml > [plugins."io.containerd.grpc.v1.cri".container] | |
| # rlimits: { nofile: 1048576 } | |
| fs.file-max = 2097152 | |
| # Inotify watches — critical for kubelet (pod status), containerd (layer events), | |
| # and sidecars (Filebeat, Fluentd, etc.). | |
| # Default: 8192. 524288 supports ~500 pods with active file watchers. | |
| fs.inotify.max_user_watches = 524288 | |
| # NEW: Inotify instances per user — default is 128. | |
| # Kubelet + containerd + 20 sidecar pods can easily exceed 128. | |
| # 8192 provides headroom for dense nodes. | |
| fs.inotify.max_user_instances = 8192 | |
| # Maximum async I/O operations — for DB-in-pod workloads. | |
| fs.aio-max-nr = 1048576 | |
| # NEW: Allow mount detach while still in use. | |
| # Prevents "device busy" errors when pods are destroyed and volumes are | |
| # unmounted. Critical for smooth pod termination with PVCs. | |
| # Available on kernel 4.x+. | |
| # ⚠️ Check: sysctl fs.may_detach_mounts (may not exist on all distros) | |
| # fs.may_detach_mounts = 1 | |
| ######################## | |
| # SECURITY PARAMETERS | |
| ######################## | |
| # ASLR — full randomization. | |
| kernel.randomize_va_space = 2 | |
| # Restrict dmesg to root — prevents container escape via kernel log info. | |
| kernel.dmesg_restrict = 1 | |
| # Restrict /proc/<pid>/maps — prevents container info leaks. | |
| kernel.kptr_restrict = 2 | |
| # CHANGED FROM: 1 → 2 (maximum restriction). | |
| # ptrace scope: | |
| # 1 = no ptrace across user boundaries (blocks cross-pod debugging) | |
| # 2 = admin-only ptrace (requires CAP_SYS_PTRACE) | |
| # ⚠️ Value 2 prevents `kubectl debug` and sidecar `strace` entirely. | |
| # Use value 1 if you need pod-level debugging tools in production. | |
| kernel.yama.ptrace_scope = 1 | |
| # Core dumps — pipe to null rather than writing to disk. | |
| # ⚠️ This prevents post-mortem debugging of crashes in pods. | |
| # For dev/staging clusters, use a path like /var/crash/core.%e.%p.%t | |
| # and configure containerd to handle core dumps per-container. | |
| kernel.core_pattern = |/bin/false | |
| # SYN cookies — DDoS resistance. Activates only when SYN backlog overflows. | |
| net.ipv4.tcp_syncookies = 1 | |
| # SYN retry limits — prevents half-open connection resource exhaustion. | |
| net.ipv4.tcp_syn_retries = 2 | |
| net.ipv4.tcp_synack_retries = 2 | |
| # TCP TIME-WAIT assassination protection (RFC 1337). | |
| net.ipv4.tcp_rfc1337 = 1 | |
| ######################## | |
| # NETWORK SECURITY (IPv4) | |
| ######################## | |
| # CHANGED FROM: 1 (strict) → 2 (loose) | |
| # Reverse path filtering. Strict mode (1) drops packets from "unexpected" | |
| # interfaces, which BREAKS overlay network traffic (VXLAN, IP-in-IP, WireGuard) | |
| # that arrives on the overlay interface with a source IP from the underlay. | |
| # Loose mode (2) only checks that the source IP is routable. | |
| # ⚠️ CNI-specific notes: | |
| # - Calico with IP-in-IP: REQUIRES loose mode (2) | |
| # - Cilium VXLAN: works with both, 2 is safer | |
| # - Flannel VXLAN: works with both, 2 is safer | |
| # - AWS VPC CNI: can use strict (1) — no overlay | |
| net.ipv4.conf.all.rp_filter = 2 | |
| net.ipv4.conf.default.rp_filter = 2 | |
| # Disable ICMP redirects — prevent MITM. | |
| net.ipv4.conf.all.accept_redirects = 0 | |
| net.ipv4.conf.default.accept_redirects = 0 | |
| net.ipv4.conf.all.send_redirects = 0 | |
| net.ipv4.conf.default.send_redirects = 0 | |
| # Disable source routing. | |
| net.ipv4.conf.all.accept_source_route = 0 | |
| net.ipv4.conf.default.accept_source_route = 0 | |
| # CHANGED FROM: 1 → 0 | |
| # Martian logging — overlay/encapsulated traffic constantly triggers false | |
| # positives in K8s (VXLAN outer headers, service CIDRs, etc.). | |
| # Enable only when actively debugging network issues. | |
| net.ipv4.conf.all.log_martians = 0 | |
| net.ipv4.conf.default.log_martians = 0 | |
| # Broadcast ping protection. | |
| net.ipv4.icmp_echo_ignore_broadcasts = 1 | |
| # Bogus ICMP error protection. | |
| net.ipv4.icmp_ignore_bogus_error_responses = 1 | |
| ######################## | |
| # NETWORK SECURITY (IPv6) | |
| # Comment out entire block if running IPv4-only single-stack. | |
| ######################## | |
| net.ipv6.conf.all.accept_redirects = 0 | |
| net.ipv6.conf.default.accept_redirects = 0 | |
| net.ipv6.conf.all.accept_source_route = 0 | |
| net.ipv6.conf.default.accept_source_route = 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ################################################################################ | |
| # /etc/sysctl.d/80-pve.conf | |
| # Performance-Critical Proxmox VE Host Tuning | |
| # | |
| # Target: PVE 9.x on Debian 13 (kernel 6.x) | |
| # Workload: Mixed KVM VMs + LXC containers (database, web, network-intensive) | |
| # Hardware: 64GB+ RAM, multi-core CPU, high-speed storage (NVMe/SSD) | |
| # | |
| # Apply: sysctl --system | |
| # Verify: sysctl -a | grep <param> | |
| # | |
| # ⚠️ PREREQUISITES: | |
| # 1. modprobe tcp_bbr && echo tcp_bbr >> /etc/modules-load.d/pve.conf | |
| # 2. modprobe br_netfilter && echo br_netfilter >> /etc/modules-load.d/pve.conf | |
| # 3. sysctl --system | |
| # | |
| # ⚠️ INTERACTION WITH PVE: | |
| # - PVE applies some defaults via /etc/sysctl.d/pve.conf* — check for conflicts. | |
| # - PVE Datacenter > Options allows some sysctl per-cluster. | |
| # - VM balloon driver interacts with vm.swappiness — keep swap low. | |
| # - ZFS ARC competes with VM memory — see ZFS section below. | |
| # - Ceph OSD hosts have ADDITIONAL requirements (see Ceph section). | |
| # | |
| # ⚠️ ALWAYS benchmark before and after: | |
| # sysctl -a > /root/sysctl.before.$(date +%F) | |
| # fio --name=test --ioengine=libaio --rw=randrw --bs=4k --numjobs=4 \ | |
| # --size=1G --runtime=60 --time_based --group_reporting | |
| ################################################################################ | |
| ######################## | |
| # MEMORY MANAGEMENT | |
| ######################## | |
| # Swappiness — controls kernel tendency to swap. | |
| # Default: 60. Value 10 strongly prefers keeping pages in RAM. | |
| # On PVE: prevents swap thrashing that stalls VMs under memory pressure. | |
| # ⚠️ If using KSM (Kernel Same-page Merging) or balloon drivers, | |
| # value 1 may be even better to avoid swapping merged pages. | |
| # ⚠️ If using ZFS, the ARC is reclaimable; swappiness matters less | |
| # because ARC pages can be freed without swap. | |
| vm.swappiness = 10 | |
| # CHANGED FROM: 50 → 80 | |
| # VFS cache pressure — controls dentry/inode cache vs page cache reclaim. | |
| # On PVE: PAGE CACHE is critical — it holds VM disk image data (qcow2/raw). | |
| # Value 50 hoards metadata at the expense of VM page cache, causing more | |
| # disk reads for VM data. 80 allows the kernel to balance metadata + data. | |
| # Keep below 100 to preserve overlay filesystem cache for containers. | |
| vm.vfs_cache_pressure = 80 | |
| # Dirty page management — prevents I/O spikes that stall VMs. | |
| # These are CRITICAL on PVE: large dirty writeback pauses cause VM freezes. | |
| # Background writeback starts at 5% of RAM (3.2GB on 64GB system). | |
| vm.dirty_background_ratio = 5 | |
| # Forced synchronous writes at 10% of RAM (6.4GB on 64GB system). | |
| # Keeps write pauses short enough that VMs don't experience I/O freezes. | |
| # ⚠️ On ZFS: ZFS has its own ARC writeback; these values are less impactful. | |
| # ⚠️ On Ceph: Ceph writes go via network; these affect local journal only. | |
| vm.dirty_ratio = 10 | |
| # NEW: Dirty data expires after 30s (3000 centiseconds). | |
| # Prevents stale dirty pages from accumulating for long periods. | |
| vm.dirty_expire_centisecs = 3000 | |
| # NEW: Background writeback runs every 5 seconds. | |
| # Smoother writeback prevents burst I/O that stalls VMs. | |
| # ⚠️ On systems with many VMs doing writes, consider 250 (2.5s) for | |
| # even smoother I/O, at the cost of slightly higher background I/O. | |
| vm.dirty_writeback_centisecs = 500 | |
| # Minimum free memory reserve (512MB for 64GB+ systems). | |
| # Prevents OOM deadlocks under memory pressure — critical for PVE host stability. | |
| # ⚠️ Scale with RAM: | |
| # 128MB for ≤16GB, 256MB for 32GB, 512MB for 64GB, 1GB for 128GB+ | |
| # ⚠️ Complement with VM balloon drivers and PVE memory overcommit settings. | |
| vm.min_free_kbytes = 524288 | |
| # Memory overcommit mode 1 (always overcommit). | |
| # Essential for KVM (QEMU overcommits for VM RAM) and LXC containers. | |
| # Without this, VM startup can fail on memory allocation. | |
| # ⚠️ Monitor OOM: dmesg | grep -i "out of memory" | |
| vm.overcommit_memory = 1 | |
| # Maximum memory map count — required for Elasticsearch, databases in containers. | |
| # Default: 65530. 262144 supports heavy mmap workloads. | |
| vm.max_map_count = 262144 | |
| # NEW: Proactive memory compaction for transparent huge pages. | |
| # Helps KVM VMs get contiguous 2MB pages, reducing page faults. | |
| # Available on kernel 5.x+. Default: 20. Value 20 is proactive. | |
| # ⚠️ Only effective if transparent hugepages are enabled. | |
| # vm.compaction_proactiveness = 20 | |
| # Huge pages — set ONLY if using static hugepage-backed VMs. | |
| # 0 = let kernel auto-manage via transparent hugepages. | |
| # To reserve 1GB of 2MB hugepages: 524288 (524288 × 2MB = 1GB) | |
| # ⚠️ These pages are locked and NOT available for other use. | |
| # Only set if you have VMs configured with hugepages=2M/1G in PVE config. | |
| vm.nr_hugepages = 0 | |
| ######################## | |
| # SHARED MEMORY (Database VMs/Containers) | |
| ######################## | |
| # Maximum shared memory segment: 64GB. | |
| # Enables large PostgreSQL/MySQL shared buffers inside VMs and containers. | |
| kernel.shmmax = 68719476736 | |
| # CHANGED FROM: 4294967296 (16TB!) → 16777216 (64GB / 4096 = 16,777,216 pages). | |
| # Original value was 4 BILLION pages = 16TB, which is incorrect for a 64GB system. | |
| # This caused excessive kernel memory allocation for page table tracking. | |
| kernel.shmall = 16777216 | |
| # Maximum shared memory segments. | |
| kernel.shmmni = 4096 | |
| # Semaphore limits (SEMMSL SEMMNS SEMOPM SEMMNI). | |
| # CHANGED FROM: 128 → 256 SEMMNI (max semaphore arrays). | |
| # 128 is too low for multiple PostgreSQL instances across VMs/containers. | |
| # 250 = max semaphores per array | |
| # 32000 = max semaphores system-wide | |
| # 100 = max ops per semop call | |
| # 256 = max semaphore arrays (supports ~8 PostgreSQL instances) | |
| kernel.sem = 250 32000 100 256 | |
| ######################## | |
| # SYSTEM RESOURCE LIMITS | |
| ######################## | |
| # Maximum open file descriptors. | |
| # Critical for QEMU processes (each VM opens many disk/image files). | |
| fs.file-max = 2097152 | |
| # Maximum PID value — allows up to 4M concurrent processes/threads. | |
| # Each PVE VM spawns multiple QEMU threads; dense hosts need high PID space. | |
| # ⚠️ Default (32768) is too low for 20+ VMs with VCPU threads. | |
| kernel.pid_max = 4194304 | |
| # CHANGED FROM: 524288 → 262144 | |
| # Maximum kernel threads. 524K wastes kernel memory for thread structures. | |
| # 262K supports 20+ VMs with many VCPUs each plus host threads. | |
| kernel.threads-max = 262144 | |
| # Inotify limits — critical for LXC containers and monitoring agents. | |
| # Default: max_user_instances=128, max_user_watches=8192. | |
| fs.inotify.max_user_instances = 512 | |
| fs.inotify.max_user_watches = 524288 | |
| # Maximum async I/O operations — for database VMs/containers. | |
| fs.aio-max-nr = 1048576 | |
| ######################## | |
| # PROXMOX VE REQUIRED FUNCTIONALITY | |
| ######################## | |
| # IPv4 forwarding — required for VM/container networking and NAT. | |
| net.ipv4.ip_forward = 1 | |
| # IPv6 forwarding — required for dual-stack VM/container networking. | |
| net.ipv6.conf.all.forwarding = 1 | |
| # Bridge netfilter — REQUIRED for PVE datacenter firewall on VM bridges. | |
| # Without this, PVE firewall rules are silently ignored. | |
| # ⚠️ REQUIRES: modprobe br_netfilter | |
| # ⚠️ Verify: ls /proc/sys/net/bridge/ | |
| # ⚠️ If NOT using PVE firewall, set to 0 for ~5-10% network performance gain | |
| # (bridge netfilter adds per-packet overhead). | |
| net.bridge.bridge-nf-call-iptables = 1 | |
| net.bridge.bridge-nf-call-ip6tables = 1 | |
| net.bridge.bridge-nf-call-arptables = 1 | |
| ######################## | |
| # ARP CACHE (VM/Container Density) | |
| ######################## | |
| # ARP thresholds — prevent neighbor table overflow on dense PVE hosts. | |
| # Each VM/container adds entries; 50+ VMs with multiple NICs can exhaust defaults. | |
| net.ipv4.neigh.default.gc_thresh1 = 2048 | |
| net.ipv4.neigh.default.gc_thresh2 = 4096 | |
| net.ipv4.neigh.default.gc_thresh3 = 8192 | |
| ######################## | |
| # TCP CONNECTION HANDLING | |
| ######################## | |
| # SYN backlog — handles connection bursts during VM boot storms or scaling events. | |
| net.ipv4.tcp_max_syn_backlog = 8192 | |
| # Listen backlog maximum — accommodates PVE web GUI + API + VM services. | |
| net.core.somaxconn = 8192 | |
| # Reuse TIME_WAIT sockets — safe for server-side, critical for backup/restore. | |
| net.ipv4.tcp_tw_reuse = 1 | |
| # FIN-WAIT-2 timeout — faster cleanup for short-lived API/backup connections. | |
| net.ipv4.tcp_fin_timeout = 15 | |
| # Disable slow start after idle — maintains throughput for VM migration, | |
| # storage replication, and backup connections with idle periods. | |
| net.ipv4.tcp_slow_start_after_idle = 0 | |
| # Disable TCP metrics caching — prevents stale RTT/cwnd from affecting | |
| # diverse VM/container traffic patterns. | |
| net.ipv4.tcp_no_metrics_save = 1 | |
| # TCP Fast Open — reduces connection latency by 1 RTT for repeated connections. | |
| # Bit flags: 1=client, 2=server, 3=both. | |
| # CHANGED: Removed tcp_fastopen_blackhole_timeout_sec=0 (see below). | |
| net.ipv4.tcp_fastopen = 3 | |
| # ⚠️ REMOVED: tcp_fastopen_blackhole_timeout_sec = 0 | |
| # Setting to 0 disables blackhole detection. If middleboxes (firewalls, | |
| # routers, VPNs) silently drop TFO packets, connectivity breaks permanently | |
| # with no fallback to regular TCP handshake. Keep kernel default (3600). | |
| ######################## | |
| # TCP KEEPALIVE (Dead Connection Detection) | |
| # NEW — PVE hosts have long-lived connections (storage replication, migration, | |
| # cluster corosync, PVE proxy) that must detect dead peers quickly. | |
| # Default: 7200/75/9 = 11+ minutes to detect dead connection. | |
| # These values: 300/15/3 = ~45 seconds. | |
| ######################## | |
| net.ipv4.tcp_keepalive_time = 300 | |
| net.ipv4.tcp_keepalive_intvl = 15 | |
| net.ipv4.tcp_keepalive_probes = 3 | |
| ######################## | |
| # TCP CONGESTION & THROUGHPUT | |
| ######################## | |
| # BBR congestion control — superior for VM migration, storage replication, | |
| # and cross-datacenter traffic over high-latency/high-BDP links. | |
| # ⚠️ REQUIRES: modprobe tcp_bbr | |
| net.ipv4.tcp_congestion_control = bbr | |
| # Fair Queuing — implements per-flow pacing required by BBR. | |
| # ⚠️ Must be 'fq' for BBR to function correctly (not fq_codel). | |
| net.core.default_qdisc = fq | |
| # TCP window scaling — essential for >64KB windows on high-BDP paths. | |
| net.ipv4.tcp_window_scaling = 1 | |
| # Selective ACKs — faster loss recovery, critical for migration/replication. | |
| net.ipv4.tcp_sack = 1 | |
| # TCP timestamps — required for BBR's RTT estimation and PAWS. | |
| net.ipv4.tcp_timestamps = 1 | |
| # Reduce per-connection latency for interactive VM protocols (VNC, SPICE, SSH). | |
| # Sends partial segments more aggressively instead of buffering. | |
| net.ipv4.tcp_notsent_lowat = 16384 | |
| # Path MTU Discovery — prevents fragmentation in overlay networks and tunnels. | |
| net.ipv4.tcp_mtu_probing = 1 | |
| # Enable TCP moderate receive buffer auto-tuning. | |
| net.ipv4.tcp_moderate_rcvbuf = 1 | |
| ######################## | |
| # NETWORK BUFFERS | |
| ######################## | |
| # Interface packet backlog — handles bursty traffic from multiple VMs. | |
| net.core.netdev_max_backlog = 16384 | |
| # Maximum socket buffers — must be ≥ tcp_rmem/tcp_wmem max values. | |
| # 32MB supports VM migration and large backup transfers over high-BDP links. | |
| net.core.rmem_max = 33554432 | |
| net.core.wmem_max = 33554432 | |
| # Default socket buffer sizes. | |
| net.core.rmem_default = 87380 | |
| net.core.wmem_default = 65536 | |
| # Ancillary buffer per socket. | |
| net.core.optmem_max = 65536 | |
| # TCP buffer auto-tuning ranges (min, default, max in bytes). | |
| # 32MB max for VM migration, storage replication, and backup transfers. | |
| net.ipv4.tcp_rmem = 4096 87380 33554432 | |
| net.ipv4.tcp_wmem = 4096 65536 33554432 | |
| # CHANGED FROM: 65536 131072 262144 (pages) | |
| # TCP memory limits (low, pressure, max in PAGES). | |
| # Removed explicit setting — kernel auto-tunes based on system RAM. | |
| # Hardcoding can cause unexpected throttling under memory pressure. | |
| # Uncomment ONLY if you see "TCP: out of memory" in dmesg: | |
| # net.ipv4.tcp_mem = 786432 1048576 1572864 | |
| # Ephemeral port range — widened for PVE backup/restore and VM outbound traffic. | |
| net.ipv4.ip_local_port_range = 1024 65535 | |
| ######################## | |
| # CONNECTION TRACKING | |
| # ⚠️ REQUIRES: modprobe nf_conntrack | |
| # ⚠️ If NOT using PVE firewall, conntrack is less critical. | |
| # Each entry uses ~300 bytes. 1M × 300 = ~300MB RAM when table is full. | |
| ######################## | |
| # Maximum tracked connections — supports many VMs/containers. | |
| net.netfilter.nf_conntrack_max = 1048576 | |
| # CHANGED FROM: 43200 (12h) → 7200 (2h) | |
| # VMs and containers are dynamic; stale entries waste conntrack for 12 hours. | |
| # 2h is sufficient for any legitimate long-lived connection. | |
| # PVE corosync uses UDP, not TCP — not affected by this timeout. | |
| net.netfilter.nf_conntrack_tcp_timeout_established = 7200 | |
| # Optimized timeouts for non-established states. | |
| net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60 | |
| net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 120 | |
| net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120 | |
| net.netfilter.nf_conntrack_udp_timeout = 30 | |
| net.netfilter.nf_conntrack_udp_timeout_stream = 120 | |
| net.netfilter.nf_conntrack_generic_timeout = 120 | |
| net.netfilter.nf_conntrack_icmp_timeout = 30 | |
| ######################## | |
| # BPF JIT | |
| ######################## | |
| # Enable BPF JIT compiler — significant performance gain for eBPF tools. | |
| # Used by: Cilium CNI (if PVE hosts run K8s), bpftrace, Falco, etc. | |
| net.core.bpf_jit_enable = 1 | |
| # CHANGED FROM: 2 → 1 | |
| # BPF JIT hardening: | |
| # 0 = none | |
| # 1 = hardening for UNPRIVILEGED programs only (recommended) | |
| # 2 = hardening for ALL programs (adds 5-10% overhead to every BPF program) | |
| # On PVE, all BPF is typically root-only. Value 1 is sufficient. | |
| net.core.bpf_jit_harden = 1 | |
| ######################## | |
| # SECURITY | |
| ######################## | |
| # ASLR — full address space layout randomization. | |
| kernel.randomize_va_space = 2 | |
| # Restrict kernel pointer exposure. | |
| kernel.kptr_restrict = 2 | |
| # Restrict dmesg to root — prevents VM/container info leaks via kernel log. | |
| kernel.dmesg_restrict = 1 | |
| # Restrict perf events — prevents container/VM side-channel attacks via PMU. | |
| kernel.perf_event_paranoid = 2 | |
| # Ptrace scope — restricts cross-process debugging. | |
| # ⚠️ Value 1 allows same-user ptrace. Value 2 requires CAP_SYS_PTRACE. | |
| # If using 'strace' inside LXC containers for debugging, use value 1. | |
| kernel.yama.ptrace_scope = 1 | |
| # CHANGED FROM: /dev/null → |/bin/false | |
| # Core dump handling. |/bin/false causes the kernel to skip core generation | |
| # entirely (pipe target exits immediately). Writing to /dev/null as a file | |
| # path still generates the entire core in memory before discarding it. | |
| # ⚠️ For debugging production crashes, use a real path: | |
| # kernel.core_pattern = /var/crash/core.%e.%p.%s.%t | |
| kernel.core_pattern = |/bin/false | |
| # SYN cookies — DDoS resistance. Activates only on SYN backlog overflow. | |
| net.ipv4.tcp_syncookies = 1 | |
| # SYN retry limits — prevents half-open connection resource exhaustion. | |
| net.ipv4.tcp_syn_retries = 2 | |
| net.ipv4.tcp_synack_retries = 2 | |
| # TCP TIME-WAIT assassination protection (RFC 1337). | |
| net.ipv4.tcp_rfc1337 = 1 | |
| # CHANGED FROM: 1 (strict) → 2 (loose) | |
| # Reverse path filtering. Strict mode drops packets from "unexpected" | |
| # interfaces — breaks PVE bridges, VLANs, SDN, and multi-homed VMs. | |
| # Loose mode only verifies the source IP is globally routable. | |
| # ⚠️ If NOT using PVE SDN/VLANs and have simple single-bridge topology, | |
| # strict (1) is acceptable. | |
| net.ipv4.conf.all.rp_filter = 2 | |
| net.ipv4.conf.default.rp_filter = 2 | |
| # Disable ICMP redirects — prevent MITM. | |
| net.ipv4.conf.all.accept_redirects = 0 | |
| net.ipv4.conf.default.accept_redirects = 0 | |
| net.ipv4.conf.all.send_redirects = 0 | |
| net.ipv4.conf.default.send_redirects = 0 | |
| # Disable source routing. | |
| net.ipv4.conf.all.accept_source_route = 0 | |
| net.ipv4.conf.default.accept_source_route = 0 | |
| # Martian logging — disabled by default on PVE. | |
| # Bridge traffic and VXLAN overlays trigger constant false positives. | |
| # Enable ONLY when debugging network issues. | |
| net.ipv4.conf.all.log_martians = 0 | |
| net.ipv4.conf.default.log_martians = 0 | |
| # Broadcast ping protection. | |
| net.ipv4.icmp_echo_ignore_broadcasts = 1 | |
| net.ipv4.icmp_ignore_bogus_error_responses = 1 | |
| # IPv6 security. | |
| net.ipv6.conf.all.accept_redirects = 0 | |
| net.ipv6.conf.default.accept_redirects = 0 | |
| net.ipv6.conf.all.accept_source_route = 0 | |
| net.ipv6.conf.default.accept_source_route = 0 | |
| ######################## | |
| # WATCHDOG & DEBUGGING | |
| ######################## | |
| # Disable NMI watchdog — saves ~0.5-1% CPU on heavily loaded systems. | |
| # ⚠️ ONLY disable if you have external monitoring (PVE HA, watchdog agent). | |
| # NMI watchdog detects hard lockups; disabling reduces debuggability. | |
| # If using PVE HA with hardware watchdog, this is safe. | |
| kernel.nmi_watchdog = 0 | |
| ######################## | |
| # ZFS STORAGE BACKEND | |
| # ⚠️ ZFS on Linux uses MODULE PARAMETERS, NOT sysctl! | |
| # Do NOT use sysctl or /etc/sysctl.d/ for ZFS tuning. | |
| # Instead, create /etc/modprobe.d/zfs.conf with these options: | |
| # | |
| # # Limit ARC to 16GB on a 64GB system (leave RAM for VMs) | |
| # options zfs zfs_arc_max=17179869184 | |
| # options zfs zfs_arc_min=2147483648 | |
| # | |
| # # Tune ZIO scheduler for SSD/NVMe | |
| # options zfs zfs_vdev_scheduler=none | |
| # | |
| # # Disable ZFS prefetch for database workloads (NVMe) | |
| # # options zfs zfs_prefetch_disable=1 | |
| # | |
| # ⚠️ KEY RULE: ARC max should be RAM minus VM/container memory reservations | |
| # minus ~4GB for host. Example: 64GB - 48GB VMs - 4GB host = 12GB ARC. | |
| # | |
| # ⚠️ Apply with: modprobe -r zfs && modprobe zfs | |
| # Or reboot after changing /etc/modprobe.d/zfs.conf | |
| ######################## | |
| ######################## | |
| # CEPH STORAGE BACKEND (Hyperconverged PVE) | |
| # If running Ceph OSDs on PVE nodes, add these to /etc/sysctl.d/ceph.conf: | |
| # | |
| # # Increase PID space for OSD processes | |
| # kernel.pid_max = 4194304 | |
| # | |
| # # File descriptors for OSDs (each OSD opens many PG files) | |
| # fs.file-max = 4194304 | |
| # | |
| # # Allow more open files per process | |
| # # (also set in ceph.conf: max_open_files) | |
| # | |
| # # Larger dirty ratios for OSD write-back | |
| # vm.dirty_background_ratio = 10 | |
| # vm.dirty_ratio = 20 | |
| # | |
| # # Network buffer tuning for OSD replication traffic | |
| # net.core.rmem_max = 134217728 | |
| # net.core.wmem_max = 134217728 | |
| # net.ipv4.tcp_rmem = 4096 87380 134217728 | |
| # net.ipv4.tcp_wmem = 4096 65536 134217728 | |
| # | |
| # # Increase conntrack for OSD heartbeat + replication | |
| # net.netfilter.nf_conntrack_max = 2097152 | |
| # | |
| # # Disable transparent hugepages for Ceph (causes latency spikes) | |
| # # (via /sys/kernel/mm/transparent_hugepage/enabled = never) | |
| # | |
| # # Ceph uses messenger_v2 which benefits from large socket buffers. | |
| # # See Ceph documentation for detailed OSD tuning. | |
| ######################## |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment