GTID replication replaces brittle file-and-position coordinates with transaction identifiers, which improves replica rebuilds, failovers, and topology changes.

MySQL and MariaDB implement GTID differently, but the core model stays consistent: each committed transaction is assigned a unique identifier, written to the binary log, and tracked by replicas as an executed set. With GTID auto-positioning enabled, the replica requests only the transactions it has not executed, rather than relying on a specific binary log file and byte offset.

Enabling GTID requires binary logging, unique server_id values, and careful initial seeding so the replica’s data matches the source at a known GTID position. Incorrect GTID state during restore can produce duplicate-key errors, missing data, or replication starting from the wrong point, so console access for rollback remains important during first enablement.

Steps to enable GTID replication:

  1. Choose unique server_id values for the source and replica.

    Example: source 101, replica 102.

  2. Add the required GTID options to the source /mysqld/ configuration file.

    Use only the MySQL block or the MariaDB block for a given server.

    MySQL config is commonly /etc/mysql/mysql.conf.d/mysqld.cnf on Debian or Ubuntu.

    MariaDB config is commonly /etc/mysql/mariadb.conf.d/50-server.cnf on Debian or Ubuntu.

    # MySQL (GTID source)
    [mysqld]
    server_id = 101
    log_bin = mysql-bin
    binlog_format = ROW
    gtid_mode = ON
    enforce_gtid_consistency = ON
    # MariaDB (GTID source)
    [mysqld]
    server_id = 101
    log_bin = mariadb-bin
    binlog_format = ROW
    gtid_domain_id = 1
    gtid_strict_mode = ON

    A syntax error in /mysqld/ configuration can prevent startup, which can require local console access to revert the change.

  3. Add the required GTID options to the replica /mysqld/ configuration file.

    Use only the MySQL block or the MariaDB block for a given server.

    # MySQL (GTID replica)
    [mysqld]
    server_id = 102
    log_bin = mysql-bin
    binlog_format = ROW
    gtid_mode = ON
    enforce_gtid_consistency = ON
    log_slave_updates = ON
    read_only = ON
    super_read_only = ON
    relay_log_recovery = ON
    # MariaDB (GTID replica)
    [mysqld]
    server_id = 102
    log_bin = mariadb-bin
    binlog_format = ROW
    gtid_domain_id = 1
    gtid_strict_mode = ON
    log_slave_updates = ON
    read_only = ON

    log_slave_updates keeps relay-applied changes in the replica’s binary log, which supports chaining replicas and safer promotions.

  4. Restart the database service on the source host.
    $ sudo docker restart sg-mysql-gtid-primary
    sg-mysql-gtid-primary

    Use the native systemd unit on non-container hosts.

  5. Restart the database service on the replica host.
    $ sudo docker restart sg-mysql-gtid-replica
    sg-mysql-gtid-replica
  6. Confirm the service is active on the source host.
    $ docker ps --filter name=sg-mysql-gtid-primary --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
    NAMES                   IMAGE       STATUS
    sg-mysql-gtid-primary   mysql:8.0   Up 16 seconds
  7. Confirm GTID is enabled on the source.
    mysql -u root -p -e "SHOW GLOBAL VARIABLES LIKE 'gtid_mode'; SHOW GLOBAL VARIABLES LIKE 'enforce_gtid_consistency';"
    +---------------+-------+
    | Variable_name | VALUE |
    +---------------+-------+
    | gtid_mode     | ON    |
    +---------------+-------+
    +--------------------------+-------+
    | Variable_name            | VALUE |
    +--------------------------+-------+
    | enforce_gtid_consistency | ON    |
    +--------------------------+-------+

    Ubuntu often permits

    sudo mysql

    when root uses socket authentication.

  8. Create a dedicated replication user on the source.
    mysql -u root -p -e "CREATE USER 'repl'@'192.0.2.61' IDENTIFIED BY 'ReplPass123!'; GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'192.0.2.61'; FLUSH PRIVILEGES;"

    Use a specific replica IP or subnet instead of '' to reduce exposure.</WRAP> <WRAP tip>Optional TLS enforcement: <code sql>ALTER USER 'repl'@'192.0.2.61' REQUIRE SSL; </code></WRAP> - Record the source //GTID// position for initial seeding. <code sql>mysql -u root -p -e "SELECT @@GLOBAL.gtid_executed\G" *************************** 1. row *************************** @@GLOBAL.gtid_executed: 10df7be4-e136-11f0-9c10-ae1f1ca9c7f8:1-3</code> <WRAP info>//MySQL// restores commonly rely on GTID_PURGED inside the dump, while //MariaDB// restores commonly apply gtid_slave_pos before starting replication.</WRAP> - Create a consistent seed backup on the source. <code bash>mysqldump --all-databases --single-transaction --routines --events --triggers --hex-blob --set-gtid-purged=ON --result-file=/root/sg-work/gtid/seed.sql</code> <WRAP alert>//MyISAM// or other non-transactional tables can break snapshot consistency with //–single-transaction//, which can cause replica drift.</WRAP> - Copy the seed backup file to the replica host. <code bash>$ docker cp sg-mysql-gtid-primary:/root/sg-work/gtid/seed.sql /root/sg-work/gtid/ $ docker cp /root/sg-work/gtid/seed.sql sg-mysql-gtid-replica:/root/sg-work/gtid/seed.sql</code> - Reset replica binary logs before restoring into a reused //MySQL// instance. <code sql>mysql -u root -p -e "RESET MASTER;"</code> <WRAP alert>//RESET MASTER// deletes binary logs on the replica, which removes local recovery history and is unsafe on a server that must retain its binary logs.</WRAP> - Disable //super_read_only// on the replica for the seed import. <code sql>mysql -u root -p -e "SET GLOBAL super_read_only = OFF; SET GLOBAL read_only = OFF;"</code> - Import the seed backup into the replica. <code bash>mysql -u root -p < /root/sg-work/gtid/seed.sql</code> - Re-enable read-only protection on the replica. <code sql>mysql -u root -p -e "SET GLOBAL read_only = ON; SET GLOBAL super_read_only = ON;"</code> - Configure the replica to use //GTID// auto-positioning against the source. <code sql>mysql -u root -p -e "CHANGE REPLICATION SOURCE TO SOURCE_HOST='192.0.2.60', SOURCE_PORT=3306, SOURCE_USER='repl', SOURCE_PASSWORD='ReplPass123!', SOURCE_AUTO_POSITION=1, GET_SOURCE_PUBLIC_KEY=1; START REPLICA;"</code> <WRAP info>Firewall rules must permit TCP 3306 from replica to source.</WRAP> - Verify replication health on the replica. <code sql>mysql -u root -p -e "SHOW REPLICA STATUS\G" *************************** 1. row *************************** Replica_IO_State: Waiting for source to send event Source_Host: 192.0.2.60 Source_User: repl Source_Port: 3306 Replica_IO_Running: Yes Replica_SQL_Running: Yes Auto_Position: 1 Retrieved_Gtid_Set: Executed_Gtid_Set: 10df7be4-e136-11f0-9c10-ae1f1ca9c7f8:1-4 Seconds_Behind_Source: 0 ##### snipped #####</code> <WRAP alert>No%% in IO or SQL running fields usually indicates credentials, network reachability, or binlog/GTID misconfiguration.

  9. Create a test database on the source.
    mysql -u root -p -e "CREATE DATABASE gtid_test;"
  10. Create a test table on the source.
    mysql -u root -p -e "CREATE TABLE gtid_test.ping (id INT PRIMARY KEY AUTO_INCREMENT, created TIMESTAMP DEFAULT CURRENT_TIMESTAMP);"
  11. Insert a test row on the source.
    mysql -u root -p -e "INSERT INTO gtid_test.ping (id) VALUES (NULL);"
  12. Query the test row on the replica.
    mysql -u root -p -e "SELECT * FROM gtid_test.ping ORDER BY id DESC LIMIT 1;"
    +----+---------------------+
    | id | created             |
    +----+---------------------+
    |  1 | 2025-12-25 02:07:38 |
    +----+---------------------+