Introduction to openSUSE Performance Tuning for Web Servers
openSUSE is a robust, enterprise-grade Linux distribution that powers many production web servers around the world. Whether you are running Apache, Nginx, or a combination of both, the default installation is optimized for general-purpose use rather than high-traffic web serving. Performance tuning transforms a stock openSUSE system into a lean, efficient web server capable of handling thousands of concurrent connections while maintaining low latency and high throughput.
This tutorial covers the essential performance tuning techniques specific to openSUSE, including kernel parameters, network stack optimization, file system configuration, web server tuning, and monitoring strategies. By the end, you will have a complete playbook for squeezing maximum performance from your openSUSE web server.
Why Performance Tuning Matters
Performance tuning is not just about raw speed. It affects every aspect of your web server's operation:
- Scalability: A tuned server can handle traffic spikes without crashing or becoming unresponsive.
- Resource efficiency: Proper tuning reduces CPU, memory, and disk I/O consumption, lowering operational costs.
- User experience: Faster response times directly translate to better user satisfaction and improved SEO rankings.
- Reliability: Tuning helps prevent resource exhaustion, connection drops, and cascading failures under load.
- Cost savings: Efficient resource usage means you can serve more traffic on the same hardware, delaying expensive upgrades.
Prerequisites
Before beginning, ensure you have the following:
- An openSUSE Leap 15.x or Tumbleweed installation
- Root or sudo access to the server
- A web server installed (Apache or Nginx)
- Basic familiarity with the command line and systemd
- A backup of your current configuration files
System-Level Kernel Tuning
The Linux kernel exposes hundreds of tunable parameters through the /proc/sys virtual file system. openSUSE uses sysctl to manage these parameters persistently. Let us start with the most impactful kernel settings for web servers.
Network Stack Optimization
The network stack is the foundation of web server performance. The following settings optimize connection handling, buffer sizes, and TCP behavior.
# Create a custom sysctl configuration file
sudo nano /etc/sysctl.d/99-webserver.conf
Add the following configuration:
# Increase the maximum number of connections the kernel will accept
net.core.somaxconn = 65535
# Increase the connection backlog queue
net.core.netdev_max_backlog = 65535
# Increase the number of available file descriptors
fs.file-max = 2097152
# Enable TCP Fast Open for reduced latency
net.ipv4.tcp_fastopen = 3
# Enable and tune TCP window scaling
net.ipv4.tcp_window_scaling = 1
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# Reduce TCP keepalive probes for faster dead connection cleanup
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
# Enable TCP SYN cookies to prevent SYN flood attacks
net.ipv4.tcp_syncookies = 1
# Increase SYN backlog
net.ipv4.tcp_max_syn_backlog = 65535
# Reduce TIME_WAIT socket reuse and recycling settings
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
# Disable ICMP redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Enable forward error correction and congestion control
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
Apply the changes immediately:
sudo sysctl -p /etc/sysctl.d/99-webserver.conf
Verify that BBR congestion control is available and active:
# Check available congestion control algorithms
sysctl net.ipv4.tcp_available_congestion_control
# Verify BBR is in use
sysctl net.ipv4.tcp_congestion_control
If BBR is not available, load the kernel module:
sudo modprobe tcp_bbr
echo "tcp_bbr" | sudo tee /etc/modules-load.d/tcp_bbr.conf
File Descriptor Limits
Web servers handle many simultaneous connections, each consuming a file descriptor. The default limits are often too low for production use.
# Edit the system limits configuration
sudo nano /etc/security/limits.conf
Add the following lines:
# Web server file descriptor limits
* soft nofile 1048576
* hard nofile 1048576
* soft nproc 65535
* hard nproc 65535
root soft nofile 1048576
root hard nofile 1048576
For systemd-managed services, you also need to configure limits in the service unit files or globally:
# Create a systemd override directory for global limits
sudo mkdir -p /etc/systemd/system.conf.d
sudo nano /etc/systemd/system.conf.d/limits.conf
[Manager]
DefaultLimitNOFILE=1048576
DefaultLimitNPROC=65535
Reload systemd and restart your web server:
sudo systemctl daemon-reexec
sudo systemctl restart nginx
# or
sudo systemctl restart apache2
Memory and Swap Tuning
Memory management is critical for web servers. Improper swap behavior can cause severe latency spikes when the system pages memory to disk.
Swappiness Configuration
The vm.swappiness parameter controls how aggressively the kernel swaps anonymous memory pages to disk. For web servers, a low value is preferred to keep application data in RAM.
# Set swappiness to a low value (default is 60)
sudo sysctl -w vm.swappiness=10
# Make it persistent
echo "vm.swappiness = 10" | sudo tee -a /etc/sysctl.d/99-webserver.conf
# Reduce vfs cache pressure to keep directory and inode cache
echo "vm.vfs_cache_pressure = 50" | sudo tee -a /etc/sysctl.d/99-webserver.conf
# Disable overcommit to prevent OOM killer from terminating web server processes
echo "vm.overcommit_memory = 1" | sudo tee -a /etc/sysctl.d/99-webserver.conf
Transparent Huge Pages
Transparent Huge Pages (THP) can cause latency issues on web servers due to defragmentation. Disabling THP or setting it to madvise is recommended.
# Check current THP status
cat /sys/kernel/mm/transparent_hugepage/enabled
# Set to madvise (applications can opt-in)
echo madvise | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
# Make it persistent using a systemd service
sudo nano /etc/systemd/system/disable-thp.service
[Unit]
Description=Disable Transparent Huge Pages
After=sysinit.target local-fs.target
[Service]
Type=oneshot
ExecStart=/bin/sh -c "echo madvise > /sys/kernel/mm/transparent_hugepage/enabled"
RemainAfterExit=yes
[Install]
WantedBy=basic.target
sudo systemctl enable disable-thp.service
sudo systemctl start disable-thp.service
Nginx Performance Tuning
Nginx is the most popular web server on openSUSE for high-performance workloads. The following optimizations target connection handling, worker processes, and caching.
Worker Process and Connection Configuration
sudo nano /etc/nginx/nginx.conf
# Set worker processes to match CPU cores
worker_processes auto;
worker_cpu_affinity auto;
# Increase worker connections
worker_rlimit_nofile 1048576;
events {
worker_connections 65535;
multi_accept on;
use epoll;
}
http {
# Enable sendfile for efficient file transfers
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Optimize keepalive connections
keepalive_timeout 30;
keepalive_requests 1000;
# Buffer sizes
client_body_buffer_size 16k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
client_max_body_size 64m;
# Connection reset timeout
reset_timedout_connection on;
# Disable unnecessary tokens
server_tokens off;
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 256;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/json
application/xml
application/xml+rss
application/xhtml+xml
application/x-javascript
image/svg+xml;
# Open file cache for static assets
open_file_cache max=10000 inactive=60s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# Include server configurations
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/vhosts.d/*.conf;
}
Upstream and Proxy Tuning
When Nginx acts as a reverse proxy, upstream connection settings significantly impact performance:
upstream backend {
server 127.0.0.1:8080;
keepalive 64;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
}
}
Static Asset Caching
server {
listen 80;
server_name static.example.com;
root /srv/www/static;
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
sendfile on;
tcp_nopush on;
}
location / {
try_files $uri $uri/ =404;
}
}
Apache Performance Tuning
Apache remains widely used on openSUSE. The key to Apache performance is selecting the right Multi-Processing Module (MPM) and tuning it properly.
Selecting the Event MPM
The Event MPM is the most efficient Apache MPM for most workloads. It uses a hybrid approach with a small number of processes and many threads per process.
# On openSUSE, enable the event MPM
sudo a2enmod mpm_event
# Disable the prefork MPM if enabled
sudo a2dismod mpm_prefork
# Enable necessary modules
sudo a2enmod headers
sudo a2enmod expires
sudo a2enmod deflate
sudo a2enmod http2
MPM Event Configuration
sudo nano /etc/apache2/mods-enabled/mpm_event.conf
<IfModule mpm_event_module>
StartServers 3
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 10000
AsyncRequestWorkerFactor 2
ListenBacklog 511
</IfModule>
The MaxConnectionsPerChild setting is particularly important. Setting it to a non-zero value prevents memory leaks from accumulating by recycling worker processes after they handle a set number of requests.
Apache Keep-Alive and Compression
sudo nano /etc/apache2/server-tuning.conf
# Keep-Alive settings
KeepAlive On
MaxKeepAliveRequests 1000
KeepAliveTimeout 5
# Compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json application/xml
DeflateCompressionLevel 6
DeflateBufferSize 8096
</IfModule>
# Cache settings
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType text/html "access plus 5 minutes"
</IfModule>
Restart Apache to apply changes:
sudo systemctl restart apache2
File System and Disk I/O Tuning
Disk I/O is often the bottleneck for web servers serving static content or writing logs. openSUSE defaults to the Btrfs or ext4 file systems, both of which can be tuned for web workloads.
Mount Options for Web Directories
Optimize mount options for directories containing web content and logs:
# Check current mount options
mount | grep /srv
# Edit fstab for persistent mount options
sudo nano /etc/fstab
Add the following options for web content partitions:
/dev/sdb1 /srv/www ext4 defaults,noatime,nodiratime,data=writeback,barrier=0 0 2
For Btrfs, use these options:
/dev/sdb1 /srv/www btrfs defaults,noatime,compress=zstd:3,space_cache=v2 0 0
The noatime option prevents the file system from updating access times on every read, significantly reducing write I/O. The compress=zstd:3 option for Btrfs provides transparent compression that reduces disk usage and can improve I/O throughput for compressible data.
I/O Scheduler Tuning
The I/O scheduler controls how disk I/O requests are ordered. For SSDs and NVMe drives, the none or mq-deadline scheduler is optimal.
# Check current scheduler
cat /sys/block/sda/queue/scheduler
# Set the scheduler to none for NVMe/SSD
echo none | sudo tee /sys/block/nvme0n1/queue/scheduler
# For SATA SSDs, use mq-deadline
echo mq-deadline | sudo tee /sys/block/sda/queue/scheduler
# Make it persistent with a udev rule
sudo nano /etc/udev/rules.d/60-io-scheduler.rules
# Set scheduler based on device type
ACTION=="add|change", KERNEL=="nvme[0-9]*n[0-9]*", ATTR{queue/scheduler}="none"
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"
sudo udevadm control --reload-rules
sudo udevadm trigger
Read-Ahead and Queue Depth
# Increase read-ahead for sequential reads (useful for log files and static assets)
sudo blockdev --setra 4096 /dev/sda
# Increase queue depth for NVMe
echo 1024 | sudo tee /sys/block/nvme0n1/queue/nr_requests
PHP-FPM Tuning for Dynamic Content
If your web server runs PHP applications, PHP-FPM configuration is critical. Poorly tuned PHP-FPM can exhaust memory and cause 502 errors.
sudo nano /etc/php8/fpm/php-fpm.conf
[global]
error_log = /var/log/php8/fpm-error.log
log_level = warning
emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 10s
daemonize = yes
Configure the pool settings:
sudo nano /etc/php8/fpm/pool.d/www.conf
[www]
user = wwwrun
group = www
listen = /run/php-fpm/www.sock
listen.owner = wwwrun
listen.group = www
listen.mode = 0660
# Use dynamic process management
pm = dynamic
pm.max_children = 120
pm.start_servers = 12
pm.min_spare_servers = 6
pm.max_spare_servers = 18
pm.max_requests = 1000
# Slow log for debugging
slowlog = /var/log/php8/slow.log
request_slowlog_timeout = 5s
# Status page for monitoring
pm.status_path = /fpm-status
ping.path = /fpm-ping
Calculate the optimal pm.max_children value based on available memory:
# Calculate based on average PHP process memory usage
# Formula: max_children = (Total RAM - RAM for other services) / Average PHP process size
# Check average memory usage of PHP-FPM processes
ps -ylC php-fpm --sort:rss | awk '{sum+=$8; count++} END {print "Average memory per process: " sum/count/1024 " MB"}'
Also tune the PHP php.ini for performance:
sudo nano /etc/php8/cli/php.ini
; OPcache settings
opcache.enable = 1
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 32
opcache.max_accelerated_files = 20000
opcache.revalidate_freq = 60
opcache.fast_shutdown = 1
opcache.enable_cli = 0
; Realpath cache
realpath_cache_size = 4096k
realpath_cache_ttl = 600
; Memory and execution limits
memory_limit = 256M
max_execution_time = 30
max_input_time = 60
sudo systemctl restart php-fpm
Database Connection Optimization
Web servers often connect to databases. Connection pooling and caching reduce overhead significantly.
Redis as a Session Handler
# Install Redis on openSUSE
sudo zypper install redis
# Enable and start Redis
sudo systemctl enable --now redis
# Configure PHP to use Redis for sessions
sudo nano /etc/php8/fpm/php.ini
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6373?auth=yourpassword&timeout=2&database=1"
session.gc_maxlifetime = 1440
session.cookie_lifetime = 0
MySQL/MariaDB Connection Tuning
sudo nano /etc/my.cnf.d/server.cnf
[mysqld]
# Connection settings
max_connections = 500
max_user_connections = 400
wait_timeout = 60
interactive_timeout = 60
# Buffer pool (set to 70-80% of available RAM for dedicated DB servers)
innodb_buffer_pool_size = 2G
innodb_buffer_pool_instances = 4
innodb_log_file_size = 512M
innodb_log_buffer_size = 64M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
# Query cache (if using MySQL 5.7 or MariaDB)
query_cache_type = 0
query_cache_size = 0
# Temporary tables
tmp_table_size = 64M
max_heap_table_size = 64M
# Connection pooling
thread_cache_size = 100
table_open_cache = 4000
open_files_limit = 65535
Firewall and Security Tuning
openSUSE uses firewalld by default. While security is essential, overly restrictive or poorly configured firewalls can impact performance.
Optimizing firewalld for Web Traffic
# Check firewalld status
sudo firewall-cmd --state
# Add HTTP and HTTPS services permanently
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
# Reload firewall
sudo firewall-cmd --reload
# Enable connection tracking optimization
sudo nano /etc/modprobe.d/conntrack.conf
# Increase connection tracking table size
options nf_conntrack hashsize=262144
# Apply the new conntrack settings
sudo sysctl -w net.netfilter.nf_conntrack_max=1048576
echo "net.netfilter.nf_conntrack_max = 1048576" | sudo tee -a /etc/sysctl.d/99-webserver.conf
Rate Limiting with Nginx
Application-level rate limiting protects your server from abuse and ensures fair resource distribution:
# Define rate limiting zones in http block
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
server {
listen 80;
server_name example.com;
# Apply connection limiting
limit_conn conn_per_ip 20;
location / {
limit_req zone=general burst=20 nodelay;
proxy_pass http://backend;
}
location /api/ {
limit_req zone=api burst=10 nodelay;
proxy_pass http://backend;
}
}
Monitoring and Benchmarking
Tuning without measurement is guesswork. Establish baselines and continuously monitor performance to validate your changes.
System Monitoring Tools
Install essential monitoring tools on openSUSE:
sudo zypper install htop iotop sysstat nethogs perf
Enable sysstat for historical performance data:
sudo systemctl enable --now sysstat
Use sar to collect and view performance statistics:
# View CPU usage
sar -u 1 10
# View memory usage
sar -r 1 10
# View network statistics
sar -n DEV 1 10
# View disk I/O
sar -d 1 10
Benchmarking with Apache Bench
# Install Apache Bench
sudo zypper install apache2-utils
# Basic benchmark
ab -n 10000 -c 100 http://localhost/
# Benchmark with keep-alive
ab -n 10000 -c 100 -k http://localhost/
# Benchmark with custom headers
ab -n 10000 -c 100 -H "Accept-Encoding: gzip,deflate" http://localhost/
Benchmarking with wrk
For more advanced benchmarking, wrk provides detailed latency distribution:
# Install wrk from source
cd /tmp
git clone https://github.com/wg/wrk.git
cd wrk
make
sudo cp wrk /usr/local/bin/
# Run a 30-second benchmark with 100 connections and 10 threads
wrk -t10 -c100 -d30s http://localhost/
# Run with a Lua script for POST requests
wrk -t10 -c100 -d30s -s post.lua http://localhost/api
Example Lua script for POST testing:
-- post.lua
wrk.method = "POST"
wrk.body = '{"key":"value"}'
wrk.headers["Content-Type"] = "application/json"
Real-Time Monitoring with Prometheus and Grafana
For production servers, set up continuous monitoring:
# Install Prometheus node exporter
sudo zypper install golang-github-prometheus-node_exporter
sudo systemctl enable --now prometheus-node_exporter
# Install Grafana (add repository first)
sudo zypper ar https://packages.grafana.com/oss/rpm/grafana.repo
sudo zypper refresh
sudo zypper install grafana
sudo systemctl enable --now grafana-server
Best Practices
- Change one parameter at a time: Making multiple changes simultaneously makes it impossible to identify which change caused an improvement or regression.
- Always benchmark before and after: Use tools like
wrkorabto establish baselines and measure the impact of each change. - Test in staging first: Never apply tuning changes directly to production. Validate all changes in a staging environment that mirrors production.
- Document all changes: Maintain a changelog of all tuning modifications with timestamps, rationale, and measured impact.
- Monitor continuously: Performance characteristics change over time as traffic patterns evolve. Set up monitoring dashboards and alerts.
- Keep backups: Always back up configuration files before modifying them. Use version control for your configuration management.
- Use systemd timers instead of cron: systemd timers offer better integration, logging, and resource control for scheduled tasks.
- Enable journal persistence: Ensure systemd journal is persistent for post-incident analysis.
- Regularly update the system: Kernel and package updates often include performance improvements and security fixes.
- Profile before optimizing: Use
perf,strace, andflamegraphsto identify actual bottlenecks rather than guessing.
Enabling Persistent Journal Logging
sudo nano /etc/systemd/journald.conf
[Journal]
Storage=persistent
Compress=yes
SystemMaxUse=500M
SystemKeepFree=1G
MaxFileSec=1month
sudo systemctl restart systemd-journald
Automating Tuning with Configuration Management
For servers that are rebuilt frequently or managed at scale, automate your tuning configuration using Ansible:
---
- name: Tune openSUSE web server
hosts: webservers
become: yes
tasks:
- name: Deploy sysctl configuration
copy:
src: files/99-webserver.conf
dest: /etc/sysctl.d/99-webserver.conf
notify: apply sysctl
- name: Deploy limits configuration
copy:
src: files/limits.conf
dest: /etc/security/limits.conf
- name: Deploy systemd limits
copy:
src: files/systemd-limits.conf
dest: /etc/systemd/system.conf.d/limits.conf
notify: reload systemd
- name: Deploy THP service
copy:
src: files/disable-thp.service
dest: /etc/systemd/system/disable-thp.service
notify: enable thp service
- name: Deploy nginx configuration
copy:
src: files/nginx.conf
dest: /etc/nginx/nginx.conf
notify: restart nginx
handlers:
- name: apply sysctl
command: sysctl -p /etc/sysctl.d/99-webserver.conf
- name: reload systemd
command: systemctl daemon-reexec
- name: enable thp service
systemd:
name: disable-thp
enabled: yes
state: started
daemon_reload: yes
- name: restart nginx
service:
name: nginx
state: restarted
Troubleshooting Common Issues
Too Many Open Files
If you see "Too many open files" errors, check and increase file descriptor limits:
# Check current limits for the web server process
cat /proc/$(pgrep -o nginx)/limits | grep "Max open files"
# Check system-wide file descriptor usage
cat /proc/sys/fs/file-nr
Connection Refused Under Load
This often indicates the backlog queue is full or the application cannot accept connections fast enough:
# Check current listen backlog
ss -lnt | grep :80
# Check for dropped connections
netstat -s | grep -i "listen"
High Memory Usage
# Check memory usage by process
ps aux --sort=-%mem | head -20
# Check for memory leaks
valgrind --leak-check=full /usr/sbin/nginx -g "daemon off;"
Slow Response Times
# Check disk I/O wait
iostat -x 1 10
# Check network latency
mtr -n example.com
# Profile the application
perf record -g -p $(pgrep -o nginx) -- sleep 30
perf report
Conclusion
Performance tuning an openSUSE web server is a systematic process that spans multiple layers of the system stack, from kernel parameters and network configuration to web server settings and application-level optimizations. The techniques covered in this tutorial provide a comprehensive foundation, but remember that tuning is an iterative process. Every workload is unique, and the optimal configuration depends on your specific traffic patterns, hardware, and application requirements. Start with the kernel and network stack optimizations, then move to web server and application tuning, always measuring the impact of each change with proper benchmarking tools. Establish continuous monitoring to catch regressions early, and use configuration management to ensure your tuning is reproducible across all your servers. With careful application of these techniques, your openSUSE web server will be well-equipped to handle high traffic loads efficiently, reliably, and cost-effectively.