11 KiB
Design: Native MariaDB Port Takeover for alt-mariadb on Port 3306
Date: 2026-07-05 Status: Approved by user, ready for implementation planning
Goal
Let install_db.sh install alt-mariadb on the standard MySQL/MariaDB port (3306) by
first moving DirectAdmin's existing CustomBuild-managed native MariaDB off that port, so
that any tool or script that assumes the default port can reach alt-mariadb without
custom port configuration.
Background
install_db.sh currently requires alt-mariadb's port as a mandatory CLI argument
(defaulting nowhere, e.g. install_db.sh 10.11 33033), and its own validate_paths()
explicitly rejects a port that matches DirectAdmin's native engine's port. Native MariaDB
(installed and managed by CustomBuild) occupies 3306 today.
Earlier in this project's design work, a different port-3306 proposal was considered and
rejected: pointing DirectAdmin's own mysql.conf at alt-mariadb itself. That failed
because DirectAdmin's legacy-code-base license enforces a hard MariaDB-version ceiling
(10.6) at login time, independent of CustomBuild settings, and alt-mariadb intentionally
runs newer versions.
This design is different and does not hit that problem: DirectAdmin keeps talking to the
same native engine, on the same version — only the TCP port that engine listens on
changes. The freed port 3306 then goes to alt-mariadb.
The user confirmed empirically, on the actual target server, that setting
mysql_inst=no in CustomBuild's options.conf and then changing the native port in
/etc/my.cnf survives a ./build rewrite_confs run — i.e., once MySQL/MariaDB is marked
as externally-managed, CustomBuild's config-regeneration sweep no longer touches it. This
is the key fact that makes a manual, persistent port change safe here (CustomBuild's
per-service rewrite_confs logic is gated on it considering that service "managed").
Scope
- Applies only to a fresh
alt-mariadbinstall (install_db.sh's normal use case) — not to migrating an already-runningalt-mariadbinstance to a new port. Native MariaDB is always already running at the timeinstall_db.shruns (DirectAdmin itself is already installed), so the "live service surgery" risk is entirely on the native side, never thealt-mariadbside. - Runs only on AlmaLinux 8 or 9. CloudLinux, Rocky, CentOS, plain RHEL, and AlmaLinux
outside 8/9 are explicitly refused before anything is touched — a stricter, separate
check from
install_db.sh's own permissivedetect_os(), because this script touches a live, DA-critical service rather than just installing an additional one. - This is a materially one-way decision:
mysql_inst=nohas no clean, officially documented path back to CustomBuild-managed MySQL once other things start depending on the new arrangement. The design rolls back cleanly on failure during the takeover itself, but is not meant to be casually reversed afterward.
CLI change to install_db.sh
PORT becomes optional. Usage becomes install_db.sh <MARIADB_VERSION> [PORT]:
install_db.sh 10.11—PORTdefaults to3306, triggering the native takeover.install_db.sh 10.11 3306— same as above; the trigger is the resolved port value, not whether it was typed explicitly.install_db.sh 10.11 33033— explicit non-3306 port, no takeover, today's behavior is unchanged.
apply_cli_args changes from requiring exactly 2 arguments to accepting 1 or 2 (still
requires at least MARIADB_VERSION). Usage text and examples must reflect the new
optional-port form.
Architecture
A new, independent script, scripts/setup/native_mariadb_port_takeover.sh, holds all
logic for moving native MariaDB off 3306. install_db.sh calls it once, early in
main(), only when the resolved PORT is 3306 — before load_da_credentials, so that
by the time DA's credentials are read, mysql.conf already reflects the new native port
and the existing "alt port must differ from DA's port" check in validate_paths() passes
naturally with no changes needed there.
This mirrors how every other multi-step, risk-bearing operation in this plugin is
isolated into its own file (da_restore_native_staging_to_alt_mysql.sh,
alt_mysql_native_backup_restore.sh, mysql_upgrade.sh) rather than growing the already
large install_db.sh further, and keeps this logic independently testable with fake
command stubs.
Configuration
Top-of-file variables in the new script, in the same "hand-editable config block"
convention already used by install_db.sh (e.g. MARIADB_TARBALL_URL):
NATIVE_MY_CNF(default/etc/my.cnf)NATIVE_MY_CNF_D(default/etc/my.cnf.d)NATIVE_MYSQL_CONF(default/usr/local/directadmin/conf/mysql.conf)NEW_NATIVE_PORT(default3307)
Sequencing
Step 0: OS guard
Read /etc/os-release. Require ID=almalinux and VERSION_ID starting with 8 or 9.
Anything else: die() immediately, before any other action.
Step 1: Baseline check
Read DA's current native port/socket/credentials from NATIVE_MYSQL_CONF (reuse the
mysql.conf parsing convention already used elsewhere in this plugin). Confirm the native
engine is actually reachable right now using those credentials — abort before touching
anything if it isn't (this script must not appear to "fix" an unrelated pre-existing
problem). Also read the current mysql_inst= value from CustomBuild's options.conf
and record it (mariadb or mysql) — this is what rollback restores.
Step 2: Idempotency check
If the current native port (from Step 1) is already something other than 3306, log "already done" and exit 0. Nothing further to do.
Step 3: Preflight the new port
Confirm NEW_NATIVE_PORT is not already bound by anything (reuse install_db.sh's
port_is_listening pattern). Refuse before making any change if it is.
Step 4: Disable CustomBuild's MySQL management
Run da build set mysql_inst no — DirectAdmin's supported CLI, not a raw edit of
options.conf. Skip if Step 1 already found it set to no (idempotency).
Step 5: Locate, back up, and rewrite the active port= directive(s)
Scan NATIVE_MY_CNF and every NATIVE_MY_CNF_D/*.cnf for a port= line inside a
[mysqld]/[mariadb]/[server] section (later-loaded my.cnf.d files override
my.cnf's own value for the same directive — both must be checked, not just one). Back up
every file before editing it (timestamped copy, matching install_db.sh's
backup_existing_file convention). If no explicit port= line exists anywhere (implicit
default), add one to [mysqld] in NATIVE_MY_CNF. If matches exist, rewrite each to
NEW_NATIVE_PORT.
Step 6: Update mysql.conf
Rewrite only the port= field in NATIVE_MYSQL_CONF to NEW_NATIVE_PORT, via a targeted
line replacement — not full-file regeneration — so user=/passwd=/host=/socket= are
left untouched exactly as they were.
Step 7: Restart native MariaDB
Restart the native MariaDB service (reuse mysql_service_name()-style detection). Verify
it is listening on NEW_NATIVE_PORT and specifically not on 3306.
Step 8: Restart DirectAdmin
Restart the directadmin service so DA's core picks up the new port immediately, rather
than relying on an unconfirmed lazy-reload behavior. This causes a brief (seconds-long)
panel interruption — expected, not a failure.
Step 9: Verify DA's own connectivity
Confirm DA's credentials (from the now-updated mysql.conf) can connect to the native
engine on NEW_NATIVE_PORT. This is the real pass/fail gate for the whole operation.
Only if Step 9 succeeds does install_db.sh continue installing alt-mariadb, now
targeting port 3306 (already free).
Error handling and rollback
Any failure at Step 4 or later triggers rollback of everything done so far, in reverse order, before the script aborts non-zero:
- Restore every file backed up in Step 5 from its timestamped copy.
- Run
da build set mysql_inst <original_value>(the value captured in Step 1) to restore CustomBuild management. - Attempt to restart native MariaDB back on 3306; verify it's reachable again via the original credentials/port.
- If DirectAdmin was already restarted (Step 8+), restart it again so it re-picks-up the reverted config, then verify reachability.
The script logs clearly what it reverted and always exits non-zero on any rollback path.
install_db.sh must not proceed to installing alt-mariadb if this script exits non-zero,
for any reason. One exception: if Step 9 fails only because DirectAdmin itself won't
restart cleanly (unrelated to the port change), the script should say so explicitly rather
than reporting it as a takeover failure.
Testing
Matching this repo's established convention (plain bash test scripts under tests/, fake
command stubs via PATH/env-var injection, no external test framework): a new
tests/native_mariadb_port_takeover_test.sh fakes da, systemctl, and the
mysql/mariadb client binaries as stub scripts that log their invocations, plus fixture
copies of /etc/my.cnf, /etc/my.cnf.d/*.cnf, and mysql.conf under a temp directory (all
overridable via the script's own env vars, same pattern as alt_mysql_native_backup_restore.sh's
tests). Scenarios:
- Happy path: native on 3306 → ends up on 3307,
mysql.confupdated, exit 0. - Idempotency: native already on 3307 → no-op, exit 0, no file changes.
- OS guard: non-AlmaLinux or AlmaLinux outside 8/9 → refuses immediately, no files touched.
- Preflight rejection:
NEW_NATIVE_PORTalready in use → refuses before any change. - Rollback: simulate failure at each of Steps 5 through 9 in turn → confirm every backed-up
file is restored and
da build set mysql_inst <original>is called with the correct original value in each case.
Out of scope
- Migrating an already-running
alt-mariadbinstance to a new port (not needed — this server has noalt-mariadbinstalled yet). - Any OS other than AlmaLinux 8/9.
- A documented path back to CustomBuild-managed MySQL/MariaDB after this runs.
- Changing
/etc/servicesor CSF/firewall rules (native MariaDB is not intended to be remotely reachable; out of scope unless that changes).
Risks / open items for implementation planning
- The exact
[mysqld]/[mariadb]/[server]section name in use on this server's/etc/my.cnf.d/*.cnffiles should be confirmed empirically during implementation (grep the actual files) rather than assumed, since MariaDB accepts more than one section name for the same server-level directives. da build set mysql_inst noand its reverse are assumed to be synchronous, side-effect-free writes tooptions.confwith no network/build side effects; this should be confirmed empirically (e.g. by diffingoptions.confbefore/after and confirming no other files change) during the first implementation task, consistent with how this project has handled other empirically-verified-not-documented DirectAdmin behaviors.