Configuring Ulimits for MySQL Large Pages in Systemd/Init Services: Persistent Solutions Beyond limits.conf


3 views

When dealing with memory-intensive applications like MySQL configured to use large pages, we often hit a wall with traditional limits.conf approaches. The fundamental issue lies in how PAM (Pluggable Authentication Modules) initializes - system services started during boot don't inherit these limits because they're not spawned through login shells.

The /etc/security/limits.conf file requires pam_limits.so to be loaded during session initialization. For systemd services (or traditional SysV init scripts), this simply doesn't happen during the early boot phase. This explains why your MySQL service might ignore the memlock limits needed for large pages.

For modern systems using systemd, we have cleaner approaches than modifying init scripts:


# Method 1: Directly in the service unit file
[Service]
LimitMEMLOCK=infinity

# Method 2: Using drop-in files (recommended)
# Create /etc/systemd/system/mysql.service.d/limits.conf
[Service]
LimitMEMLOCK=infinity

After creating/modifying these files, run:

systemctl daemon-reload
systemctl restart mysql

For systems still using SysV init scripts, you have several options:


# Option 1: Modify the init script directly (not recommended)
# Add this near the start of the start() function:
ulimit -l unlimited

# Option 2: Create a wrapper script
#!/bin/bash
ulimit -l unlimited
exec /usr/sbin/mysqld "$@"

Since you're managing servers with Chef, here's how to implement this properly without overriding RPM-managed files:


# For systemd systems
directory '/etc/systemd/system/mysql.service.d' do
  owner 'root'
  group 'root'
  mode '0755'
  action :create
end

file '/etc/systemd/system/mysql.service.d/limits.conf' do
  content "[Service]\nLimitMEMLOCK=infinity\n"
  owner 'root'
  group 'root'
  mode '0644'
  notifies :run, 'execute[systemctl-daemon-reload]', :immediately
end

execute 'systemctl-daemon-reload' do
  command 'systemctl daemon-reload'
  action :nothing
end

After implementation, verify your settings:


# Check effective limits
cat /proc/$(pgrep mysqld)/limits | grep locked

# Alternative method for systemd
systemctl show mysql | grep LimitMEMLOCK

If you encounter issues, check:

  • Systemd version (needs to be >= 219 for some limit features)
  • AppArmor/SELinux constraints
  • Whether your system actually supports large pages

When enabling MySQL large pages support, we often need to set ulimit -l for locked memory. While /etc/security/limits.conf works for shell sessions, it doesn't affect system services during boot because:

  • PAM (pluggable authentication modules) only processes limits for login sessions
  • Systemd services ignore traditional Unix limits configuration
  • RPM-managed init scripts shouldn't be directly modified

The most maintainable solution is creating a systemd drop-in file without modifying the original service unit:


# Create directory if not exists
sudo mkdir -p /etc/systemd/system/mysql.service.d/

# Create override file
sudo tee /etc/systemd/system/mysql.service.d/override.conf << 'EOF'
[Service]
LimitMEMLOCK=infinity
EOF

# Reload systemd and restart MySQL
sudo systemctl daemon-reload
sudo systemctl restart mysql

After implementation, verify the settings with these commands:


# Check applied limits
systemctl show mysql | grep LimitMEMLOCK

# Verify from MySQL process
cat /proc/$(pgrep mysqld)/limits | grep locked

For infrastructure-as-code environments like Chef, implement this as:


directory '/etc/systemd/system/mysql.service.d' do
  owner 'root'
  group 'root'
  mode '0755'
  action :create
end

file '/etc/systemd/system/mysql.service.d/override.conf' do
  content <<-EOF
    [Service]
    LimitMEMLOCK=infinity
  EOF
  notifies :run, 'execute[systemctl-daemon-reload]', :immediately
end

execute 'systemctl-daemon-reload' do
  command 'systemctl daemon-reload'
  action :nothing
end

service 'mysql' do
  action [:restart]
end

For non-systemd systems or special cases:


# Option 1: SysV init wrapper
cat > /etc/init.d/mysql-wrapper << 'EOF'
#!/bin/sh
ulimit -l unlimited
/etc/init.d/mysql-original "$@"
EOF

# Option 2: Profile.d script
echo "ulimit -l unlimited" > /etc/profile.d/mysql_limits.sh