Editing MariaDB configuration files is easy; proving which value the server actually uses is harder. A Linux package can load several .cnf files, each file can contain groups read by different programs, included directories are processed in order, systemd may add command-line arguments, and an administrator may have changed a dynamic variable after startup. Looking at one my.cnf therefore does not establish the running configuration.
This guide explains option-file discovery, precedence, groups, includes, package differences, command-line overrides, environment variables, global and session variables, and restart behavior. It also provides a production workflow to inventory, change, validate, deploy, troubleshoot, and roll back MariaDB configuration without relying on guesswork.
Examples use Ubuntu 24.04 and MariaDB 10.11-style package paths where appropriate. MariaDB Foundation/Corporation packages and Ubuntu distribution packages are not identical, and other Linux families use different layouts. Always ask the installed binary which files and groups it reads before editing anything.
Use the right vocabulary
MariaDB configuration has several related concepts:
- Option: a startup or client-program argument, such as
--port=3306. - Option file: an INI-like file such as
/etc/mysql/mariadb.conf.d/50-server.cnf. - Option group: a section such as
[mariadb],[server], or[client]. - System variable: a server value visible through SQL, such as
max_connections. - Status variable: an operational counter or state, not usually a configuration input.
- Global value: the server-level system-variable value, often inherited by new sessions.
- Session value: a connection-specific value that may differ from the global value.
- Command-line option: an argument supplied by systemd, a container entrypoint, or an operator.
- Environment variable: process environment used by MariaDB programs, generally lower precedence than option files and command-line arguments.
Many server options correspond to system variables, but not every option is dynamic or even visible in the same way. Documentation for the exact variable states its scope, type, default, valid range, and whether it is dynamic.
Understand the precedence model
A practical model is:
built-in or environment default
-> option files in discovered order
-> later duplicate option
-> command-line argument
-> runtime SET for a dynamic system variable
-> per-session SET where a session scope exists
This model needs two qualifications.
First, MariaDB does not stop after finding the first normal option file. It can read multiple files and includes in sequence. When the same option is specified more than once, a later setting generally overrides an earlier one. The exact search list is platform and build dependent.
Second, runtime SET GLOBAL changes do not rewrite option files and do not normally survive restart. A session value can also hide a global change inside an existing connection. Always compare intended startup configuration, running global state, and a new session.
Do not memorize a universal /etc/my.cnf order. Ask each installed program.
Discover files read by the installed binary
Start with version and package source:
mariadbd --version
mariadb --version
apt-cache policy mariadb-server
dpkg-query -W -f='${Package} ${Version}n' 'mariadb-*' 2>/dev/null
Print MariaDB Server's default option-file search information:
mariadbd --help --verbose 2>/dev/null |
sed -n '/Default options/,/Variables and options/p'
Near the beginning of the output, MariaDB reports files read in order and groups recognized by that binary. Preserve this output in a change record because it reflects the installed build.
Inspect the filesystem without assuming all files are active:
sudo find /etc/mysql -maxdepth 3
( -type f -o -type l )
-printf '%M %u %g %p -> %ln' | sort
On Debian and Ubuntu, /etc/mysql/my.cnf may be managed through update-alternatives and link to another file. Resolve the chain:
readlink -f /etc/mysql/my.cnf
namei -l /etc/mysql/my.cnf
update-alternatives --display my.cnf 2>/dev/null || true
Do not replace a managed symlink with a regular file. Package upgrades and coexistence with MySQL tooling can depend on that structure.
Read include directives in context
An option file can include one file:
!include /etc/mysql/custom/network.cnf
It can include a directory:
!includedir /etc/mysql/mariadb.conf.d/
Files in an included directory are read in alphabetical order according to MariaDB's option-file rules and accepted extensions. This is why distribution files often carry numeric prefixes and a custom override may be named z-custom.cnf.
Find includes and group headers:
sudo rg -n '^[[:space:]]*[!?]include|^[[:space:]]*[' /etc/mysql
The optional ?includedir directive was introduced only in newer maintenance releases, including MariaDB 11.4.10 and 11.8.6. It skips unreadable files rather than failing like !includedir. Do not use it on an Ubuntu 24.04 MariaDB 10.11 installation; the older parser does not support that feature.
An included directory is not automatically “later than everything.” Its position is wherever the parent file contains the directive. Trace the actual graph and filename order.
Choose the correct option group
Putting a valid option under the wrong group can make it appear ignored. Important server groups include:
| Group | Typical readers |
|---|---|
[client-server] |
MariaDB clients and server; useful for common socket or port |
[server] |
MariaDB Server |
[mysqld] |
MariaDB and MySQL server binaries |
[mariadb] |
MariaDB Server only |
[mariadbd] |
MariaDB Server |
[mariadb-X.Y] |
Specific MariaDB major.minor branch |
[galera] |
MariaDB Server builds with Galera support |
Client groups include:
| Group | Typical readers |
|---|---|
[client] |
MariaDB and MySQL-compatible client programs |
[client-mariadb] |
MariaDB client programs |
[mariadb] in client context |
The mariadb command-line client where documented |
[mariadb-backup] |
mariadb-backup |
[mariadb-dump] |
Dump utility where supported by that program |
Ask a program which groups it reads:
mariadbd --help --verbose 2>/dev/null | head -80
mariadb --help | sed -n '1,100p'
mariadb-backup --help 2>/dev/null | head -100
Use [mariadb] or [server] for MariaDB-specific server settings. Use [client-server] sparingly for truly shared values. Do not put a server-only option such as innodb_buffer_pool_size under [client].
Understand option-file syntax
A simple custom server file might be:
[mariadb]
# Human-readable suffixes use powers of 1024.
max_connections = 300
innodb_buffer_pool_size = 8G
slow_query_log = ON
long_query_time = 1.5
Inside option files:
- Omit the leading command-line
--. - Dashes and underscores in option names are interchangeable.
- Lines beginning with
#are comments. - Blank lines are ignored.
- The same group can appear more than once.
- Quote values when spaces or special characters require it.
- Recognized escape sequences can change literal backslashes.
- Boolean options may support prefixes such as
skipordisable.
Prefer one consistent spelling, normally underscores for values also queried as system variables. Avoid double negatives such as skip_skip_name_resolve. For booleans, explicit ON or OFF is easier to review than relying on presence-only behavior unless documentation specifies otherwise.
Do not place inline comments after values without confirming parser behavior. Use a separate comment line.
Keep package files and custom policy separate
Do not edit /etc/mysql/mariadb.conf.d/50-server.cnf merely because it contains the setting you found. Package upgrades may replace, merge, or prompt about managed files, and later custom changes become hard to distinguish.
Create a late custom file:
sudoedit /etc/mysql/mariadb.conf.d/z-custom.cnf
Example:
[mariadb]
max_connections = 300
skip_name_resolve = ON
Use configuration management to own that file, its permissions, review history, and deployment. Split files by responsibility only when it improves ownership:
z-10-network.cnf
z-20-security.cnf
z-30-innodb.cnf
z-40-logging.cnf
Alphabetical prefixes make intended precedence explicit. Too many fragments can still become difficult to reason about, so maintain a generated inventory and avoid defining the same option in several custom files.
Inspect effective startup arguments
my_print_defaults prints options from selected groups. On MariaDB versions supporting it, this is convenient:
my_print_defaults --mariadbd
For broad compatibility, list the relevant groups explicitly:
my_print_defaults
server mysqld mariadb mariadbd galera
The output can contain duplicate options in read order. The later occurrence typically wins. It may also expose secrets from client groups, so do not paste unredacted output into tickets or chat.
The server binary can print its option-derived arguments:
mariadbd --print-defaults
--print-defaults, --no-defaults, --defaults-file, and --defaults-extra-file generally must appear first when used with MariaDB programs. This is correct:
mariadb --defaults-file=/etc/mariadb-audit/client.cnf
--execute='SELECT 1;'
This may not be parsed as intended:
# Do not put a defaults selector after ordinary options.
mariadb --user=audit
--defaults-file=/etc/mariadb-audit/client.cnf
--defaults-file tells a program to read only that file and fails if it does not exist. --defaults-extra-file adds another file at its documented position. Test exact behavior on the installed version and program; my_print_defaults option support itself changed in later releases.
Inspect systemd command-line overrides
Option files are not the final startup source when systemd adds arguments or environment. Inspect the service definition and drop-ins:
systemctl cat mariadb
systemctl show mariadb
-p FragmentPath
-p DropInPaths
-p ExecStart
-p Environment
-p EnvironmentFiles
Do not edit files under /usr/lib/systemd/system or /lib/systemd/system directly. Package upgrades own them. Use an approved drop-in only when a setting genuinely belongs at the service-manager layer:
sudo systemctl edit mariadb
After changing a unit or drop-in:
sudo systemctl daemon-reload
sudo systemctl restart mariadb
Most MariaDB variables belong in MariaDB option files, where operators and database tooling expect them. Systemd drop-ins are appropriate for service limits, environment integration, or a controlled nonstandard launch, but they can obscure precedence if used as a second configuration system.
Inspect the actual process command line without assuming ps includes every source:
pid=$(systemctl show -p MainPID --value mariadb)
sudo tr '' 'n' < "/proc/$pid/cmdline"
Do not expose secret-bearing arguments. Credentials should never be supplied on a server command line.
Compare intended configuration with running state
After startup, SQL is authoritative for the current system-variable value:
SHOW GLOBAL VARIABLES WHERE Variable_name IN
('max_connections','innodb_buffer_pool_size',
'skip_name_resolve','slow_query_log','long_query_time');
Information Schema provides richer metadata on supported releases:
SELECT VARIABLE_NAME, GLOBAL_VALUE, SESSION_VALUE,
VARIABLE_SCOPE, READ_ONLY
FROM information_schema.SYSTEM_VARIABLES
WHERE VARIABLE_NAME IN
('MAX_CONNECTIONS','INNODB_BUFFER_POOL_SIZE',
'SKIP_NAME_RESOLVE','SLOW_QUERY_LOG','LONG_QUERY_TIME')
ORDER BY VARIABLE_NAME;
Column availability can vary by release. Inspect the table definition before building automation:
DESCRIBE information_schema.SYSTEM_VARIABLES;
For session-scoped variables, compare global and current session:
SELECT @@GLOBAL.long_query_time,
@@SESSION.long_query_time;
A SET GLOBAL long_query_time = 2 affects the default for new connections, not necessarily existing ones. Open a new connection to test application behavior.
Distinguish dynamic and static changes
MariaDB documents each system variable as dynamic or non-dynamic. A dynamic variable can be tested at runtime:
SET GLOBAL max_connections = 300;
That change normally lasts until restart. Persist the approved value in an option file separately:
[mariadb]
max_connections = 300
A non-dynamic variable requires a server restart. Attempting SET GLOBAL should fail rather than silently persist.
Runtime testing is useful, but it creates drift when operators forget the file. Use a change record containing:
- Previous runtime value.
- Test runtime value.
- Exact configuration-file change.
- Restart requirement.
- New-session verification.
- Rollback value and file revision.
MariaDB's SET statement does not make a change permanent. Do not assume MySQL-specific SET PERSIST workflows apply to MariaDB.
Plan a safe configuration change
Use this workflow for production:
- Capture version, package, file order, groups, includes, systemd unit, and current SQL values.
- Identify whether the variable is dynamic and its valid range.
- Model resource impact and dependencies.
- Change one owned custom file through
sudoeditor configuration management. - Check rendered defaults and duplicate definitions.
- Test on a staging server with matching package and workload.
- Arrange backups, monitoring, console access, and rollback.
- Apply dynamically or restart during an approved window.
- Verify service state, logs, running values, new sessions, and workload behavior.
- Observe for a defined period before closing the change.
Before editing, save evidence without making an unmanaged backup full of secrets:
sudo cp --preserve=all
/etc/mysql/mariadb.conf.d/z-custom.cnf
/var/backups/mariadb-config/z-custom.cnf.pre-change
Ensure the backup directory exists with restricted permissions and is governed by retention. Prefer configuration version control and deployment artifacts over accumulating ad hoc .bak files inside an included directory. A filename like z-custom.cnf.bak may still be parsed on some platforms if its extension remains accepted.
Validate before restart
First inspect what the parser sees:
my_print_defaults server mysqld mariadb mariadbd galera
mariadbd --print-defaults
Search duplicates in the active configuration tree:
sudo rg -n
'^[[:space:]]*(max[-_]connections|innodb[-_]buffer[-_]pool[-_]size)[[:space:]]*='
/etc/mysql
Use a server configuration-validation option only if the installed release documents it. Do not start a second mariadbd against the production data directory as a syntax test; it can contend for files and cause damage.
Check storage and memory before resource changes:
free -h
df -hT
systemctl show mariadb -p MemoryMax -p LimitNOFILE
max_connections, per-session buffers, caches, logs, and InnoDB settings interact. A syntactically valid configuration can still exhaust memory or file descriptors.
Keep a local administrative socket session or out-of-band console available. If MariaDB fails after restart, inspect logs before changing multiple settings:
sudo systemctl --no-pager --full status mariadb
sudo journalctl -u mariadb -b --no-pager
Verify after deployment
Confirm service and process:
sudo systemctl is-active mariadb
sudo systemctl --no-pager --full status mariadb
sudo journalctl -u mariadb --since '-15 minutes' --no-pager
Query exact variables:
SELECT VERSION(), @@hostname, @@port;
SHOW GLOBAL VARIABLES LIKE 'max_connections';
SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW GLOBAL VARIABLES LIKE 'skip_name_resolve';
Open a new application-style connection and verify session defaults plus a safe query. Monitor memory, connection errors, latency, disk activity, replication, backup, and application health. Configuration correctness is behavioral, not only syntactic.
Record both desired file value and observed runtime value. If they differ, do not edit more files randomly; trace precedence.
Troubleshoot ignored or surprising settings
MariaDB ignores an option
Check spelling, group, file inclusion, accepted extension, and program:
mariadbd --help --verbose 2>/dev/null | head -100
my_print_defaults server mysqld mariadb mariadbd
sudo rg -n 'option_name_here' /etc/mysql
The setting may be under [client], in a directory never included, or overridden later.
Unknown variable prevents startup
Inspect the first relevant journal error and server version:
sudo journalctl -u mariadb -b --no-pager
mariadbd --version
An option may belong to MySQL, a different MariaDB branch, a plugin not loaded, or a newer release. Remove or correct the unsupported option using the prepared rollback; do not add loose unknown-option prefixes to hide every error.
File value differs from SHOW VARIABLES
Check later duplicates, systemd ExecStart, runtime SET GLOBAL, and whether the variable transformed or capped the requested value. Some sizes are rounded internally. Compare after a restart that actually used the edited file.
Global value changed but application did not
Existing sessions can retain old session values. Inspect the connection pool, compare @@GLOBAL and @@SESSION, then recycle connections through a controlled deployment.
Restart reverted a working runtime change
The SET GLOBAL value was not added to an option file, or another later file overrides it. Capture the desired value in the owned custom file and test restart persistence.
Editing /etc/mysql/my.cnf has no effect
It may be a symlink containing only include directives, the option group may be wrong, or a later included file wins. Resolve the symlink and trace includes rather than replacing it.
Client reads an unexpected password or socket
Inspect client groups and user home files:
mariadb --print-defaults
my_print_defaults client client-server client-mariadb mariadb
ls -la ~/.my.cnf
Output can expose passwords. Redact before storage. Check root's home separately when commands run through sudo; different users read different per-user files.
Container configuration changes disappear
The file may exist only in a writable container layer and vanish on replacement. Inspect image entrypoint arguments, mounted ConfigMaps or Secrets, chart values, and StatefulSet templates. Treat the workload specification as the desired state, not an interactive edit inside a pod.
Manage configuration in containers and Kubernetes
Container images often pass options directly:
args:
- "--max-connections=300"
- "--skip-name-resolve"
Those command-line values override option-file values. Inspect the actual pod:
kubectl -n data get statefulset mariadb -o yaml
kubectl -n data get pod mariadb-0
-o jsonpath='{.spec.containers[0].command}{"n"}{.spec.containers[0].args}{"n"}'
A ConfigMap-mounted .cnf must use a path the image includes and a group the server reads. Verify inside the running pod without exposing secrets:
kubectl -n data exec mariadb-0 -- mariadbd --print-defaults
kubectl -n data exec mariadb-0 --
mariadb -NBe "SHOW GLOBAL VARIABLES LIKE 'max_connections'"
ConfigMap updates may not trigger a pod restart, and subPath mounts commonly do not receive live file updates. Even if the file changes, static variables require restart. Roll the StatefulSet through the platform's controlled process and verify one replica at a time where topology permits.
Do not put database passwords in a ConfigMap. Use Secrets with restricted RBAC and a secret-management lifecycle. Also avoid printing a complete --print-defaults output when client credentials may be included.
Back up configuration as part of recovery
A data backup without server configuration can lengthen recovery. Preserve:
- Owned MariaDB option files and include structure.
- Systemd drop-ins and service limits.
- Package/repository version evidence.
- TLS certificate paths and a separate secure key recovery process.
- Plugin package and load configuration.
- Firewall, storage mount, and kernel tuning dependencies.
- Configuration-management commit or release identifier.
Do not mix secrets into a broadly accessible configuration archive. Test restoration on an isolated host and confirm mariadbd --print-defaults plus SQL variables match the intended baseline.
Production best practices
- Ask the installed program which files and groups it reads.
- Keep package-owned files unchanged and place policy in owned late-loading files.
- Define each custom option once whenever possible.
- Use MariaDB-specific server groups for MariaDB-only options.
- Trace includes, symlinks, systemd arguments, and container args.
- Compare parsed defaults with running global and session values.
- Treat
SET GLOBALas temporary unless the option file is also updated. - Version-gate variables, directives, and validation commands.
- Validate resource impact, not only syntax.
- Test new sessions after global changes.
- Keep config changes in reviewed configuration management.
- Prepare rollback and console access before restart.
- Verify restart persistence and real workload behavior.
- Protect option files and diagnostic output that may contain secrets.
FAQ
Where is the MariaDB configuration file on Ubuntu?
Usually under /etc/mysql, with server fragments in /etc/mysql/mariadb.conf.d/. Ask mariadbd --help --verbose because package source and installed build determine the exact search order.
Does MariaDB read only the first my.cnf it finds?
No. Under normal discovery it can read multiple option files and included directories in order. Later duplicate settings generally override earlier ones.
Which group should MariaDB Server settings use?
[mariadb] or [server] are clear choices. [mysqld] provides MySQL compatibility. Confirm the groups shown by the installed server binary.
Does SET GLOBAL persist after restart?
Normally no. Add the approved value to an option file and verify after restart. Existing sessions may also retain their previous session value.
Why is a valid setting ignored?
It may be in the wrong group, outside the include graph, use an unaccepted extension, be overridden later, or belong to another version or plugin.
Should I edit 50-server.cnf directly?
Prefer a separately owned late-loading custom file. Package files can change during upgrades and make local policy difficult to audit.
Can I use ?includedir on MariaDB 10.11?
No. That optional directive arrived in newer 11.4 and 11.8 maintenance releases. Use supported include syntax for the installed version.
How do I prove the active value?
Inspect parsed defaults, systemd or container command-line arguments, and SHOW GLOBAL VARIABLES; then open a new session to confirm session defaults and behavior.
Conclusion
Reliable management of MariaDB configuration files depends on evidence at every layer. Discover the installed binary's file order and groups, trace includes and symlinks, keep custom settings in an owned late-loading file, and inspect service-manager or container arguments that can override it.
After deployment, compare the desired file value with MariaDB's running global value and a new session. Version-gated validation, resource checks, monitored restarts, and a prepared rollback turn configuration from a collection of .cnf fragments into a reproducible production system.
Suggested Internal Links
- Install MariaDB on Ubuntu 24.04: Production Setup Guide
- MariaDB 12.3 vs 11.8 and 10.11: An LTS Version Guide
- Secure a New MariaDB Server: Production Hardening Guide
- Configure MariaDB Remote Access Without Exposing It
- Size the InnoDB Buffer Pool for MariaDB Workloads
- Tune MariaDB Connections, Threads, and Timeouts