Files
alt-mysql/docs/superpowers/specs/2026-07-05-native-port-takeover-design.md

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-mariadb install (install_db.sh's normal use case) — not to migrating an already-running alt-mariadb instance to a new port. Native MariaDB is always already running at the time install_db.sh runs (DirectAdmin itself is already installed), so the "live service surgery" risk is entirely on the native side, never the alt-mariadb side.
  • 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 permissive detect_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=no has 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.11PORT defaults to 3306, 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 (default 3307)

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.conf updated, 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_PORT already 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-mariadb instance to a new port (not needed — this server has no alt-mariadb installed yet).
  • Any OS other than AlmaLinux 8/9.
  • A documented path back to CustomBuild-managed MySQL/MariaDB after this runs.
  • Changing /etc/services or 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/*.cnf files 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 no and its reverse are assumed to be synchronous, side-effect-free writes to options.conf with no network/build side effects; this should be confirmed empirically (e.g. by diffing options.conf before/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.