Skip to content

MySQL

MySQL 8.0 with optional primary-replica replication, automated backups, and slow query logging.

Configuration

yaml
services:
  db:
    package: mysql
    config:
      root_password: "${MYSQL_ROOT_PASSWORD}"
      replication_password: "${MYSQL_REPL_PASSWORD}"

To add read replicas:

yaml
services:
  db:
    package: mysql
    replicas: 2
    config:
      root_password: "${MYSQL_ROOT_PASSWORD}"
      replication_password: "${MYSQL_REPL_PASSWORD}"

Parameters

ParameterRequiredDefaultDescription
root_passwordYesRoot password for the MySQL server
replication_passwordYesPassword for the replication user
replicasNo0Number of read replicas
dedicated_serverNotrueLet MySQL auto-tune memory and I/O for the full server (innodb_dedicated_server)
max_connectionsNo151Maximum concurrent connections
slow_query_logNotrueEnable the slow query log
long_query_timeNo5Queries slower than this (seconds) are logged
log_queries_not_using_indexesNotrueLog queries that don't use indexes
keep_daily_backupsNo5Number of daily full backups to retain
keep_weekly_backupsNo1Number of weekly backups to retain
keep_monthly_backupsNo1Number of monthly backups to retain

Replication

When replicas is set, managed creates a primary-replica topology:

  • Primary, handles all writes, streams binary logs to replicas
  • Replicas, read-only copies that follow the primary via GTID-based replication over TLS

Replication uses caching_sha2_password authentication and requires SSL. The primary's auto-generated CA certificate is shared with replicas through managed state.

Checking replication health

bash
managed service db check-replication

Promoting a replica

Promotion moves the whole cluster, not just one server:

bash
managed service db promote --server db-replica-1

What it does, in order:

  1. Stops writes on the current primary — nothing can be written in two places at once, which is the one failure no repair can untangle afterwards.
  2. Waits for this replica to finish applying everything it had already fetched. If it can't catch up, the promote is refused and the old primary goes back to accepting writes — a refused promote costs nothing. --arg force=yes promotes anyway and accepts losing those transactions.
  3. Opens this server for writes and takes the read-only pin out of its config. No restart, so connections that are already open aren't dropped.
  4. Re-points every other replica — and the old primary — at the new one.
  5. Records the new primary in managed's records, so *.managed.internal names, connection strings, and the dashboard all follow.

Servers that weren't answering are named in the output; they're picked up on the next apply.

Promotion is always a person's decision — managed never promotes on its own. Automatic promotion needs a quorum to decide a server is really gone rather than briefly unreachable, and getting that wrong produces two primaries.

Run managed apply afterwards. The re-pointed replicas are connected encrypted but without certificate verification (the new primary's CA isn't on their disks yet), and the demoted primary is still carrying a writable config that a reboot would honour. The apply fixes both.

If the primary is gone rather than merely busy, steps 1 and 2 are skipped, and the output says so: any writes it accepted that never reached this replica are lost.

Surviving a failed proxy too

A promoted replica is only reachable if whatever your app connects through survived as well. The ProxySQL package follows a promotion automatically (it watches each server's read-only flag), and can itself run as a pair sharing one address — see instances and virtual_ip.

Backups

Full backups run every 6 hours via mysqldump with --single-transaction, compressed with gzip and uploaded to object storage. Binary log backups run every 15 minutes for point-in-time recovery.

Retention policy

Backups follow a daily/weekly/monthly retention scheme:

  • Daily: keep the most recent backup per calendar day (default: 5 days)
  • Weekly: keep the oldest daily per ISO week (default: 1 week)
  • Monthly: keep the oldest daily per calendar month (default: 1 month)

Binary log backups older than the oldest retained full backup are automatically cleaned up.

Manual backup

bash
managed service db backup

List backups

bash
managed service db backup-list

Restore from backup

bash
managed service db restore --arg confirm=yes

Commands

CommandDescription
managed service <name> check-replicationCheck replication health and lag
managed service <name> backupCreate a full backup now
managed service <name> backup-listList available backups in storage
managed service <name> restoreRestore from the latest (or specified) backup
managed service <name> promotePromote a replica to primary

Connection

MySQL listens on port 3306, open to public by default. The easiest way in:

bash
managed repl <service-name>

It opens the mysql client with the password passed through the environment — never on a command line. To connect manually, -p (with no value) makes the client prompt for the password; don't type it into the command itself:

bash
mysql -u root -p -h <server-ip> -P 3306

How it works

The service installs MySQL 8.0 from the default Ubuntu repositories. The primary is configured with:

  • Binary logging (ROW format) and GTID for replication
  • skip-name-resolve and local-infile = 0 for security
  • innodb_dedicated_server = ON by default (auto-sizes buffer pool, redo log, flush method based on available RAM)
  • Slow query log with configurable thresholds
  • Log rotation for error and slow query logs

Where the data lives

MySQL keeps all its data in /var/lib/mysql on the server. managed installs MySQL onto whatever disk and RAID layout the server already has, it never repartitions or sets up RAID for you.

On a cloud server that's taken care of automatically. On an OVH bare-metal server you set the disk layout up yourself, once, before the first install, so your data lands on a redundant array with room to grow. Follow Storage & RAID on OVH bare metal before you run managed apply on one of those boxes.