> ## Documentation Index
> Fetch the complete documentation index at: https://www.towbar.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# MariaDB stack

> CMS Service + mariadb Datastore; share a network and use an app user.

export const UseCaseNavigation = () => {
  useLayoutEffect(() => {
    const storageKey = "towbar-use-case-open-groups";
    const groupSelector = "#sidebar li[data-title] > button[aria-expanded]";
    let restoring = false;
    let frame = 0;
    function storedGroups() {
      try {
        return new Set(JSON.parse(sessionStorage.getItem(storageKey) || "[]"));
      } catch {
        return new Set();
      }
    }
    function save(groups) {
      try {
        sessionStorage.setItem(storageKey, JSON.stringify([...groups]));
      } catch {}
    }
    function groupName(button) {
      return button.parentElement?.dataset.title;
    }
    function rememberOpenGroups() {
      const groups = new Set();
      for (const button of document.querySelectorAll(groupSelector)) {
        if (button.getAttribute("aria-expanded") === "true") {
          const name = groupName(button);
          if (name) groups.add(name);
        }
      }
      save(groups);
    }
    function restoreOpenGroups() {
      frame = 0;
      const groups = storedGroups();
      if (!groups.size) return;
      restoring = true;
      for (const button of document.querySelectorAll(groupSelector)) {
        if (groups.has(groupName(button)) && button.getAttribute("aria-expanded") === "false") {
          button.click();
        }
      }
      restoring = false;
    }
    function scheduleRestore() {
      if (!frame) frame = requestAnimationFrame(restoreOpenGroups);
    }
    function onClick(event) {
      if (restoring || !(event.target instanceof Element)) return;
      const button = event.target.closest(groupSelector);
      if (button) {
        const name = groupName(button);
        if (!name) return;
        const groups = storedGroups();
        if (button.getAttribute("aria-expanded") === "true") groups.delete(name); else groups.add(name);
        save(groups);
        return;
      }
      if (event.target.closest('a[href^="/docs/use-cases/"]')) {
        rememberOpenGroups();
      }
    }
    const observer = new MutationObserver(scheduleRestore);
    document.addEventListener("click", onClick, true);
    observer.observe(document.getElementById("sidebar") || document.body, {
      childList: true,
      subtree: true
    });
    scheduleRestore();
    return () => {
      document.removeEventListener("click", onClick, true);
      observer.disconnect();
      cancelAnimationFrame(frame);
    };
  }, []);
  return null;
};

<UseCaseNavigation />

<img className="towbar-doc-brand-logo" src="https://mintcdn.com/avgeek/1QStfcSBIWWsf0OD/assets/database-logos/mariadb.webp?fit=max&auto=format&n=1QStfcSBIWWsf0OD&q=85&s=90c9bdb9c85c83fd7b7642ee549f5a91" alt="MariaDB stack logo" aria-hidden="true" width="770" height="512" data-path="assets/database-logos/mariadb.webp" />

**Related reading:** [MariaDB](/docs/databases/mariadb).

These are separate files in one repository. A shared Docker network works only when the workloads run on the same server; Towbar does not automatically order their deployments or copy secret values between them.

## Towbar manifests

<CodeGroup>
  ```yaml title=".towbar/datastores/cms-with-mariadb-mariadb.datastore.yml" theme={"system"}
  id: cms-with-mariadb-mariadb
  name: MariaDB stack MariaDB
  type: mariadb
  container:
    network: application
    networkAlias: cms-with-mariadb-mariadb
    resources:
      cpus: 1
      memory: 1g
  secrets:
    runtime:
      - MYSQL_ROOT_PASSWORD
      - MYSQL_DATABASE
      - MYSQL_USER
      - MYSQL_PASSWORD
  environments:
    production:
      server: 192.0.2.10
  ```

  ```yaml title=".towbar/services/cms-with-mariadb-api.service.yml" theme={"system"}
  id: cms-with-mariadb-api
  name: MariaDB stack
  deployment:
    type: dockerfile
    context: .
    dockerfile: Dockerfile
  container:
    port: 3000
    network: application
  health:
    path: /health
  environments:
    production:
      server: 192.0.2.10
  secrets:
    runtime:
      - DATABASE_URL
  ```
</CodeGroup>

## Configure

1. Register and prepare the example server or servers, connect the repository, and map `production` to the branch containing these files.
2. Save each Datastore credential value under **Datastore → Settings → Secrets**, deploy it first, and verify engine readiness before starting a dependent Service.
3. Save the Service's declared keys in its selected environment, or enable and authorize the selected [external-secret provider](/docs/secrets/external). Do not commit the values.
4. Point each Service connection secret at `cms-with-mariadb-mariadb` on the database engine's private port. The server and network must match; credentials are saved separately for each Service.
5. Create an application-specific user with only the required privileges after the database is ready. Save its connection URL on the consuming Service instead of reusing the Datastore's administrator credentials.
6. Sync the repository, inspect the resolved configuration, deploy manually, and perform the verification below before enabling automation.

## Verify

The CMS release supports the managed MariaDB version.

For field constraints, see [Service manifest](/docs/services/manifest) and [Datastore manifest](/docs/datastores/manifest).
