Skip to content

[Bug]: Custom MySQL/MariaDB Configuration silently ignored on non-root SSH servers (file mounted read-only, wrong owner) #11600

Description

@kleintonno

Note

Related to #1697, which was closed as "probably fixed already" without an actual code change — I hit the exact same symptom on the current main and am opening this with a full root-cause diagnosis, since #1697 is now locked and can't take new comments.

Description and Error Message

"Custom MySQL/MariaDB Configuration" is silently not applied when the server is connected to Coolify via a non-root SSH user. Boot log:

mysqld: File '/etc/mysql/conf.d/custom-config.cnf' not found (OS errno 13 - Permission denied)
mysqld: [ERROR] Stopped processing the 'includedir' directive in file /etc/my.cnf at line 32.

Because includedir aborts entirely on the first error, no custom config line is applied — not just whichever line triggered it, but the whole file, silently, with no error surfaced in the Coolify UI.

Root cause (confirmed against main@7a7564e6, 2026-09-02): app/Actions/Database/StartMysql.php (identically StartMariadb.php) bind-mounts the custom config read-only, with no ownership fixup:

// StartMysql.php:174-186
if (! is_null($this->database->mysql_conf) && ! empty($this->database->mysql_conf)) {
    $docker_compose['services'][$container_name]['volumes'] = array_merge(
        ...
        [
            [
                'type' => 'bind',
                'source' => $this->configuration_dir.'/custom-config.cnf',
                'target' => '/etc/mysql/conf.d/custom-config.cnf',
                'read_only' => true,
            ],
        ]
    );
}

The file is written host-side via tee over the SSH connection Coolify uses for that server (add_custom_mysql()). With a non-root SSH user, the file lands owned by that user's host UID/GID — not by the container's mysql user (privilege-dropped in the entrypoint, usually UID 999). Because the mount is read-only, this can't be fixed from inside the container (chmod/chown both fail with "Read-only file system").

Two follow-on traps that make this hard to debug even once you suspect the SSH user:

  • Switching the server's SSH user to root in Coolify settings does not fix an already-existing database resource: tee only overwrites file content, never ownership, on a pre-existing file. The stale custom-config.cnf/docker-compose.yml under /data/coolify/databases/<uuid>/ have to be deleted once so the next deploy re-creates them fresh under the new user.
  • Coolify's SSH multiplexing keys the mux socket only by server UUID, not by user (muxSocket() in app/Helpers/SshMultiplexingHelper.php). Saving a new SSH user does not invalidate an already-open ControlMaster connection — it has to be closed explicitly (SshMultiplexingHelper::removeMuxFile($server)) or the next deploy still runs under the old user.

Suggested fix: the exact same class of bug for SSL certificates was fixed in #11286 / #11352 (explicit chown mysql:mysql run before container start). The same pattern needs to be applied to the custom-config bind mount in StartMysql.php / StartMariadb.php.

Expected Behavior

  1. Custom MySQL/MariaDB Configuration is saved for a database resource on a non-root-SSH server.
  2. Database resource is restarted.
  3. The container boots without permission errors.
  4. SHOW VARIABLES reflects every configured value.

Steps to Reproduce

  1. Add a server to Coolify using a non-root SSH user.
  2. Create a MySQL (or MariaDB) database resource on it.
  3. Add any content under "Custom MySQL Configuration" and save.
  4. Restart the database resource.
  5. Check the container logs — see the permission error above.
  6. SHOW VARIABLES LIKE '...' for any configured value — still shows the MySQL default, not the configured one.

Coolify Version

v4.3.14

Are you using Coolify Cloud?

No (self-hosted)

Operating System and Version (self-hosted)

Ubuntu 26.04 LTS

Additional Information

Disclosure: I used AI assistance (Claude Code) to trace this through the codebase after hitting it on a real server while tuning MySQL for a production sale event. I've personally reproduced and verified every claim above against my own incident and against the current main source — including the exact file/line references and the two follow-on traps, which I hit and resolved manually before writing this up.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions