Introduction
Optimizing an Ubuntu server involves several critical steps that can dramatically improve performance.
This comprehensive guide covers three essential optimization areas: implementing BBR (Bottleneck
Bandwidth and Round-trip propagation time) congestion control, optimizing file system performance
through the noatime directive, and configuring system resource limits to handle
high-traffic scenarios efficiently.
- Throughput improvements up to 2,700x with BBR congestion control
- Reduced I/O operations through file access time optimization
- Enhanced capacity for concurrent connections and open files
- Better performance for WordPress sites using Nginx, MariaDB, and PHP
1. BBR Congestion Control Algorithm
Understanding BBR
Congestion control algorithms determine how quickly data should be transmitted across a network. Traditional algorithms like Reno and Cubic rely on packet loss as their primary signal for network congestion. BBR (Bottleneck Bandwidth and Round-trip propagation time), developed by Google, takes a revolutionary approach by using latency measurements instead of packet loss to optimize data transmission rates.
Traditional Algorithms (Reno/Cubic)
Primary Signal: Packet Loss
Approach: Reactive - waits for congestion to occur
Performance: Baseline throughput
Issue: Can't distinguish between congestion and random loss
BBR Algorithm
Primary Signal: Latency/RTT
Approach: Proactive - prevents congestion
Performance: Up to 2,700x higher throughput
Advantage: Optimizes for available bandwidth
Performance Comparison
Implementation Steps
1Check Available Congestion Control Algorithms
Expected Output: net.ipv4.tcp_available_congestion_control = reno cubic bbr
2Check Currently Enabled Algorithm
Expected Output: net.ipv4.tcp_congestion_control = cubic or
reno
3Enable BBR (Four Commands Required)
# Command 1: Load BBR kernel module
sudo modprobe tcp_bbr
# Command 2: Make BBR available after reboot
echo "tcp_bbr" | sudo tee -a /etc/modules-load.d/modules.conf
# Command 3: Set BBR as the congestion control algorithm
echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf
# Command 4: Apply the settings
sudo sysctl -p
4Verify BBR is Active
Expected Output: net.ipv4.tcp_congestion_control = bbr
2. File System Optimization - Disabling Access Time Recording
Understanding the noatime Directive
By default, most Linux distributions track the last access time for every file on the system. This means that every time a file is read, the file system performs an additional I/O operation to update the access time metadata. For frequently accessed files, especially on high-traffic web servers, this creates significant unnecessary overhead.
- Eliminates unnecessary I/O operations for access time updates
- Significant performance improvement for frequently accessed files
- Reduces disk wear on SSD-based systems
- Access time information is rarely used in practice
Implementation Process
1Identify Your Storage Drive
Look for the entry mounted on / (root). The device will be listed in the first column (e.g.,
/dev/vda2).
| Filesystem | Size | Used | Avail | Use% | Mounted on |
|---|---|---|---|---|---|
| /dev/vda2 | 78G | 12G | 62G | 16% | / |
2Verify Current Mount Parameters
Example Output: /dev/vda2 / ext4 rw,relatime,errors=remount-ro 0 0
3Backup the fstab File
4Edit the fstab File
# Original line:
# /dev/vda2 / ext4 defaults 0 1
# Modified line (add noatime after defaults):
# /dev/vda2 / ext4 defaults,noatime 0 1
# Important: No spaces between defaults, comma, and noatime
# There IS a space between noatime and the following 0
- Position cursor after "defaults"
- Type a comma (no space before comma)
- Type "noatime" (no space after comma)
- Ensure there's a space before the "0"
- Save: Ctrl+O, then Enter
- Exit: Ctrl+X
5Reboot and Verify
After reboot, verify the change:
Expected Output: Should now include noatime in the options list.
3. Increasing System Resource Limits
Understanding Open File Limits
On Linux systems, sockets are treated as files. The default limit for open files per process is typically very low (1024), which severely restricts the number of concurrent connections a server can handle. For web servers handling multiple simultaneous requests, this limitation can cause connection failures and performance degradation.
Hard vs Soft Limits Architecture
| Limit Type | Description | Who Can Modify | Default Value |
|---|---|---|---|
| Hard Limit | Maximum ceiling for soft limit | Root user only | 1,048,576 |
| Soft Limit | Enforced resource limit | Users (up to hard limit) | 1,024 (too low!) |
Implementation Steps
1Check Current Limits
ulimit -Hn
Expected Output: 1048576
ulimit -Sn
Expected Output: 1024 (this is too low)
2Create Custom Limits Configuration
# Add these directives to set both hard and soft limits to 120,000
* soft nofile 120000
* hard nofile 120000
# Explanation:
# * = applies to all users
# soft = soft limit type
# hard = hard limit type
# nofile = number of open files
# 120000 = the limit value
- 120,000: Suitable for most WordPress hosting scenarios with Nginx, MariaDB, and PHP
- 200,000-250,000: High-traffic websites or multiple sites
- Up to 1,000,000: Enterprise-level applications with very high concurrent connections
3Reboot and Verify
After reboot, verify the changes:
ulimit -Sn # Should return 120000
4. Enabling PAM-based User Limits
Understanding PAM (Pluggable Authentication Modules)
PAM is a flexible framework for user authentication and session management in Linux. By enabling PAM-based limits, we ensure that non-root users can utilize the higher file descriptor limits we've configured. This is crucial for web server processes that typically run under dedicated user accounts (like www-data for Nginx).
1Navigate to PAM Configuration Directory
2Modify common-session File
Add this line at the end of the file:
session required pam_limits.so
3Modify common-session-noninteractive File
Add the same line at the end:
session required pam_limits.so
The pam_limits.so module enforces resource limits defined in
/etc/security/limits.conf and files in /etc/security/limits.d/. This
ensures that the limits apply to all user sessions, both interactive and non-interactive (like web
server processes).
4Final Reboot
5. Component-Specific Open File Limits
Web Stack Components
Nginx
Web Server
Requires high file limits for concurrent connections
MariaDB
Database Server
Needs limits for database connections and files
PHP-FPM
Application Layer
Multiple workers require adequate file descriptors
Common Error to Watch For
Cause: The open file limit for a specific service (Nginx, MariaDB, or PHP) is set too low.
Solution: Increase the limit in the service's systemd configuration or main configuration file.
Where to check: Service log files located in /var/log/
Summary & Best Practices
Optimizations Completed
- ✓ BBR Congestion Control: Up to 2,700x throughput improvement over legacy algorithms
- ✓ Noatime Directive: Reduced I/O overhead for frequently accessed files
- ✓ System Resource Limits: Increased from 1,024 to 120,000 open files
- ✓ PAM Configuration: Enabled user-level resource limit enforcement
Best Practices Checklist
| Practice | Recommendation |
|---|---|
| Backup Configuration Files | Always create backups before editing system files (especially /etc/fstab) |
| Documentation | Keep notes of all changes made for future reference and troubleshooting |
| Testing | Verify each change after reboot to ensure it persists |
| Monitoring | Watch log files for "too many open files" errors after deployment |
| Host-Specific Configuration | Consult provider documentation if using non-Vultr hosting |
Performance Impact Summary
Expected Performance Improvements
Next Steps
In the next section of the course, you will:
- Point a domain name to your server
- Install and configure Nginx web server
- Install and optimize MariaDB database
- Configure PHP-FPM for optimal performance
- Set component-specific open file limits