This is the multi-page printable view of this section. .
Module: MYSQL
- 1: Configuration
- 2: Parameters
- 3: Administration
- 4: Playbook
- 5: Monitoring
- 6: Metrics
- 7: FAQ
MySQL is a widely used open-source relational database. Pigsty’s MYSQL module installs a fixed native MySQL 8.4 LTS platform on managed nodes. Choose a standalone instance or three-node single-primary InnoDB Cluster with Group Replication. The module manages TLS, backups, monitoring, and lifecycle.
MYSQL is a supplementary pilot module for a simple, low-cost MySQL cluster. Its scope is smaller than PGSQL. Deployment, convergence, HA failover, daily backups, monitoring, and alerts have systematic tests. Destructive complete-outage recovery and physical restore remain manual. Follow the Administration procedures.
Module Capabilities
The MYSQL module currently provides:
- Fixed native MySQL 8.4 LTS server, client, Shell, Router, and matching XtraBackup packages.
- Standalone or three-node single-primary InnoDB Cluster, created and reconciled through MySQL Shell AdminAPI.
- Router on each HA member, with topology-aware read/write
6446and read-only6447endpoints. - TLS with leaf certificates from the shared Pigsty CA. Non-TLS connections are rejected.
- Additive
mysql_databasesandmysql_usersdeclarations that never delete data implicitly. mysql_parametersoverrides, includingmax_connections, with rolling restarts after configuration changes.- Daily full physical XtraBackup backup and prepare, retention, concurrency locks, and atomic completion.
- mysqld_exporter metrics, 68 recording rules, 27 alerts, five Grafana dashboards, and VictoriaLogs error logs.
- Default
sql_require_primary_key, blocking tables without primary keys that would break MGR replication and recovery. - Convergent repair of dropped members and AdminAPI drift by rerunning
mysql.yml. Safeguards restrict destructive paths.
Module Architecture
NODE supplies node management, repositories, and the shared CA. INFRA supplies VictoriaMetrics, VictoriaLogs, Grafana, and Alertmanager. MYSQL depends on both, but not ETCD or PGSQL.
flowchart LR
admin["Pigsty admin node"] -->|"mysql.yml"| mysqld["mysqld ×3 / single-primary MGR<br>3306 · TLS"]
client["Application clients"] -->|"RW 6446 / RO 6447"| router["MySQL Router<br>(on every HA member)"]
router --> mysqld
mysqld --> backup["XtraBackup daily full<br>(current primary only)"]
mysqld --> exporter["mysqld_exporter :9104"]
mysqld --> journal["Error log → Journald"]
exporter --> vm["VictoriaMetrics"]
journal --> vector["Vector"] --> vl["VictoriaLogs"]
vm --> grafana["Grafana"]
vl --> grafana
vm --> alertmanager["Alertmanager"]
style mysqld fill:#4479A1,stroke:#33618a,color:#fff
style router fill:#70C1B3,stroke:#4f968b,color:#fff
style vm fill:#E66B7A,stroke:#b84e5c,color:#fff
style vl fill:#C98367,stroke:#9e634e,color:#fffIn three-node clusters, mysql_seq=1 coordinates bootstrap only. Runtime PRIMARY is elected. Reruns never force it back to node 1.
Components and Ports
| Component | Purpose | Fixed endpoint |
|---|---|---|
mysqld | Standalone server or MGR member | Classic 3306, X Protocol 33060 |
| Group Replication | Three-member replication and consensus (XCOM) | 33061 |
| MySQL Router | Topology-aware entry point on every HA member | RW 6446, RO 6447 |
| MySQL Shell | AdminAPI cluster lifecycle | Local control plane |
| XtraBackup | Daily full physical backup | Local backup repository |
mysqld_exporter | Server and MGR metrics | 9104 |
The role creates and manages three platform identities:
dbuser_cluster@'%': TLS-only AdminAPI and Router bootstrap identity (created on HA clusters only);dbuser_monitor@'127.0.0.1': least-privilege exporter identity;dbuser_backup@'localhost': local XtraBackup identity.
Supported Platforms
The native-package platform gate admits:
| Arch | Supported systems |
|---|---|
x86_64 | EL 8/9/10, Debian 12/13, Ubuntu 22/24 |
aarch64 | EL 9/10 |
Preflight rejects Debian/Ubuntu ARM64 because Oracle APT has no MySQL 8.4 arm64 packages. On ARM, use EL 9/10, such as Rocky Linux.
Scope and Boundaries
MYSQL provides a fixed platform. Before adoption, accept these scope limits:
- One or three nodes only: No in-place 1→3 or 3→5 expansion and no persistent two-node operation. Use logical migration for capacity changes and same-address replacement for hardware refresh.
- Fixed versions, ports, directories, and charset: No parameters change them. Memory sizing comes from node specifications. Override individual keys through
mysql_parameters. - Daily local full backups: No incremental chains, continuous binlog archives, or PITR. Physical restore is manual; follow the procedure.
- Manual complete-outage recovery: This avoids split-brain from automated guesses. Playbook failures print recovery instructions.
- No VIP, DNS, or HAProxy access layer: Connect through any member’s Router, preferably with a multi-host DSN.
Documentation
| Page | Content |
|---|---|
| Configuration | Topology planning, identity, databases, users, parameter overrides, backup settings |
| Parameters | The 11 public parameters and fixed platform conventions |
| Administration | Status checks, client access, config changes, failure handling, and three recovery runbooks |
| Playbook | mysql.yml and mysql-rm.yml usage, tags, and guardrails |
| Monitoring | Dashboards, recording rules, alert rules, log queries |
| Metrics | Label model and the derived-metric dictionary |
| FAQ | Platform limits, primary-key policy, recovery, troubleshooting |
Quick Start
Declare a cluster in the inventory (full template: conf/demo/mysql.yml):
After NODE provisioning, deploy:
Then open the Grafana MySQL Overview dashboard to inspect the cluster.
1 - Configuration
Declare the cluster in inventory. mysql.yml adjusts live state to match. This page covers topology and configuration blocks. See Parameters for the full reference.
Before You Deploy
- Manage nodes with
NODE. Install the shared CA at/etc/pki/ca.crtthroughnode_ca. MYSQL issues leaf certificates only. - Include
mysqlin repositories withnode_repo_modules: node,infra,mysql, or cache packages locally withrepo_extra_packages: [mysql]. - Use supported platforms:
x86_64EL 8/9/10, Debian 12/13, Ubuntu 22/24; oraarch64EL 9/10. - Set production
mysql_root_password,mysql_monitor_password, andmysql_cluster_password. Preflight rejectsCHANGE_MEplaceholders.
Identity
Each cluster is an inventory group with two required identity parameters:
| Parameter | Level | Description |
|---|---|---|
mysql_cluster | Cluster | Cluster name; must match the inventory group holding the members. Also the backup directory and the cls monitoring label |
mysql_seq | Instance | 1 for standalone; sequential 1..3 for HA; doubles as server_id |
One member selects standalone; three select InnoDB Cluster. Preflight rejects other counts. mysql_seq=1 coordinates bootstrap only; it does not set runtime PRIMARY.
Instances use {{ mysql_cluster }}-{{ mysql_seq }}, such as my-test-1. Inventory IP or resolvable hostname becomes the advertised MySQL/MGR address. Ordinary reruns cannot change it.
Standalone Instance
The minimal standalone declaration:
Standalone has no Router or 6446/6447 endpoints. Connect to 3306. TLS, monitoring, and backups otherwise behave as in HA.
Three-Node InnoDB Cluster
This creates one writable PRIMARY and two read-only SECONDARY members. It tolerates one node failure. Every member runs Router; any 6446 reaches the current primary.
Every mysql.yml run must select all members with -l, or omit it to select all MySQL clusters.
Preflight rejects partial selection to prevent topology divergence.
Databases
mysql_databases declares databases additively:
| Field | Default | Description |
|---|---|---|
name | required | Database name, [A-Za-z0-9_$-]; system schema names are rejected |
encoding | utf8mb4 | Character set |
collate | utf8mb4_0900_ai_ci | Collation |
Each entry accepts only these three fields; preflight validation rejects additional keys.
Convergence is additive. Reruns create missing databases. Removing an entry never drops a database; deletion is manual.
Default sql_require_primary_key=ON rejects tables without primary keys with ERROR 3750. Such tables become read-only under MGR and block AdminAPI disaster recovery.
Give every table a primary key, including an invisible-column key if needed. Override through mysql_parameters only if necessary.
Users
mysql_users declares users and grants additively:
Use 'db.table' grant scopes. Either part can be *, as in '*.*' and 'app.*'. Values are comma-separated privileges. Preflight rejects malformed users, hosts, scopes, or privilege names.
Semantics:
- Create missing users. Update existing passwords and connection limits.
- Apply
privgrants. Removing mappings does not REVOKE privileges. - Do not declare platform identities:
root,dbuser_monitor,dbuser_cluster, ordbuser_backup. - The server requires TLS.
PREFERREDnegotiates encryption;DISABLEDplaintext connections fail. Prefer explicitVERIFY_CA.
Parameter Overrides
mysql_parameters appends [mysqld] overrides at the end of managed configuration. The last value takes precedence:
Rules and safety:
- Start option names with a letter;
._-characters are permitted. Values must be single-line scalars. mysqld --validate-configchecks the rendered result. Invalid options stop deployment without changing the running service.- Reserved settings cannot be overridden. These include identity/protocol (
user,pid_file,server_id,datadir,socket,port,bind_address,mysqlx_bind_address,report_host,mysqlx, …). They also include replication/plugins (gtid_mode,enforce_gtid_consistency,log_bin,relay_log,plugin_load*,clone,plugin_clone,plugin_mysqlx,group_replication_*, …) and TLS (require_secure_transport,ssl_*). The role owns and rejects overrides for these keys. - Changes trigger rolling restarts: secondaries first, primary last.
Default buffer pool is 25% of node memory, at least 256MB. Redo capacity is half that size, limited to 128MB–4GB. Replica parallelism follows CPU count. Override innodb_buffer_pool_size and other keys through mysql_parameters when needed.
Backup Settings
The backup contract (details in Administration):
- Run a daily full physical XtraBackup backup and prepare it immediately. The result is directly restorable.
- Standalone backs up locally. Every HA timer runs, but only the current PRIMARY performs backup. Others skip.
- Use
<path>/<cluster>/<UTC timestamp>/, atomiclatestsymlinks, and retention-based pruning. - No incremental chains, binlog archiving, or PITR. Standalone recovery reaches the most recent backup.
After failover, backups use the new primary’s local disk. Compare latest timestamps on all members and select the newest.
For off-site protection, synchronize backups yourself, for example with scheduled rclone or rsync.
Platform Credentials
Credential lifecycle rules:
- Passwords must be single-line and cannot retain a
CHANGE_MEprefix. Preflight enforces this. - Ordinary HA reruns cannot rotate
mysql_cluster_password. Metadata and Router keyrings bind it, so implicit rotation fails. Standalone rotation works normally. mysql_root_passwordcannot reset silently. If the live value differs, the task fails instead of replacing it.
Root owns /etc/mysql/pigsty/, with directory mode 0700 and file mode 0600. It contains secrets and ready-to-use local client configuration:
Full Example
Standalone plus three-node HA, matching the four-node sandbox:
Use conf/demo/mysql.yml for the full template. conf/mysql.yml belongs to OpenHalo, a MySQL-compatible PostgreSQL kernel, and is unrelated to this module.
2 - Parameters
Deployment exposes 11 parameters; removal adds two protected-operation parameters. Versions, ports, directories, charset, TLS paths, and schedules are fixed. Memory sizing follows node specifications. Use mysql_parameters to change server behavior.
Quick Reference
| Parameter | Level | Default | Description |
|---|---|---|---|
mysql_cluster | Cluster | required | Cluster name and identity |
mysql_seq | Instance | required | 1 for standalone; sequential 1..3 for HA |
mysql_root_password | Cluster | DBUser.Root | Local root password |
mysql_monitor_password | Cluster | DBUser.Monitor | Exporter identity password |
mysql_cluster_password | Cluster | DBUser.Cluster | AdminAPI/Router/backup identity password |
mysql_databases | Cluster | [] | Additive database declarations |
mysql_users | Cluster | [] | Additive user and grant declarations |
mysql_parameters | Cluster/Instance | {} | [mysqld] option overrides |
mysql_backup_enabled | Cluster | true | Daily full-backup timer |
mysql_backup_repo | Cluster | see below | Local backup path and retention |
mysql_exporter_enabled | Cluster | true | Exporter and monitoring target |
The removal parameters are used by mysql-rm.yml:
| Parameter | Level | Default | Description |
|---|---|---|---|
mysql_safeguard | Global/Cluster/CLI | true | Refuse removal by default |
mysql_rm_confirm | CLI | '' | Must exactly match the instance or cluster name |
Earlier parameters are no longer supported: mysql_role, mysql_services, mysql_packages, mysql_data, mysql_port, mysql_replication_*, and mysql_*_username. Do not use them.
Identity
mysql_cluster
Required cluster identity, matching the member inventory group. Preflight enforces this. Start with a letter, digit, or underscore. ._- is permitted; maximum length is 63:
This determines instance names (my-test-1), MGR group UUID, backup directory (<repo>/my-test/), and monitoring cls.
mysql_seq
Required sequence: 1 for standalone, consecutive 1, 2, 3 for HA. It also sets server_id:
mysql_seq=1 marks the bootstrap coordinator only. Runtime PRIMARY is elected; reruns preserve its placement.
Credentials
mysql_root_password
Local root@'localhost' password for socket or loopback access. Use one line without a CHANGE_ME prefix:
The default is DBUser.Root:
Set it at initial launch. Later mismatches between live and declared values fail explicitly. Rotate manually with ALTER USER, then update inventory.
mysql_monitor_password
Exporter password for dbuser_monitor@'127.0.0.1'. This read-only, loopback account has a three-connection limit:
The default is DBUser.Monitor:
mysql_cluster_password
Shared password for TLS-only dbuser_cluster@'%' and local dbuser_backup@'localhost'. It supports AdminAPI, Router bootstrap, and XtraBackup:
The default is DBUser.Cluster:
HA metadata and Router keyrings bind this password. Ordinary reruns cannot rotate it; preflight rejects mismatches. Standalone has no such binding. Update inventory and rerun there.
Business Objects
mysql_databases
Additive database list accepting only name / encoding / collate:
Create and update only. Removing an entry never drops a database. See Configuration for syntax and validation.
mysql_users
Additive user list with fields name / host / password / connlimit / priv:
Grants apply without implicit revocation. Platform identities are forbidden. See Configuration for syntax and validation.
mysql_parameters
Dictionary of [mysqld] overrides appended to managed configuration. The last value takes precedence:
Constraints and behavior:
- Keys must match
[A-Za-z][A-Za-z0-9_.-]{0,63}; values must be single-line scalars.mysqld --validate-configrejects errors before changing the instance. - Reserved settings are rejected, treating
-and_alike:user,pid_file,server_id,datadir,socket,port,bind_address,mysqlx_bind_address,report_host,gtid_mode,enforce_gtid_consistency,log_bin,relay_log,require_secure_transport,ssl_ca,ssl_cert,ssl_key,plugin_load,plugin_load_add,clone,plugin_clone,mysqlx,plugin_mysqlx. This also includesgroup_replication_*,plugin_group_replication*,plugin_mysqlx_bind_address, andssl_*. - Rerun
mysql.ymlto apply changes. It rolls secondaries first, then the primary, with one brief write interruption. - Common overrides include
sql_require_primary_key(defaultON),long_query_time(default1),binlog_expire_logs_seconds(default seven days), and memory settings.
AdminAPI replication settings written through SET PERSIST control runtime behavior. Each run restores group_replication_group_seeds to declared membership to prevent persisted drift.
Backup
mysql_backup_enabled
Control the daily mysql-backup.timer, which adds up to 30 minutes of random delay:
false stops the timer but retains script and configuration. If backup was never enabled, the repository directory is absent and manual execution exits immediately.
mysql_backup_repo
The local backup repository — local is the only supported method:
Layout and restore procedure: Administration.
Monitoring
mysql_exporter_enabled
Whether mysqld_exporter runs and the VictoriaMetrics target is registered:
false stops the exporter and empties /infra/targets/mysql/<instance>.yml. Only mysql-rm.yml removes the file.
Removal Parameters
mysql_safeguard
Removal safety switch, default true. Explicitly set false for mysql-rm.yml; otherwise, removal stops:
mysql_rm_confirm
Confirmation string, empty by default. For one member, exactly match its instance name, such as my-test-3. For full-cluster or standalone removal, exactly match mysql_cluster. Both confirmation and mysql_safeguard=false are required.
Fixed Platform Conventions
These settings are fixed or derived. They are not inventory parameters:
| Item | Value |
|---|---|
| Versions | MySQL Server/Client/Shell/Router 8.4 LTS, Percona XtraBackup 8.4 |
| Ports | 3306 (classic), 33060 (X Protocol; loopback-only on standalone), 33061 (MGR), 6446/6447 (Router RW/RO), 9104 (exporter); INFRA carries the read-only reference constant mysql_exporter_port: 9104 for monitoring config, not as a public MYSQL parameter |
| Data directory | /var/lib/mysql (binlogs under binlog/, 7-day expiry) |
| Config file | EL: /etc/my.cnf.d/pigsty.cnf; Debian/Ubuntu: /etc/mysql/mysql.conf.d/pigsty.cnf |
| Service units | MySQL: mysqld on EL, mysql on Debian/Ubuntu; Router: mysqlrouter; Exporter: mysqld_exporter |
| Secrets and scripts | /etc/mysql/pigsty/ (root-owned: directory 0700, files 0600) |
| Logs | Error log at /var/log/mysql/error.log, mirrored to Journald; slow log at /var/log/mysql/slow.log (1s threshold) |
| TLS | Enforced (require_secure_transport=ON); CA at /etc/pki/ca.crt, leaf certs under /etc/mysql/pki/ |
| Charset | utf8mb4 / utf8mb4_0900_ai_ci |
| Memory | Buffer pool = max(25% of node memory, 256MB); redo = clamp(50% of buffer pool, 128MB, 4GB) |
| Replication | GTID enforced, sql_require_primary_key=ON, single-primary MGR, BEFORE_ON_PRIMARY_FAILOVER consistency |
| Datadir markers | .pigsty-mysql-initialized (ownership check) and .pigsty-mysql-retired (retirement guard) |
3 - Administration
Declare state in the inventory and apply it with the playbook. Most dropped members or AdminAPI drift recover after ./mysql.yml -l <cluster>. Member replacement, physical restore, and complete-outage recovery require the manual procedures below.
Quick Reference
| Operation | Command |
|---|---|
| Deploy / converge a cluster | ./mysql.yml -l <cluster> |
| Preflight without changes | ./mysql.yml -l <cluster> --check |
| Local root session | mysql --defaults-extra-file=/etc/mysql/pigsty/root.cnf |
| Inspect MGR topology | SELECT MEMBER_HOST,MEMBER_STATE,MEMBER_ROLE FROM performance_schema.replication_group_members; |
| AdminAPI status | dba.getCluster().status() in mysqlsh |
| Trigger a backup | systemctl start mysql-backup (in HA, only the primary runs it) |
| Retire a secondary | ./mysql-rm.yml -l <IP> -e mysql_safeguard=false -e mysql_rm_confirm=<instance> |
| Retire a whole cluster | ./mysql-rm.yml -l <cluster> -e mysql_safeguard=false -e mysql_rm_confirm=<cluster> |
Status Checks
Run these commands as root on a cluster member. Only root can read /etc/mysql/pigsty/ credentials. Examples use EL service names. Debian/Ubuntu uses mysql, not mysqld.
On any member, confirm services and topology:
A healthy three-node cluster has three ONLINE rows and exactly one PRIMARY. For AdminAPI status:
Use MySQL Overview or mysql:cls:health for fleet status: 2 healthy, 1 degraded, 0 critical.
Client Access
HA clients connect through any member’s Router, which follows failovers automatically:
Guidance:
- The server requires TLS and rejects plaintext. Client
PREFERREDnegotiates encryption automatically. Prefer explicitVERIFY_CAwith the Pigsty CA; JDBC usessslMode=VERIFY_CA. - There is no VIP/DNS layer. Use a multi-host DSN, such as
jdbc:mysql://10.10.10.11:6446,10.10.10.12:6446,10.10.10.13:6446/app, to avoid one Router failure point. Alternatively, list all members in the application load balancer. - Standalone instances have no Router. Connect directly to
3306. - A partitioned or quorum-lost member makes its Router reject read/write and read-only connections. This prevents stale Router reads.
Measured write interruptions are approximately 3–4 seconds for graceful primary shutdown and 20 seconds for kill -9 with default eviction. Secondary rolling restarts do not interrupt clients.
Manage Databases and Users
Edit mysql_databases / mysql_users in the inventory, then converge:
In HA, changes run on the current primary and replicate to other members. Declarations are additive. Nothing is dropped or revoked implicitly. Perform removals manually, then update inventory.
Change Cluster Parameters
All tuning goes through mysql_parameters:
Rolling-restart semantics (verified by testing):
- Validate rendered settings with
mysqld --validate-config. Invalid options stop execution without changing the service. - Check health. Fewer than three ONLINE members blocks rolling restarts. Repair before changing configuration.
- Restart secondaries one at a time, waiting for
ONLINEafter each. Restart the primary last. - Primary restart causes one automatic failover and a write pause of a few seconds. Schedule a change window if needed.
Standalone instances restart in place.
Switchover
The module does not orchestrate planned switchovers; use AdminAPI when you need one:
Routers follow the switchover automatically. Rerun ./mysql.yml -l <cluster> to verify convergence. Primary placement is runtime state, so the playbook does not move it back.
Member Failures and Self-Healing
No action is needed for ordinary failover. After a primary crash, MGR elects a replacement in approximately 20 seconds. Routers follow it. Systemd restarts the failed member, which rejoins automatically. Intervene for these cases:
| Symptom | Action |
|---|---|
A member stays OFFLINE (process up, GR stopped) | Rerun ./mysql.yml -l <cluster> — it rejoins the member |
A member repeatedly fails to join, logging peers not configured | Same: the converge pins group_replication_group_seeds back to the declared list |
| A member has not returned after a network partition heals | Wait ~1 minute for auto-rejoin; rerun the playbook if it still has not rejoined |
All members OFFLINE | Complete outage — see Recover from a Complete Outage |
| Hardware is unrecoverable | See Replace a Failed Member |
Matching alerts: MySQLClusterMemberOffline (WARN), MySQLClusterNoPrimary / MySQLClusterQuorumLost (CRIT).
Replace a Failed Member
Replacement hardware must reuse the failed member’s service address. Keep inventory unchanged. For failed my-test-3 (10.10.10.13), use three steps:
Notes:
- Step 1 removes the address from cluster metadata. Only an absent address qualifies for fresh clone. Retirement requires a reachable ONLINE SECONDARY or detached member. For dead hardware, use force removal in 1b.
- Replacement hardware must be fully clean, with an empty datadir and no Router keyring. OS reinstallation guarantees this. Preflight or Router bootstrap rejects partial cleanup.
- Clone copies the full dataset; time depends on size. One primary and one secondary keep the cluster available.
- Address changes during replacement and long-term two-node operation are unsupported.
Retire and Resurrect a Cluster
Retire a whole cluster (stop services, deregister monitoring, keep all data):
Retirement writes /var/lib/mysql/.pigsty-mysql-retired on every member. This blocks ordinary mysql.yml reruns and accidental revival. To restore service deliberately:
Two commands suffice for standalone instances. HA needs another step. Reruns start services, but all three Group Replication members remain OFFLINE. No member self-bootstraps, to prevent split-brain. The playbook reports complete outage. Follow steps 3–4 of complete-outage recovery to restore quorum.
Playbooks never destroy datadirs, backups, or packages. Decide on destruction manually after verifying backups.
Manage Backups
Backup layout, on the current primary’s local disk:
Check backup freshness on all HA members because backups follow the primary:
This version has no backup-freshness metric or backup alerts. Failures appear only in mysql-backup logs.
Query them through VictoriaLogs or the Instance dashboard’s Router / Backup Logs panel. In important environments, add external log checks and regularly rehearse restoration.
Restore from Physical Backup
This procedure restores standalone data to the latest backup. It is destructive: later writes are lost. Check the latest timestamp first. For HA, restore one primary node, then clone it to the other members.
The step-4 marker proves datadir ownership. Missing or mismatched contents cause mysql.yml to reject the restored directory. For HA, use "topology": "innodb_cluster" and the member’s own instance name.
Recover from a Complete Outage
If all three members are OFFLINE, MGR does not rebuild quorum automatically, preventing split-brain. This can follow power loss or cascading failure. mysql.yml stops with instructions. Use this procedure:
Notes:
- Step 3 usually restores all reachable members. Step 4 rejoins remaining members without manual per-node work.
- If only a minority survives, complete reboot to restore writes, then replace failed members.
super_read_onlyprevents writes until step 3 finishes. Members usually allow reads. Previously expelled members can remain inoffline_modeand reject ordinary connections.- Default
sql_require_primary_key=ONprevents tables without primary keys from blocking recovery.
Platform Password Boundaries
Operational boundaries for the three platform passwords (details: Parameters):
mysql_monitor_password: Change inventory and rerun to rotate.mysql_root_password: Implicit resets are rejected. On the primary, manually runALTER USER 'root'@'localhost' IDENTIFIED BY '...';. Then update inventory and rerun to refresh credential files.mysql_cluster_password: HA metadata and Router keyrings bind this password. Ordinary reruns reject rotation, and no automated HA rotation exists. If necessary, rotate manually through AdminAPI, synchronize all member credential files, then update inventory. Standalone instances rotate through inventory and rerun.
4 - Playbook
mysql.yml deploys and adjusts clusters to declared state. Separate mysql-rm.yml retires members or clusters with safeguards. Before every removal, recheck the target, backups, and exact confirmation value.
mysql.yml
Selected clusters follow check → install → bootstrap → access → provision → backup → monitor:
Usage rules:
- Complete HA clusters: Partial
-lselection fails preflight to prevent topology divergence. Multiple complete clusters or no-lare permitted. - Idempotent: Converged clusters return
changed=0within seconds. After AdminAPI rejoin/clone, one expectedchangedcan restore declared replication seeds. - Check mode: Fresh nodes preview through package installation only; later stages need installed components. Deployed clusters support full previews.
- Initial three-node deployment takes approximately two minutes: certificates, configuration/init, AdminAPI creation, two clones, Router bootstrap, business objects, backup, and monitoring.
Stages and Tags
Common tag-scoped runs:
Run the full playbook for parameter and configuration changes. They require the rolling-restart workflow below.
Config Changes and Rolling Restarts
mysql_launch orchestrates restarts after configuration, certificate, or Systemd unit changes:
- Health: All three HA members must be
ONLINE. Degraded clusters reject restarts; repair first. - Secondaries: Use live roles, not
mysql_seq. Restart one secondary at a time and wait forONLINE. - Primary: Restart last. One automatic failover pauses writes for a few seconds.
Standalone restarts in place. Render-time mysqld --validate-config rejects invalid options before service changes.
Guardrails
mysql.yml rejects these conditions and reports the cause and next steps:
| Refused scenario | Rationale |
|---|---|
| Partial member selection | HA operations must cover the whole cluster |
| Invalid topology | Member count must be 1 or 3, with consecutive mysql_seq |
| Unsupported platform | Arch/OS outside the support matrix (e.g. Ubuntu ARM64) |
| Placeholder passwords | CHANGE_ME credentials left in place |
| Foreign datadir | Datadir lacks the Pigsty marker, or the marker names another cluster/instance/topology |
| Retirement marker present | Instance was retired by mysql-rm.yml; resurrection must be explicit |
| Implicit password changes | mysql_cluster_password or live root password differs from the declaration |
| Illegal parameter overrides | Reserved keys, malformed names, or multi-line values in mysql_parameters |
| Degraded-cluster restart | Config-driven restarts require all members ONLINE |
| Non-fresh clone target | Replacement members must be brand-new machines with empty datadirs |
| Complete outage | Quorum is never rebuilt automatically; the error prints the manual recovery steps |
These checks reduce risk but cannot guarantee against data loss. Before bypassing checks, verify backups and exact scope. Marker deletion and datadir erasure must be deliberate decisions.
mysql-rm.yml
Retirement accepts three scopes. Each requires mysql_safeguard=false and mysql_rm_confirm exactly matching its target:
Retirement behavior:
- Single-member retirement uses AdminAPI
force: falsefor anONLINE SECONDARY, or verifies an already detached member, then stops local services. The script runs on the target, which must be reachable. For dead hardware, use manual force removal. Primary retirement is rejected; first switch withsetPrimaryInstance. Removing two of three members together is also rejected. - Full-cluster retirement stops Router and backup timers, then secondaries before the primary. It deregisters exporters and monitoring targets.
- Each datadir receives
.pigsty-mysql-retired, blocking ordinarymysql.ymlreruns. - All data remains: datadirs, backups, configuration, certificates, packages, metadata, and Router identities. Destruction requires a separate manual decision after backup verification.
--check previews the full plan without changes.
Playbook Boundaries
These operations remain outside playbook scope. Follow manual Administration procedures:
- Planned switchover (
setPrimaryInstance); - Force removal of dead, unreachable members (
removeInstancewithforce: true); - Quorum rebuild after a complete outage (
rebootClusterFromCompleteOutage); - Physical restore (XtraBackup copy-back runbook);
- Destruction: removing datadirs, backups, or retirement markers;
- Topology changes (1→3, 3→5) and member re-addressing.
5 - Monitoring
MYSQL sends mysqld_exporter metrics to VictoriaMetrics and Journald/Vector error logs to VictoriaLogs. Grafana supplies five dashboards. vmalert loads 68 recording rules and 27 alerts.
Collection Architecture
Each node runs mysqld_exporter on 9104 with least-privilege dbuser_monitor@'127.0.0.1'. Deployment creates a discovery file on the INFRA node:
The VictoriaMetrics mysql job reads this directory. mysql_exporter_enabled: false empties the target list. Only mysql-rm.yml removes the file.
Collectors cover global status/variables, binlog size, InnoDB, process lists, top-50 statement digests, table/index I/O waits, MGR membership, and replication statistics.
Label Model
All MySQL metrics carry a consistent label set:
| Label | Meaning | Example |
|---|---|---|
job | Scrape job | mysql |
cls | Cluster name | my-test |
ins | Instance name | my-test-1 |
ip | Member address | 10.10.10.11 |
topology | Topology type | innodb_cluster / standalone |
Derived names use mysql:ins:* for instances and mysql:cls:* for clusters. See Metrics.
Grafana Dashboards
| Dashboard | Purpose |
|---|---|
| MySQL Overview | Fleet view: cluster inventory, health, QPS/TPS, active alerts, instance list |
| MySQL Cluster | One cluster: member states, workload, node resources, cluster logs |
| MySQL Instance | One instance: connections, statements, InnoDB, temp tables, locks, logs |
| MySQL Group Replication | MGR deep dive: roles, certification/applier queues, flow control, read-only safety, GR logs |
| MySQL Alert | Alert summary and key platform logs |
mysql:cls:health reports 2 healthy, 1 degraded but writable, or 0 critical or unwritable. Overview’s Healthy Clusters stat and Cluster Health timeline use it.
Group Replication panels apply only to innodb_cluster. No data is expected for standalone clusters.
Alert Rules
The 27 rules use severity CRIT, WARN, or INFO. Use these alerts for paging:
Availability and Cluster State
| Alert | Severity | Condition |
|---|---|---|
MySQLInstanceDown | CRIT | Connection probe failing for 1m |
MySQLClusterNoPrimary | CRIT | No ONLINE primary for 1m |
MySQLClusterQuorumLost | CRIT | ONLINE members below majority for 1m |
MySQLClusterMultiplePrimary | CRIT | More than one primary for 30s (split-brain signal) |
MySQLSecondaryWritable | CRIT | A secondary writable for 2m (divergence risk) |
MySQLClusterMemberOffline | WARN | A declared member out of the group for 5m |
MySQLPrimaryReadOnly | WARN | Primary read-only for 5m |
MySQLExporterDown | WARN | Scrape failing for 2m |
Capacity and Performance
Other alerts cover:
- Connections:
MySQLConnectionsHighWARN at 80%,MySQLConnectionsCriticalCRIT at 95%. - Replication queues:
MySQLGRQueueHighWARN andMySQLGRQueueCriticalCRIT. - Flow control:
MySQLGRFlowControlHigh. - InnoDB:
MySQLBufferPoolWaits,MySQLInnoDBLogWaits,MySQLRedoCapacityHigh,MySQLDeadlocksHigh, andMySQLHistoryListLarge. - INFO hints: slow queries, disk temporary tables, full joins, buffer-pool hit ratio, and recent restarts.
Tests showed approximately 20-second primary crash failovers produced pending alerts without false pages. Complete outages fired ClusterNoPrimary and QuorumLost within two minutes.
Log Queries
MySQL writes errors to /var/log/mysql/error.log and through syslog → Journald → Vector → VictoriaLogs. app=mysqld-<instance> labels support dashboard panels and LogsQL queries:
Log cls uses the node cluster, node_cluster. Match it to mysql_cluster, as examples do, to align metric and log labels.
Known boundaries:
- Slow queries:
slow.log, with a 1s threshold, remains local. Inspect it on the instance or use statement digests such asmysql:ins:statement_latency. - Router runtime logs:
/var/log/mysqlrouter/also remains local.
Verify the Pipeline
Verify every pipeline stage after deployment:
6 - Metrics
mysqld_exporter supplies raw mysql_ metrics. vmalert derives mysql:ins:* and mysql:cls:* metrics for dashboards and alerts. This page defines those metrics.
Common Labels
Metrics carry job=mysql, cls, ins, ip, and topology (standalone or innodb_cluster). Instance-derived metrics retain all identity labels. Cluster metrics aggregate to cls and topology.
Availability
| Metric | Meaning |
|---|---|
mysql:ins:exporter_up | Scrape success (transport health) |
mysql:ins:up | MySQL connection probe success (database health) |
mysql:ins:uptime | Instance uptime in seconds |
mysql:cls:instances | Declared instance count |
mysql:cls:up | Online instance count |
mysql:cls:health | Cluster health: 2 healthy / 1 degraded-writable / 0 critical |
For HA, mysql:cls:health combines quorum, single-primary, and full-membership state. For standalone, it equals 2 × mysql:cls:up.
Workload
| Metric | Meaning |
|---|---|
mysql:ins:qps | Questions per second |
mysql:ins:tps | Transactions per second (commit + rollback) |
mysql:ins:read_qps / mysql:ins:write_qps | Read-class / write-class command rate |
mysql:ins:row_ops | InnoDB row operations (read/insert/update/delete dimensions) |
mysql:ins:statement_rate | Performance-schema statement rate |
mysql:ins:statement_latency | Mean statement latency (seconds) |
mysql:ins:rows_examined_per_query | Average rows examined per query |
mysql:ins:statement_errors | Statement error rate |
mysql:ins:slow_queries / mysql:ins:slow_query_ratio | Slow query rate and ratio |
mysql:ins:no_index_queries | Rate of queries using no index |
Connections and Sessions
| Metric | Meaning |
|---|---|
mysql:ins:connections | Current connections (Threads_connected) |
mysql:ins:connection_usage | Connections / max_connections |
mysql:ins:connection_rate | New connection rate |
mysql:ins:threads_running / mysql:ins:threads_cached | Active / cached threads |
mysql:ins:aborted_connects / mysql:ins:aborted_clients | Failed handshakes / abnormal disconnects |
mysql:ins:connection_errors | Total connection error rate |
mysql:ins:rx_bytes / mysql:ins:tx_bytes | Network receive / transmit rate |
Temp Tables, Scans, and Caches
| Metric | Meaning |
|---|---|
mysql:ins:tmp_tables / mysql:ins:tmp_disk_tables | In-memory / on-disk temp table creation rate |
mysql:ins:tmp_disk_ratio | Share of temp tables spilling to disk |
mysql:ins:full_joins / mysql:ins:full_scans | Index-less join / full scan rate |
mysql:ins:sort_merge_passes | Sort merge passes (undersized sort buffer signal) |
mysql:ins:table_open_cache_hit_ratio | Table open cache hit ratio |
mysql:ins:open_files_usage | Open file usage ratio |
InnoDB
| Metric | Meaning |
|---|---|
mysql:ins:buffer_pool_hit_ratio | Buffer pool hit ratio |
mysql:ins:buffer_pool_usage / mysql:ins:buffer_pool_dirty_ratio | Buffer pool usage / dirty page ratio |
mysql:ins:buffer_pool_waits | Free-page wait rate (memory pressure signal) |
mysql:ins:data_read_bytes / mysql:ins:data_write_bytes | Data file read / write throughput |
mysql:ins:data_reads / mysql:ins:data_writes / mysql:ins:data_fsyncs | Data file I/O and fsync rates |
mysql:ins:redo_bytes | Redo write throughput |
mysql:ins:redo_utilization | Redo capacity utilization (checkpoint lag) |
mysql:ins:log_waits | Redo buffer wait rate |
mysql:ins:row_lock_waits / mysql:ins:row_lock_time | Row lock wait rate / time |
mysql:ins:deadlocks | Deadlock rate |
mysql:ins:history_list_length | Purge lag (history list length) |
mysql:ins:binlog_bytes | Total on-disk binlog size (bytes) |
Group Replication
Instance membership flags have value 1 or are absent. False conditions produce no series, so alerts use unless:
| Metric | Meaning |
|---|---|
mysql:ins:gr_member | Instance is in any MGR member state |
mysql:ins:gr_online | Instance is ONLINE |
mysql:ins:gr_primary / mysql:ins:gr_secondary | Instance is the ONLINE primary / a secondary |
Cluster-level quorum and topology:
| Metric | Meaning |
|---|---|
mysql:cls:gr_online_members | ONLINE member count |
mysql:cls:gr_primary_members | ONLINE primary count |
mysql:cls:gr_quorum | Majority held (0/1) |
mysql:cls:gr_single_primary | Exactly one primary (0/1) |
Replication pipeline (certification and apply):
| Metric | Meaning |
|---|---|
mysql:ins:gr_certifier_queue / mysql:ins:gr_applier_queue | Transactions backed up in certification / applier queues |
mysql:ins:gr_certifier_queue_ratio / mysql:ins:gr_applier_queue_ratio | Queue depth relative to flow-control thresholds |
mysql:ins:gr_checked_rate / mysql:ins:gr_applied_rate | Certification / apply throughput |
mysql:ins:gr_conflict_rate | Certification conflict rate (should be 0 under single-primary) |
Raw Metric Families
For data not covered by derived metrics, query raw exporter families:
| Prefix | Content |
|---|---|
mysql_up / up | Database probe / scrape status |
mysql_global_status_* | Full SHOW GLOBAL STATUS counters |
mysql_global_variables_* | Key system variables (e.g. max_connections) |
mysql_perf_schema_events_statements_* | Statement digests (top 50 by digest) |
mysql_perf_schema_table_io_waits_* / ..._index_io_waits_* | Table / index I/O waits |
mysql_perf_schema_replication_group_member_info | MGR membership (member_state / member_role dimensions) |
mysql_perf_schema_transactions_* / mysql_perf_schema_conflicts_detected_total | MGR certification, applier queues, and conflicts |
mysql_binlog_* | Binlog file count and size |
mysql_info_schema_processlist_* | Session distribution by state |
Browse the complete list with the mysql_ prefix in VictoriaMetrics vmui (/select/vmui).
7 - FAQ
How mature is the MYSQL module?
This pilot targets simple, low-cost MySQL clusters. Deployment/convergence, HA failover, daily backups, and monitoring/alerts have systematic tests, including fault injection and complete-outage exercises. Destructive recovery remains manual, with procedures. It has no PITR, VIP/DNS access layer, or automatic scaling; its scope is smaller than PGSQL. Before production, validate your requirements and rehearse recovery.
Why is MySQL fixed at 8.4? Can I pick a version?
The fixed platform pins server, client, Shell, Router, and XtraBackup to 8.4 LTS. This keeps compatibility predictable and avoids a broad version test matrix. That tradeoff keeps the pilot simple. Use another tool for other versions or extensive customization.
Why only 1 or 3 nodes? How do I scale?
Topology is standalone or three-node single-primary InnoDB Cluster. Preflight rejects other counts, and datadir identity blocks in-place 1→3 conversion. Dynamic membership would add quorum management, Router re-bootstrap, and convergence complexity beyond this pilot’s scope.
Scaling paths:
- Vertical: move to bigger machines one at a time via same-address replacement (rolling hardware refresh);
- Standalone → HA: build a new three-node cluster and migrate logically (
mysqldumpormysqlsh util.dumpInstance); - Read scaling: send read-only traffic to
6447, shared by the two secondaries.
Why does CREATE TABLE fail with ERROR 3750 (primary key required)?
sql_require_primary_key=ON is the default. Tables without primary keys are read-only under Group Replication and block AdminAPI disaster recovery. Rejecting them at creation avoids recovery failures. Give every table a primary key. Override only for a legacy system that cannot change:
Standalone instances keep the same default so they stay HA-portable.
Why is Ubuntu/Debian ARM64 rejected?
Oracle APT provides no MySQL 8.4 arm64 packages. On ARM, including Apple Silicon VMs, use EL 9/10 (Rocky/Alma). Oracle YUM supports aarch64 fully.
Which port should clients use? Is TLS mandatory?
For HA, use any member’s Router on 6446 for read/write or 6447 for read-only. Routers follow failover. Standalone clients use 3306. The server requires TLS through require_secure_transport=ON; plaintext fails with ERROR 3159. Default PREFERRED negotiates TLS, while explicit DISABLED fails. Prefer VERIFY_CA with /etc/pki/ca.crt.
Each node has its own Router, without a shared VIP. List every member’s 6446 in a multi-host DSN to tolerate node failure.
The primary moved. Will it move back automatically?
No. mysql_seq=1 sets bootstrap order only. MGR can elect any runtime primary after failover or rolling restart. Reruns preserve this placement. Use setPrimaryInstance for deliberate placement.
A member went down. What do I do?
Usually no action is needed. Systemd restarts mysqld, which rejoins automatically. Primary failover and recovery take approximately 20 seconds. If a member remains OFFLINE after network recovery or STOP GROUP_REPLICATION, rerun ./mysql.yml -l <cluster>. If rejoin fails, follow the reported cause and next step.
All three nodes are down. How do I recover?
Complete outage requires manual recovery to prevent split-brain. Run dba.rebootClusterFromCompleteOutage() on the most advanced member, then rerun the playbook for the others. See complete-outage recovery. The mysql.yml failure message prints the same instructions.
Where are backups stored? Can I restore to a point in time?
Daily full physical backups reside at /data/backups/mysql/<cluster>/ on the current primary. After failover, new backups use the new primary; compare all members for the latest. There are no incremental chains, binlog archives, or PITR. Standalone can recover only to its latest backup, losing up to one day of writes. HA mainly depends on three synchronized replicas, with backups for final recovery and full rebuilds. See Physical Restore, and synchronize backup directories yourself for off-site protection.
Will I be alerted if backups fail?
Not yet. Backup-freshness metrics and alerts are a known gap. VictoriaLogs receives unit:mysql-backup logs, also visible in the Instance dashboard Router / Backup Logs panel. Add external log checks for important environments and rehearse restoration regularly.
Why are some mysql_parameters keys rejected?
The platform owns identity (server_id, datadir, ports, …), replication (gtid_mode, log_bin, group_replication_*), and TLS settings. Overrides could break identity or security guarantees. Preflight rejects both - and _ spellings. Other options are permitted and checked by mysqld --validate-config. See the reserved list.
Does changing parameters cause downtime?
Configuration changes roll secondaries first, without client interruption, then restart the primary. One automatic failover pauses writes for approximately 3–4 seconds in tests. Degraded clusters reject rolling restarts to avoid adding changes during an outage. Schedule a window for failover-sensitive workloads.
How do I change root or platform passwords?
mysql_monitor_password: Update inventory and rerun. Exporter configuration updates too.mysql_root_password: Implicit resets fail to prevent silent misconfiguration. RunALTER USER 'root'@'localhost' ...manually, then update inventory and rerun.mysql_cluster_password: HA metadata and Router keyrings bind it. Ordinary reruns reject rotation, and no automated HA procedure exists. Standalone rotation works normally. If necessary in HA, use AdminAPI, synchronize member credential files, then update inventory.
How do I resurrect a retired cluster? What if the marker is deleted by mistake?
mysql-rm.yml retains all data and writes a retirement marker. Remove /var/lib/mysql/.pigsty-mysql-retired on each member and rerun mysql.yml to revive it (details). This suffices for standalone. HA also needs quorum rebuilding. The retirement marker prevents accidental revival. Separate .pigsty-mysql-initialized ownership checks still prevent assigning data to another cluster.
Why doesn’t conf/mysql.yml match this module?
That template is OpenHalo, a PostgreSQL kernel with MySQL wire compatibility (pg_mode: mysql). It is unrelated to native MYSQL, whose template is conf/demo/mysql.yml. Use this module for native MySQL ecosystem compatibility. Consider OpenHalo for MySQL-protocol applications on PostgreSQL infrastructure.
The playbook fails with no ONLINE member holds the cluster?
This means no reachable ONLINE member carries the cluster. Follow the printed complete-outage instructions. If members are ONLINE, check coordinator seq-1 connectivity to every member’s port 3306. Reconciliation runs there. Also verify TLS trust and /etc/pki/ca.crt.
Monitoring shows no data / blank dashboards?
First run curl http://<member>:9104/metrics | grep mysql_up. Then check INFRA files in /infra/targets/mysql/ and query up{job="mysql"} in VictoriaMetrics. Standalone Group Replication panels correctly show No data. See Monitoring for full checks.