Bali, Indonesia
Insights

Editing my.cnf Doesn't Mean MySQL or MariaDB Loaded Your New Setting

October 26, 2026
0:00
A lot of people assume that if they edit my.cnf and restart MySQL or MariaDB, the new value is now live. The assumption holds on a fresh test box with one config file. It breaks on production servers that have been through panel updates, tuning scripts, and package upgrades. On those servers, the file you edited is usually one of several the database reads, and it is not always the last one. The result looks confusing from the outside. The file clearly says max_connections = 500, the service restarted without errors, and the site still throws too-many-connections errors at the old limit. Nothing is broken. The server simply loaded a different value from somewhere else.
MySQL and MariaDB do not read one config file. They read a list of option files in a fixed order. Any of those files can pull in more files with !include (a single file) or !includedir (every .cnf file in a directory). When the same option appears more than once, the last value read wins. The server does not warn you about the duplicate. It just keeps overwriting the value as it reads. The layout depends on the distro and the package. On RHEL-family systems you usually find /etc/my.cnf with an !includedir /etc/my.cnf.d line at the bottom. On Debian and Ubuntu, /etc/mysql/my.cnf is often a small file that includes /etc/mysql/conf.d/ plus either mysql.conf.d/ or mariadb.conf.d/. The real settings live in files like 50-server.cnf or mysqld.cnf. Numeric prefixes are there because include directories are typically processed in sorted order, so a file named 60-tuning.cnf can quietly undo whatever 50-server.cnf says. Check the documentation for your exact version rather than assuming the order. The second layer is option groups, the section headers in square brackets. The server reads options from groups such as [mysqld], [server], and on MariaDB [mariadb], plus some version-specific groups. Client tools read [client] and [mysql]. A server setting placed under [client] or [mysql] is invisible to the server. A very common example is max_allowed_packet added under [mysql]. That only changes the command-line client, not the server limit discussed in MySQL Server Has Gone Away During Import.
The config files describe what you asked for. The running server tells you what you got. When someone reports that a change did not work, the first thing worth checking is the live value. It takes seconds and settles the question before anyone starts editing files again.
SQL
-- What the running server is actually using
SHOW GLOBAL VARIABLES LIKE 'max_connections';
SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_size';

-- Seconds since the server started; a large number means your restart never happened
SHOW GLOBAL STATUS LIKE 'Uptime';

-- MySQL 8.0+ only: where the current value came from
SELECT VARIABLE_NAME, VARIABLE_SOURCE, VARIABLE_PATH
FROM performance_schema.variables_info
WHERE VARIABLE_NAME = 'max_connections';
The Uptime check matters more than it looks. If it shows days, the service never restarted. Maybe you restarted a different unit name, or the restart failed and an old process kept running. On MySQL 8, variables_info goes further and tells you whether the value came from a config file, the command line, a persisted setting, or the compiled default. It also shows the file path when there is one. MariaDB does not have this table, so there you rely on the file checks below.
You do not have to guess the file list. The server binary can print it, along with the merged options it would start with.
BASH
# Option files this binary reads, in order
mysqld --verbose --help 2>/dev/null | grep -A 1 'Default options'

# The merged options from those files (must be the first argument)
mysqld --print-defaults

# Find every place a setting is defined, including include directories
grep -rn 'max_connections' /etc/my.cnf /etc/my.cnf.d /etc/mysql 2>/dev/null
On newer MariaDB releases the binary may be mariadbd, so use that name if mysqld is missing. Either way, these commands are read-only and safe on a live server. If the binary is not in your PATH, it usually lives under /usr/sbin/. The --print-defaults output lists options in the order they were read, so duplicates show up side by side. The last occurrence is the one that wins.
TEXT
Illustrative output format:
/usr/sbin/mysqld would have been started with the following arguments:
--user=mysql --datadir=/var/lib/mysql --max_connections=500 --max_connections=150
In that example, someone set 500 in one file, and a file read later set it back to 150. The grep from the previous block tells you which file that was. Compare its path against the order printed by --verbose --help and the picture is complete. One limitation: --print-defaults only shows option files. It does not show flags added by the service manager or values persisted at runtime. Those are covered next.
When the files look right and the live value is still wrong, the override usually comes from one of these sources. Persisted runtime settings. On MySQL 8, SET PERSIST writes values to mysqld-auto.cnf in the data directory. That file is read after all the regular option files, so it beats anything in /etc. Someone may have run SET PERSIST max_connections = 150 months ago and forgotten about it. MariaDB has no equivalent. There, a plain SET GLOBAL changes the value only until the next restart. That is a different trap: the change works until a reboot, and then quietly reverts because nobody added it to a file. Command-line options. Options passed on the command line override option files. A systemd unit, a drop-in override, or an environment variable used by the unit can add flags without touching any .cnf file. systemctl cat followed by your service name shows the full unit, including drop-ins. Look for extra options on the ExecStart line. Limits outside the database. Some values get adjusted at startup. If the OS or systemd caps open files, the effective open_files_limit can be lower than requested. That can indirectly reduce how many connections the server accepts. innodb_buffer_pool_size may be rounded to fit internal chunk sizes, so the live number can differ slightly from what you typed. The error log usually mentions these adjustments, so read it after a restart that did not behave as expected. The wrong server. On hosts running more than one instance, a container, or a separate database VPS, the file you edited may belong to a server that does not handle the site's traffic. Running SELECT @@hostname, @@port, @@datadir; from the application's connection confirms which instance you are actually talking to. Typos are a smaller category. Depending on the version, an unknown option name in [mysqld] can stop the server from starting, which is at least obvious. With the loose- prefix, the server ignores it with a warning in the log, which is easy to miss.
On production servers, it helps to stop editing whichever file you find first. Put your own overrides in one dedicated file inside the include directory the server already reads. Give it a name that sorts after the distro and panel files, such as 99-local-overrides.cnf. Put every setting under [mysqld], and add a comment with the reason and the date of each change. Before relying on it, confirm with --verbose --help and --print-defaults that the file is read and comes last. Some control panels manage /etc/my.cnf themselves and may rewrite it during updates, so a separate file also protects your changes from that. For dynamic variables, the clean workflow on MariaDB is to apply the change with SET GLOBAL and add the same line to your override file. Then the running value and the value after a restart match. On MySQL 8, choose deliberately between the file and SET PERSIST. Using both for the same variable is how people end up chasing a ghost value. If you inherit a server, check mysqld-auto.cnf early so you know what is already persisted. The habit that ties this together is small: after every config change, run SHOW GLOBAL VARIABLES for the setting you changed and SHOW GLOBAL STATUS LIKE 'Uptime' to confirm the restart. If the live value matches, you are done. If it does not, --print-defaults, a quick grep across the include directories, and a look at the systemd unit will point to the file or flag that won. The config file is a request. The live variable is the answer.