# \[RESOLVED\] Should ansible.mysql be split into separate MySQL and MariaDB collections?

**URL:** <https://forum.ansible.com/t/resolved-should-ansible-mysql-be-split-into-separate-mysql-and-mariadb-collections/45866>\
**Category:** Project Discussions\
**Tags:** resolved, dev, mysql, mariadb, collections\
**Created:** [June 1, 2026, 8:40am UTC](https://forum.ansible.com/t/resolved-should-ansible-mysql-be-split-into-separate-mysql-and-mariadb-collections/45866 "2026-06-01T08:40:46Z")\
**Posts on this page:** 8\
**Page:** 2

<div class="post-metadata">

**Author:** ![jgoossens](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/jgoossens/32/7122_2.png) [@jgoossens](https://forum.ansible.com/u/jgoossens)\
**Post date:** [October 16, 2026, 10:17pm UTC](https://forum.ansible.com/t/resolved-should-ansible-mysql-be-split-into-separate-mysql-and-mariadb-collections/45866/21 "2026-10-16T22:17:35Z")

</div>

Hi @Andersson007 and all,

First of all: I understand the motivation, and thanks for all the work you’ve put into this collection. Anyone who has looked at the `engine × version` test matrix or the conditional branches in the code can see why a split looks attractive. I don’t want to downplay that pain.

Still, I’d like to ask you to reconsider the plan, or at least narrow it. From the user side it moves a lot of that complexity onto people who never chose to deal with it.

**The biggest issue: a large share of “MySQL” users are running MariaDB without ever having chosen it.**

Debian is the clearest example. Debian 13 (trixie) doesn’t ship a `mysql-server` package at all. `default-mysql-server`, the package you get when you “install MySQL” on Debian, depends on `mariadb-server-compat`. The `mysql` command, the `mysql.service` unit and the `/etc/mysql` paths are all MariaDB underneath.

The same goes for the Ansible ecosystem. `geerlingguy.mysql`, one of the most widely used MySQL roles on Galaxy, installs `default-mysql-server` on Debian 12 and 13, which means MariaDB. It manages databases and users with plain `mysql_db:` and `mysql_user:` tasks, which resolve to this collection. Everyone who uses that role on current Debian is a MariaDB user of `ansible.mysql` today, whether they know it or not.

For these users the split means:

- a deprecation warning on runs where nothing changed on their side, about an engine they believe they don’t use;
- a hard break the day `ansible.mysql` 6.0.0 reaches the Ansible package, often inside roles they didn’t write and can’t easily change;
- and the fix isn’t “change one FQCN”. They first have to find out which engine they’re actually running, on every host, and then branch on it.

@noonedeadpunk raised exactly this (“as an operator you usually don’t know what you’re dealing with as a backend”), and gotmax23 called it a very good point, but it hasn’t been answered yet. In my view it’s the most important argument in this thread.

Some context on where we stand: we run a hosting platform that deploys and manages several hundred MySQL (Percona) database servers with Ansible. We’re now adding MariaDB next to it, including migrating customers between the two engines in both directions.

Three more points:

1. **Ansible can’t template a module name.** A role that supports both engines can’t write `"{{ engine }}_user":`. Every task that creates a database or a user needs two copies, or a per-engine include file that exists only to switch the FQCN. That is permanent duplication in every role that supports both engines, not a one-time rename.

2. **Most of the code really is shared.** Connection handling, `mysql_db`, `mysql_query`, `mysql_info`, `mysql_variables` and the basics of `mysql_user` behave the same on both engines. The clone only had to drop three modules, which says a lot. After the split, every fix in that shared part has to be found and backported by hand between two collections, indefinitely. That’s the “double the work, maintainers spread thinner” cost felixfontein and bvitnik warned about. Forks drift, and users end up with a fix in one collection but not the other.

3. **The differences are real, but contained.** Replication, roles and auth plugins differ, and the collection already handles that in `implementations/mysql` and `implementations/mariadb`. Splitting doesn’t remove those differences. It moves the branching out of one codebase and into every playbook and role that touches a database.

Alternatives that would ease the maintenance pain without hitting these users:

- Keep one collection and shrink the test matrix instead, e.g. test MariaDB only on its current LTS releases (11.8 and 12.3).
- If the split stays: keep the engine-neutral modules (`mysql_db`, `mysql_user`, `mysql_query`, `mysql_info`, `mysql_variables`) supporting MariaDB in `ansible.mysql` without a removal date, and move only the parts that really diverge (replication, roles) to `ansible.mariadb`. Or share one common `module_utils`, so a fix lands once.
- Add the engine-detection / engine-agnostic entry point noonedeadpunk suggested, so roles don’t have to branch on every task.
- At minimum, hold back the warning until the removal is actually close. Then contact the authors of the most-used roles that install MariaDB through distro defaults, before anything breaks.

I realise the vote is done and the work has started, so I don’t say this lightly. I just think the split looks cheaper from inside the collection than it will be for the many people who don’t even know they’re on MariaDB.

Thanks for considering it.

---

<div class="post-metadata">

**Author:** ![bvitnik](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bvitnik/32/4132_2.png) [@bvitnik](https://forum.ansible.com/u/bvitnik)\
**Post date:** [October 16, 2026, 11:30pm UTC](https://forum.ansible.com/t/resolved-should-ansible-mysql-be-split-into-separate-mysql-and-mariadb-collections/45866/22 "2026-10-16T23:30:35Z")

</div>

@jgoossens Very well said mister. As someone that worked in the cloud hosting business and managed database servers (MySQL, Percona XtraDB, MariaDB, Postgres…) with Ansible throughout most of my career… I can really feel your pain 🥲.

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [October 17, 2026, 4:32pm UTC](https://forum.ansible.com/t/resolved-should-ansible-mysql-be-split-into-separate-mysql-and-mariadb-collections/45866/23 "2026-10-17T16:32:02Z")

</div>

> [@jgoossens](#):
>
> 1. **Ansible can’t template a module name.** A role that supports both engines can’t write `"{{ engine }}_user":`.

Actually you can do that, though it’s not really documented. I found an example in the ansible-core 2.16 porting guide where a specific previously allowed syntax got deprecated ([Ansible-core 2.16 Porting Guide — Ansible Core Documentation](https://docs.ansible.com/projects/ansible-core/devel/porting_guides/porting_guide_core_2.16.html#using-a-mapping-for-action)). What’s allowed (before 2.16 and after it) is:

```auto
- name: Print a message
  action: "ansible.builtin.debug"
  args:
    msg: "Hello World!"

```

Here the module name `ansible.builtin.debug` is a string that can be templated.

It’s something I’d avoid if possible, but it might make your live simpler in some cases.

---

<div class="post-metadata">

**Author:** ![bvitnik](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bvitnik/32/4132_2.png) [@bvitnik](https://forum.ansible.com/u/bvitnik)\
**Post date:** [October 18, 2026, 10:39pm UTC](https://forum.ansible.com/t/resolved-should-ansible-mysql-be-split-into-separate-mysql-and-mariadb-collections/45866/24 "2026-10-18T22:39:36Z")

</div>

@felixfontein Hmmm, not really. I’ve used that pattern extensively in my code… until it broke in Ansible 2.14. It has a limitation. `action:` value can be templated but Ansible must be able to resolve the value during the initial playbook parsing. In other words, value must be static. If value is computed dynamically during the run, it will not work.

I found this in my code:

```auto
- name: "Running handler"
  # This stopped working in ansible-core 2.14 so as a workaround we hardcode
  # action to be "shell". Keep this in mind when defining "args".
  # For more details, see:
  # - https://github.com/ansible/ansible/pull/77955
  # - https://github.com/ansible/ansible/issues/80374
  # action: "{{ item['item']['handler']['action'] }}"
  action: "ansible.builtin.shell"
  args: "{{ item['item']['handler']['args'] }}"
  loop: "{{ changes['results'] | selectattr('item.handler', 'defined') | selectattr('changed') | list }}"
  loop_control:
    label: "{{ item['item']['handler']['name'] }}"
  listen: "handler"

```

Because the value of `action:` here is based on the elements of a list, which is dynamically assembled, Ansible fails to resolve the value complaining that variable referenced in Jinja template (item) cannot be found.

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [October 19, 2026, 4:37am UTC](https://forum.ansible.com/t/resolved-should-ansible-mysql-be-split-into-separate-mysql-and-mariadb-collections/45866/25 "2026-10-19T04:37:42Z")

</div>

Ah, that’s quite possible. I’ve never used this pattern myself, only seen it in other roles/playbook (I think molecule was using it at some point).

---

<div class="post-metadata">

**Author:** ![jgoossens](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/jgoossens/32/7122_2.png) [@jgoossens](https://forum.ansible.com/u/jgoossens)\
**Post date:** [October 19, 2026, 6:54am UTC](https://forum.ansible.com/t/resolved-should-ansible-mysql-be-split-into-separate-mysql-and-mariadb-collections/45866/26 "2026-10-19T06:54:38Z")

</div>

Thanks @felixfontein for the tip, and thanks bvitnik for the kind words and for checking it against real code.

I think the `action:` question is a bit off topic, but in a way it shows the point. Supporting both engines in one role would need an undocumented workaround that even you would rather avoid. It also apparently stops working when the value is only known at runtime, and that’s exactly the case here: the engine is something you detect on the host. That’s a lot of complexity to ask every role author to carry.

Ansible was built to make server administration easier. For many people “MySQL” is simply the database their distro installs, and today `mysql_user` just works for them, whether it’s MySQL or MariaDB underneath. It would be great if it stayed that way.

@Andersson007, would you be open to one of the narrower options? For example: keep the engine-neutral modules (`mysql_db`, `mysql_user`, `mysql_query`, `mysql_info`, `mysql_variables`) working with MariaDB in `ansible.mysql`, and only move what really diverges. Happy to think along.

---

<div class="post-metadata">

**Author:** ![eRadical](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/eradical/32/10800_2.png) [@eRadical](https://forum.ansible.com/u/eRadical)\
**Post date:** [October 24, 2026, 12:08pm UTC](https://forum.ansible.com/t/resolved-should-ansible-mysql-be-split-into-separate-mysql-and-mariadb-collections/45866/27 "2026-10-24T12:08:01Z")

</div>

Hi @jgoossens,

Thanks for sharing your thoughts and for raising these valid points! You make a great case regarding user convenience and how nice it feels when a module “just works” out of the box regardless of whether MySQL or MariaDB is installed on the host.

However, the core reason for moving toward separate collections is that MySQL and MariaDB have already diverged significantly under the hood and I strongly believe that the gap will only continue to widen over time.

Even for seemingly engine-neutral modules like `mysql_user`, `mysql_db`, or `mysql_variables`, the underlying differences in authentication plugins, privilege grant syntax, default configuration structures, reserved keywords, and Python client libraries mean that maintaining dual compatibility requires a constantly growing set of conditionals and workarounds.

Attempting to keep core modules dual-compatible creates a few long-term challenges:

- **Maintenance overhead & subtle bugs:** Hiding engine differences inside shared modules leads to edge-case bugs that are hard to test and maintain across both ecosystems.

- **Feature friction:** It limits our ability to leverage engine-specific features or support new syntax introduced in newer versions of MySQL or MariaDB without risking regressions on the other.

- **Future divergence:** As both database projects continue their independent development roadmaps, the divergence will accelerate, making shared code increasingly fragile.

Splitting into dedicated collections allows each project to evolve cleanly, focus on native features, and provide predictable, robust modules for users of each engine.

While we completely recognize that this transition requires role authors to adjust how they handle engine detection, we believe clean separation is the most sustainable path forward for the long-term health and quality of both collections.

* * *

I personally handle this in my roles by testing the server flavour once in a main task file and then include engine-specific files:

```auto
- include_tasks: do-rds-mariadb-users.yml
   when: db_flavour == 'rds-mariadb'
- include_tasks: do-mariadb-users.yml
   when: db_flavour == 'mariadb'

- include_tasks: do-mysql-users.yml
   when: db_flavour == 'mysql'
- include_tasks: do-rds-mysql-users.yml
   when: db_flavour == 'rds-mysql'

```

(I manage a mix of MySQL and MariaDB servers in EC2 and/or in RDS… though we’re phasing away the MySQL ones…)

---

<div class="post-metadata">

**Author:** ![kks](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/kks/32/10352_2.png) [@kks](https://forum.ansible.com/u/kks)\
**Post date:** [October 24, 2026, 5:55pm UTC](https://forum.ansible.com/t/resolved-should-ansible-mysql-be-split-into-separate-mysql-and-mariadb-collections/45866/28 "2026-10-24T17:55:28Z")

</div>

> [@eRadical](#):
>
> ```auto
> - include_tasks: do-rds-mariadb-users.yml
> when: db_flavour == 'rds-mariadb'
> - include_tasks: do-mariadb-users.yml
> when: db_flavour == 'mariadb'
> 
> - include_tasks: do-mysql-users.yml
> when: db_flavour == 'mysql'
> - include_tasks: do-rds-mysql-users.yml
> when: db_flavour == 'rds-mysql'
> 
> ```

is equvivalent to

```yaml
- include_tasks: {{ 'do-' ~ db_flavour ~'-users.yml' }}

```

This is much more extendable pattern as it does not require changing task file when adding another db flavour.

Same trick with import tasks will not work, because imports should be resolved at parse time, not runtime.

```yaml
- import_tasks: {{ 'do-' ~ db_flavour ~'-users.yml' }} # This DOES NOT work

```

[Previous page](https://forum.ansible.com/t/resolved-should-ansible-mysql-be-split-into-separate-mysql-and-mariadb-collections/45866.md?page=1)
