Skip to content
Search RunEntLinux

Searches general documentation on RunEntLinux. Each distribution has its own documentation search.

    Browse this documentation

    Performance tuning is a systematic process: first identify the bottleneck, then optimize accordingly. Blindly adjusting parameters often does more harm than good. This article covers common tuning techniques and their applicable scenarios, helping you improve server performance step by step from kernel parameters to the application layer.

    Before tuning, you must record the current performance baseline; otherwise, there is no way to measure the effect of optimizations.

    Terminal window
    # Record basic system information
    uname -a
    cat /etc/os-release
    lscpu
    free -h
    lsblk
    # Install performance analysis tools
    sudo dnf install sysstat perf iotop htop -y
    # Enable sysstat data collection
    sudo systemctl enable --now sysstat
    # View historical performance data
    sar -u 1 5 # CPU usage
    sar -r 1 5 # Memory usage
    sar -d 1 5 # Disk I/O
    sar -n DEV 1 5 # Network throughput
    Terminal window
    # Quickly determine the bottleneck type
    # CPU bottleneck: load average consistently higher than the number of CPU cores
    uptime
    # Memory bottleneck: low available memory with active swap
    free -h
    vmstat 1 5
    # I/O bottleneck: high iowait
    iostat -xz 1 5
    # Network bottleneck: packet drops or saturated bandwidth
    ss -s
    sar -n DEV 1 5

    sysctl is used to modify kernel parameters at runtime.

    Terminal window
    # View all parameters
    sysctl -a
    # View a specific parameter
    sysctl net.core.somaxconn
    sysctl vm.swappiness
    # Temporary change (lost after reboot)
    sudo sysctl -w net.core.somaxconn=65535
    # Permanent change
    sudo tee /etc/sysctl.d/99-tuning.conf > /dev/null <<'EOF'
    # Custom kernel parameter tuning
    EOF
    # Apply the configuration
    sudo sysctl --system
    Terminal window
    sudo tee /etc/sysctl.d/99-network.conf > /dev/null <<'EOF'
    # ==============================
    # Network Performance Tuning
    # ==============================
    # --- TCP Buffers ---
    # Default and maximum TCP send/receive buffers (bytes)
    # Format: min default max
    net.core.rmem_default = 262144
    net.core.rmem_max = 16777216
    net.core.wmem_default = 262144
    net.core.wmem_max = 16777216
    net.ipv4.tcp_rmem = 4096 87380 16777216
    net.ipv4.tcp_wmem = 4096 65536 16777216
    # --- Connection Queues ---
    # Maximum listen queue length (affects connection establishment under high concurrency)
    net.core.somaxconn = 65535
    # Half-open connection queue size
    net.ipv4.tcp_max_syn_backlog = 65535
    # NIC receive queue length
    net.core.netdev_max_backlog = 65535
    # --- TCP Connection Optimization ---
    # Enable TCP Fast Open (reduces handshake latency)
    net.ipv4.tcp_fastopen = 3
    # TIME_WAIT related
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_fin_timeout = 15
    net.ipv4.tcp_max_tw_buckets = 262144
    # Keepalive parameters
    net.ipv4.tcp_keepalive_time = 600
    net.ipv4.tcp_keepalive_intvl = 30
    net.ipv4.tcp_keepalive_probes = 5
    # --- Congestion Control ---
    # Use the BBR congestion control algorithm (recommended)
    net.core.default_qdisc = fq
    net.ipv4.tcp_congestion_control = bbr
    # --- Local Port Range ---
    net.ipv4.ip_local_port_range = 1024 65535
    # --- Other ---
    # Allow more orphan sockets
    net.ipv4.tcp_max_orphans = 262144
    # SYN Flood protection
    net.ipv4.tcp_syncookies = 1
    EOF
    sudo sysctl --system

    Verify that BBR is active:

    Terminal window
    sysctl net.ipv4.tcp_congestion_control
    # Output should be: net.ipv4.tcp_congestion_control = bbr
    lsmod | grep bbr
    Terminal window
    sudo tee /etc/sysctl.d/99-memory.conf > /dev/null <<'EOF'
    # ==============================
    # Memory Tuning
    # ==============================
    # Swappiness: controls the kernel's tendency to swap memory pages to swap
    # 0 = avoid swap as much as possible (suitable for memory-rich database servers)
    # 10 = low swap tendency (recommended for most server scenarios)
    # 60 = default value
    vm.swappiness = 10
    # Dirty page flush policy
    # Background flush starts when dirty pages exceed this percentage of memory
    vm.dirty_background_ratio = 5
    # Foreground processes must wait for flush when dirty pages exceed this ratio
    vm.dirty_ratio = 15
    # Dirty page lifetime (centiseconds, 500 = 5 seconds)
    vm.dirty_expire_centisecs = 500
    # Flush thread wakeup interval (centiseconds)
    vm.dirty_writeback_centisecs = 100
    # VFS cache pressure (default 100)
    # Below 100 = prefer keeping dentry/inode cache
    # Above 100 = prefer reclaiming cache
    vm.vfs_cache_pressure = 50
    # Behavior when memory is exhausted
    # 0 = heuristic OOM (default)
    # 1 = allow overcommit
    # 2 = disallow overcommit
    vm.overcommit_memory = 0
    EOF
    sudo sysctl --system

    Huge pages reduce TLB misses and improve performance for memory-intensive applications (such as databases).

    Terminal window
    # View current huge page configuration
    grep -i huge /proc/meminfo
    # Calculate the number of huge pages needed
    # For example, to allocate 4GB for the database: 4096MB / 2MB (default huge page size) = 2048 pages
    echo "Huge pages needed: $((4096 / 2))"
    # Set the number of huge pages
    sudo sysctl -w vm.nr_hugepages=2048
    # Permanent configuration
    echo 'vm.nr_hugepages = 2048' | sudo tee -a /etc/sysctl.d/99-memory.conf
    sudo sysctl --system
    # Verify
    grep -i huge /proc/meminfo
    # HugePages_Total: 2048
    # HugePages_Free: 2048
    # Hugepagesize: 2048 kB

    THP is managed automatically by the kernel. It is beneficial for most server applications, but some databases (such as MongoDB and Redis) recommend disabling THP to avoid latency jitter.

    Terminal window
    # Check current status
    cat /sys/kernel/mm/transparent_hugepage/enabled
    # Temporarily disable
    echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
    echo never | sudo tee /sys/kernel/mm/transparent_hugepage/defrag
    # Permanently disable (via systemd)
    sudo tee /etc/systemd/system/disable-thp.service > /dev/null <<'EOF'
    [Unit]
    Description=Disable Transparent Huge Pages
    DefaultDependencies=no
    After=sysinit.target local-fs.target
    Before=basic.target
    [Service]
    Type=oneshot
    ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/enabled'
    ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/defrag'
    [Install]
    WantedBy=basic.target
    EOF
    sudo systemctl daemon-reload
    sudo systemctl enable disable-thp

    ulimit controls the maximum system resources available to user processes. Default values are typically conservative and may need adjusting for high-concurrency scenarios.

    Terminal window
    # View all limits for the current user
    ulimit -a
    # Key parameters:
    # -n Maximum open files (open files)
    # -u Maximum processes (max user processes)
    # -l Maximum locked memory (max locked memory)
    Terminal window
    # Modify system-level limits
    sudo tee /etc/security/limits.d/99-tuning.conf > /dev/null <<'EOF'
    # Format: <domain> <type> <item> <value>
    # All users
    * soft nofile 65535
    * hard nofile 131072
    * soft nproc 65535
    * hard nproc 131072
    # Specific user (e.g., nginx)
    nginx soft nofile 131072
    nginx hard nofile 262144
    # Root user
    root soft nofile 131072
    root hard nofile 262144
    EOF

    For systemd-managed services, you also need to set limits in the service file:

    Terminal window
    # Method 1: Modify systemd default limits
    sudo mkdir -p /etc/systemd/system.conf.d
    sudo tee /etc/systemd/system.conf.d/limits.conf > /dev/null <<'EOF'
    [Manager]
    DefaultLimitNOFILE=131072
    DefaultLimitNPROC=65535
    EOF
    # Method 2: Set limits for a specific service (recommended)
    sudo systemctl edit nginx
    # Add in the editor:
    # [Service]
    # LimitNOFILE=131072
    # LimitNPROC=65535
    sudo systemctl daemon-reload
    sudo systemctl restart nginx
    # Verify the actual limits for the service
    cat /proc/$(pidof nginx | awk '{print $1}')/limits

    tuned is a dynamic tuning daemon provided by RHEL-based distributions, with multiple built-in predefined tuning profiles.

    Terminal window
    sudo dnf install tuned -y
    sudo systemctl enable --now tuned
    # List all available tuning profiles
    tuned-adm list
    # Common profiles:
    # throughput-performance - High throughput optimization
    # latency-performance - Low latency optimization
    # network-latency - Network low latency
    # network-throughput - Network high throughput
    # virtual-guest - Virtual machine guest
    # virtual-host - Virtualization host
    # postgresql - PostgreSQL optimization
    # View current profile
    tuned-adm active
    # Switch profiles
    sudo tuned-adm profile throughput-performance
    # Get the recommended profile
    tuned-adm recommend
    # View profile details
    tuned-adm profile_info throughput-performance
    Terminal window
    # Create a custom profile based on an existing one
    sudo mkdir -p /etc/tuned/my-web-server
    sudo tee /etc/tuned/my-web-server/tuned.conf > /dev/null <<'EOF'
    [main]
    summary=Custom tuning for web server
    include=throughput-performance
    [sysctl]
    net.core.somaxconn = 65535
    net.ipv4.tcp_max_syn_backlog = 65535
    net.ipv4.tcp_tw_reuse = 1
    net.core.default_qdisc = fq
    net.ipv4.tcp_congestion_control = bbr
    vm.swappiness = 10
    [vm]
    transparent_hugepages = never
    [disk]
    readahead = 4096
    EOF
    # Apply the custom profile
    sudo tuned-adm profile my-web-server
    sudo tuned-adm active

    The I/O scheduler determines how disk read/write requests are ordered and merged.

    Terminal window
    # View the current I/O scheduler
    cat /sys/block/sda/queue/scheduler
    # Example output: [mq-deadline] kyber bfq none
    # Temporary change
    echo "kyber" | sudo tee /sys/block/sda/queue/scheduler
    # Permanent change (via udev rules)
    sudo tee /etc/udev/rules.d/60-io-scheduler.rules > /dev/null <<'EOF'
    # SSD: use none (noop) or mq-deadline
    ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="none"
    # HDD: use bfq
    ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"
    EOF
    SchedulerSuitable Scenario
    none (noop)NVMe SSDs, virtual machines (host already handles scheduling)
    mq-deadlineGeneral SSDs, database scenarios (guarantees deadlines)
    bfqHDDs, desktop/interactive scenarios (fair scheduling)
    kyberHigh-speed SSDs, high throughput scenarios
    Terminal window
    # View the current read-ahead value (the unit is KB, not 512-byte sectors; 2048 = 2 MiB)
    cat /sys/block/sda/queue/read_ahead_kb
    # For sequential read-intensive scenarios, increase read-ahead
    echo 2048 | sudo tee /sys/block/sda/queue/read_ahead_kb
    # For random read-intensive scenarios (e.g., databases), reduce read-ahead
    echo 128 | sudo tee /sys/block/sda/queue/read_ahead_kb

    Bind NIC interrupts to specific CPU cores to reduce context switching:

    Terminal window
    # View NIC interrupt distribution
    cat /proc/interrupts | grep eth0
    # Automatic setup (using the irqbalance service)
    sudo systemctl enable --now irqbalance
    # Or manually bind (for multi-queue NICs)
    # View NIC queues
    ls /sys/class/net/eth0/queues/
    # Installing the tuned network plugin can auto-optimize this
    Terminal window
    # Install ethtool
    sudo dnf install ethtool -y
    # View NIC information
    ethtool eth0
    # View and modify ring buffer size
    ethtool -g eth0 # View
    sudo ethtool -G eth0 rx 4096 tx 4096 # Modify
    # View and enable NIC features
    ethtool -k eth0 # View
    sudo ethtool -K eth0 tso on # Enable TCP Segmentation Offload
    sudo ethtool -K eth0 gro on # Enable Generic Receive Offload
    sudo ethtool -K eth0 gso on # Enable Generic Segmentation Offload

    High-concurrency proxy/NAT servers need a larger connection tracking table:

    Terminal window
    sudo tee /etc/sysctl.d/99-conntrack.conf > /dev/null <<'EOF'
    # Maximum connection tracking table entries
    net.netfilter.nf_conntrack_max = 1048576
    # Connection tracking hash table bucket size
    # Typically set to 1/4 of nf_conntrack_max
    net.netfilter.nf_conntrack_buckets = 262144
    # Shorten timeout for various states
    net.netfilter.nf_conntrack_tcp_timeout_established = 3600
    net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
    net.netfilter.nf_conntrack_tcp_timeout_close_wait = 15
    net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 30
    EOF
    sudo sysctl --system
    Terminal window
    # View XFS mount options
    mount | grep xfs
    # Optimize mount options (edit /etc/fstab)
    # noatime - Don't update access time, reduces writes
    # nodiratime - Don't update directory access time
    # logbufs=8 - Increase log buffer count
    # Example:
    # /dev/sda1 /data xfs defaults,noatime,nodiratime,logbufs=8 0 0
    # Remount to apply without rebooting
    sudo mount -o remount,noatime,nodiratime /data
    Terminal window
    # Optimize mount options
    # noatime - Don't update access time
    # commit=60 - Data commit interval (seconds); increasing improves performance but increases data loss risk
    # Example:
    # /dev/sdb1 /data ext4 defaults,noatime,commit=60 0 2
    sudo mount -o remount,noatime /data
    /usr/local/bin/perf_check.sh
    #!/bin/bash
    # Check current system performance parameter configuration
    echo "============================================"
    echo " Performance Parameter Check - $(date)"
    echo "============================================"
    echo ""
    echo "--- CPU ---"
    echo "Cores: $(nproc)"
    echo "Current Load: $(uptime | awk -F'load average:' '{print $2}')"
    echo "Tuned Profile: $(tuned-adm active 2>/dev/null || echo 'tuned not installed')"
    echo ""
    echo "--- Memory ---"
    free -h
    echo "swappiness: $(sysctl -n vm.swappiness)"
    echo "dirty_ratio: $(sysctl -n vm.dirty_ratio)"
    echo "dirty_background_ratio: $(sysctl -n vm.dirty_background_ratio)"
    echo "THP: $(cat /sys/kernel/mm/transparent_hugepage/enabled)"
    echo "HugePages: $(grep HugePages_Total /proc/meminfo)"
    echo ""
    echo "--- Network ---"
    echo "somaxconn: $(sysctl -n net.core.somaxconn)"
    echo "tcp_max_syn_backlog: $(sysctl -n net.ipv4.tcp_max_syn_backlog)"
    echo "tcp_congestion: $(sysctl -n net.ipv4.tcp_congestion_control)"
    echo "ip_local_port_range: $(sysctl -n net.ipv4.ip_local_port_range)"
    echo "tcp_tw_reuse: $(sysctl -n net.ipv4.tcp_tw_reuse)"
    echo ""
    echo "--- File Descriptors ---"
    echo "System limit: $(sysctl -n fs.file-max)"
    echo "Current usage: $(cat /proc/sys/fs/file-nr)"
    echo "ulimit -n (current user): $(ulimit -n)"
    echo ""
    echo "--- I/O ---"
    for disk in $(lsblk -dn -o NAME); do
    scheduler=$(cat /sys/block/$disk/queue/scheduler 2>/dev/null)
    readahead=$(cat /sys/block/$disk/queue/read_ahead_kb 2>/dev/null)
    rotational=$(cat /sys/block/$disk/queue/rotational 2>/dev/null)
    type="HDD"
    [ "$rotational" = "0" ] && type="SSD"
    echo "${disk} (${type}): scheduler=${scheduler}, readahead=${readahead}KB"
    done
    echo ""
    echo "--- Connection Tracking ---"
    if [ -f /proc/sys/net/netfilter/nf_conntrack_max ]; then
    echo "Max connections: $(sysctl -n net.netfilter.nf_conntrack_max)"
    echo "Current connections: $(cat /proc/sys/net/netfilter/nf_conntrack_count 2>/dev/null || echo N/A)"
    else
    echo "nf_conntrack module not loaded"
    fi
    Terminal window
    chmod +x /usr/local/bin/perf_check.sh
    1. Measure before optimizing — Use sar, vmstat, iostat, perf to identify the real bottleneck
    2. Change one parameter at a time — This is the only way to confirm which change produced the effect
    3. Record every change — Makes it easy to roll back and review
    4. Validate with load testing — Use tools like ab, wrk, fio, sysbench to verify optimization results
    5. Watch for side effects — Optimizing one parameter may introduce issues in other areas
    Terminal window
    # Common benchmarking tools
    sudo dnf install httpd-tools -y # Provides ab
    # ab -n 10000 -c 100 http://localhost/
    # fio disk performance test
    sudo dnf install fio -y
    fio --name=randread --ioengine=libaio --rw=randread --bs=4k \
    --size=1G --numjobs=4 --runtime=60 --group_reporting
    # sysbench CPU test
    sudo dnf install sysbench -y
    sysbench cpu --threads=$(nproc) run