Skip to content

feat(db): remove the hardcoded --server-id=0 parameter from MySQL startup, fixes #6768#7608

Merged
rfay merged 8 commits into
ddev:mainfrom
cyppe:feature/mysql-server-id-override
Sep 30, 2025
Merged

feat(db): remove the hardcoded --server-id=0 parameter from MySQL startup, fixes #6768#7608
rfay merged 8 commits into
ddev:mainfrom
cyppe:feature/mysql-server-id-override

Conversation

@cyppe
Copy link
Copy Markdown
Contributor

@cyppe cyppe commented Sep 9, 2025

Simplify MySQL Server ID Configuration

Summary

This PR removes the hardcoded --server-id=0 parameter from MySQL startup, allowing users to configure the server ID through standard MySQL configuration files in .ddev/mysql/.

Problem

Currently, ddev hardcodes --server-id=0 in the MySQL startup command, which prevents users from configuring MySQL replication. Command-line arguments take precedence over configuration files, so it's not possible to override the server ID through custom MySQL .cnf files placed in .ddev/mysql/.

According to MySQL documentation: "If the server ID is set to 0, binary logging takes place, but a source with a server ID of 0 refuses any connections from replicas, and a replica with a server ID of 0 refuses to connect to a source."

This limitation prevents developers from:

  • Testing MySQL replication setups in their local ddev environment
  • Using applications that require MySQL replication or depend on MySQL binary log events
  • Developing applications that need non-zero server IDs for replication functionality

Solution

  • Removed hardcoded --server-id=0 from docker-entrypoint.sh
  • Users can now set any server ID value using standard MySQL configuration files
  • Maintains backward compatibility (MySQL/MariaDB default behavior when no server-id is specified)
  • Follows standard MySQL configuration practices
  • Much simpler implementation with no environment variable complexity

Database Version Defaults

When no server-id is explicitly configured, different database versions have different default behaviors:

MariaDB:

  • MariaDB 10.2.2+: Default server_id = 1
  • MariaDB 10.2.1 and below: Default server_id = 0
  • Note: A server_id of 0 prevents replication (primaries refuse replica connections, replicas refuse primary connections)

MySQL:

  • MySQL 8.0+: Default server_id = 1
  • MySQL < 8.0: Default server_id = 0
  • MySQL 8.0+ issues an informational message if server_id is not explicitly set when binary logging is enabled

References:

Usage

Users can now set a custom server ID using standard MySQL configuration:

Create .ddev/mysql/server-id.cnf:

[mysqld]
server-id = 1

Then run ddev restart to apply the configuration.

Benefits of This Approach

  1. Standard MySQL Practice: Uses normal MySQL configuration file precedence
  2. Flexible: Users can set any server ID value they want
  3. Simple: No special environment variables or complex setup needed
  4. Compatible: Works with both MySQL and MariaDB
  5. Clean: Follows existing ddev patterns for MySQL configuration overrides

Use Case Example

This change enables developers to use MySQL replication functionality in their ddev development environment. Applications requiring MySQL replication can now use:

.ddev/mysql/replication.cnf:

[mysqld]
server-id        = 1
log_bin          = /var/log/mysql/mysql-bin.log
binlog_row_image = full

With this PR, developers can configure MySQL replication settings locally, matching their production MySQL replication setup using standard MySQL configuration practices.

@cyppe cyppe requested a review from a team as a code owner September 9, 2025 14:04
@cyppe cyppe changed the title Add MySQL Server ID Override Support feat(db): add MySQL server ID override support via MYSQL_SERVER_ID environment variable Sep 9, 2025
@stasadev
Copy link
Copy Markdown
Member

stasadev commented Sep 9, 2025

Previous attempt to do something similar:

@cyppe cyppe closed this Sep 9, 2025
@cyppe
Copy link
Copy Markdown
Contributor Author

cyppe commented Sep 9, 2025

I repoen again as this solution is simpler.

@cyppe cyppe reopened this Sep 9, 2025
@stasadev stasadev changed the title feat(db): add MySQL server ID override support via MYSQL_SERVER_ID environment variable feat(db): add MySQL server ID override support via MYSQL_SERVER_ID environment variable, fixes #6768 Sep 9, 2025
Copy link
Copy Markdown
Member

@stasadev stasadev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does look simpler, and requires a push for Docker images from our side. (I'll do it.)

It needs some addition to the documentation, doesn't it?

@cyppe
Copy link
Copy Markdown
Contributor Author

cyppe commented Sep 9, 2025

Agree, can you give some advice on where you want to mention this env variable and I will add it.

@nickchomey
Copy link
Copy Markdown
Collaborator

nickchomey commented Sep 9, 2025

I wonder if it would be better to just remove the command line arg for --server-id, which would then allow us to set it via a standard .cnf file rather than a ddev-specific env var? Or is that arg necessary for something?

@stasadev
Copy link
Copy Markdown
Member

stasadev commented Sep 9, 2025

can you give some advice on where you want to mention this env variable

Here https://docs.ddev.com/en/stable/users/extend/customization-extendibility/#custom-mysqlmariadb-configuration-mycnf

# Oracle mysql 5.7+ deprecates mysql_install_db
if [ "${mysqld_version}" = "5.7" ] || [[ "${mysqld_version%%%.*}" =~ ^8.[04]$ ]]; then
mysqld --defaults-file=/var/tmp/my.cnf --initialize-insecure --datadir=${DATADIR:-/var/lib/mysql} --server-id=0
mysqld --defaults-file=/var/tmp/my.cnf --initialize-insecure --datadir=${DATADIR:-/var/lib/mysql} --server-id=${MYSQL_SERVER_ID:-0}
Copy link
Copy Markdown
Member

@stasadev stasadev Sep 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create_base_db.sh is called during building ddev-dbserver (on our side, not on the user's side), so MYSQL_SERVER_ID is never going to be used here.

The only relevant change in docker-entrypoint.sh, right?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, this usage doesn't need the --server-id=${MYSQL_SERVER_ID}

@stasadev
Copy link
Copy Markdown
Member

stasadev commented Sep 9, 2025

I wonder if it would be better to just remove the command line arg for --server-id, which would then allow us to set it via a standard .cnf file rather than a ddev-specific env var? Or is that arg necessary for something?

That's an interesting idea, but I don't know the answer.
--server-id=0 has been used for years. Is it actually needed or not? 🤔

@cyppe
Copy link
Copy Markdown
Contributor Author

cyppe commented Sep 9, 2025

I would not mind removing it instead. Mysql defaults to 1 that is much better than 0. But wonder about historical reason to set it, as its pretty odd.

@cyppe
Copy link
Copy Markdown
Contributor Author

cyppe commented Sep 9, 2025

”MySQL 5.7: server_id default is 0. If binary logging is enabled, you must set a non-zero server_id or the server won’t start; and with server_id=0, a source/replica refuses replication connections.

MySQL 8.0+: default changed to 1 (part of broader default changes). It still must be unique in any replication topology.”

@rfay
Copy link
Copy Markdown
Member

rfay commented Sep 9, 2025

IIRC the server_id is only for replication, which would be really unusual in a local dev environment. Am I wrong?

@rfay
Copy link
Copy Markdown
Member

rfay commented Sep 9, 2025

Oh, reading the OP... you do want to set up replication. Can you say a bit about what's inspiring you to think about replication?

@cyppe
Copy link
Copy Markdown
Contributor Author

cyppe commented Sep 9, 2025

Its in the PR description, I have learned from other attempts to include it :)

"Using packages that require MySQL replication, such as laravel-trigger which provides MySQL event subscribers based on MySQL replication
Developing applications that depend on MySQL binary log events"

So all other settings is possible to fix . But the hardcoded --server-id=0 blocks me

So its not standard replication, as that would not make so much sense. Its more about consuming the data from the replication stream to dispatch laravel jobs based on changes in db.

@nickchomey
Copy link
Copy Markdown
Collaborator

Oh, reading the OP... you do want to set up replication. Can you say a bit about what's inspiring you to think about replication?

I need to override this locally for the same reasons as OP - development/testing for production.

Perhaps the test suite could be run after removing the cli flag and see what happens? If it works, maybe its best to just remove it

Copy link
Copy Markdown
Member

@rfay rfay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's nothing wrong with this; just needs to do a little bit less and have some documentation.

How about a guest blog to ddev.com explaining how to do replication with mysql8.x ?

# Oracle mysql 5.7+ deprecates mysql_install_db
if [ "${mysqld_version}" = "5.7" ] || [[ "${mysqld_version%%%.*}" =~ ^8.[04]$ ]]; then
mysqld --defaults-file=/var/tmp/my.cnf --initialize-insecure --datadir=${DATADIR:-/var/lib/mysql} --server-id=0
mysqld --defaults-file=/var/tmp/my.cnf --initialize-insecure --datadir=${DATADIR:-/var/lib/mysql} --server-id=${MYSQL_SERVER_ID:-0}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, this usage doesn't need the --server-id=${MYSQL_SERVER_ID}

fi
echo "Starting mysqld --skip-networking --socket=${MYSQL_UNIX_PORT}"
mysqld --defaults-file=/var/tmp/my.cnf --user=root --socket=${MYSQL_UNIX_PORT} --innodb_log_file_size=48M --skip-networking --datadir=${DATADIR:-/var/lib/mysql} --server-id=0 --skip-log-bin &
mysqld --defaults-file=/var/tmp/my.cnf --user=root --socket=${MYSQL_UNIX_PORT} --innodb_log_file_size=48M --skip-networking --datadir=${DATADIR:-/var/lib/mysql} --server-id=${MYSQL_SERVER_ID:-0} --skip-log-bin &
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one isn't needed either.

echo "Starting mysqld."
tail -f /var/log/mysqld.log ${DATADIR:-/var/lib/mysql}/mysqld.err &
exec mysqld --server-id=0
exec mysqld --server-id=${MYSQL_SERVER_ID:-0}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only one that matters. Of course, needs docs on how it's to be used and why and when.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, will fix first thing tomorrow.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@cyppe cyppe requested a review from a team as a code owner September 10, 2025 03:56
MYSQL_SERVER_ID=1
```

**Note:** Command-line arguments take precedence over configuration files, so you cannot override the server ID through custom `.cnf` files in `.ddev/mysql/`. The environment variable approach is required.
Copy link
Copy Markdown
Collaborator

@nickchomey nickchomey Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I suggest adding something that explains why only server-id needs to have a cli arg that requires being changed by env var, while everything else can be done with cnf? It's still not clear to me and surely causes confusion for anyone who comes across it (including the maintainers, apparently).

The closest I've seen to an explanation is this

MySQL 5.7: server_id default is 0. If binary logging is enabled, you must set a non-zero server_id or the server won’t start; and with server_id=0, a source/replica refuses replication connections.

MySQL 8.0+: default changed to 1 (part of broader default changes). It still must be unique in any replication topology

I don't see why the overridable defaults aren't sufficient.

Even "we don't actually know why this is here, but we're keeping it for safety's sake, so the env var is needed" would be better than nothing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the cleanest solution is probably to remove --server-id=0, but seems like its a riskier operation? I am open for any suggestion, as long as it does not delay the actual possibility to change server id. And as I understand it might be a too risky change right now. While ENV is 100% backward compatible.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My impression was that ddev's extension test suite was meant to allow for making such changes and testing for breakage. If there isn't a test for it, either one should be added (which would require understanding the actual purpose for it) or maybe it wasn't important to begin with

Comment thread docs/content/users/extend/customization-extendibility.md Outdated
@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Sep 10, 2025
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Sep 10, 2025

@cyppe
Copy link
Copy Markdown
Contributor Author

cyppe commented Sep 10, 2025

I have tested the functionality now with the new build, and it works good.

Step 1.
Before adding any environment variables (proves there is no breaking changes). It still starts with: --server-id=0 (pass)

cyppe@generate-db:~$ ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
cyppe          1  2.3  2.4 3212208 686972 ?      Ssl  13:39   0:01 mysqld --server-id=0

Step 2.
Adding .env.db file with

MYSQL_SERVER_ID=1

Step 3.
After ddev restart. It now starts with --server-id=1 (pass)

cyppe@generate-db:~$ ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
cyppe          1  0.6  1.8 3212664 534812 ?      Ssl  13:43   0:00 mysqld --server-id=1

Step 4.
Remove .env.db and create docker-compose.db.yaml instead.

services:
	db:
	  environment:
		- MYSQL_SERVER_ID=2

Step 5.
After ddev restart. It starts with --server-id=2 (pass)

cyppe@generate-db:~$ ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
cyppe          1  2.0  1.7 3212680 498116 ?      Ssl  13:54   0:00 mysqld --server-id=2

Let me know if you want me to test something more.

I will continue to config my environment based on this artifacts, and if there is something useful to share regarding how I use the replication in laravel I will try to share something. Will at least make sure it runs as expected and share some info.

@rfay
Copy link
Copy Markdown
Member

rfay commented Sep 10, 2025

I don't understand what replication has to do with laravel, but am sure interested in your experimentation. Can you say more? Replication should be completely transparent to Laravel, true?

@cyppe
Copy link
Copy Markdown
Contributor Author

cyppe commented Sep 10, 2025

For normal replication as in master -> slave it is of course transparent to laravel.

But in my case laravel runs a long running process (package https://github.com/huangdijia/laravel-trigger) so laravel becomes the slave and listens to all updates in database. So I can define what tables and what type of events is interesting, and then I can dispatch events/jobs in laravel based on "realtime" actions in database.

But can try to show something.. still in process to configure the last bits.

@rfay
Copy link
Copy Markdown
Member

rfay commented Sep 10, 2025

Super interesting! So this really isn't an actual "Laravel" thing, but rather a very specific extra technique. Looking forward to hearing more!

@rfay
Copy link
Copy Markdown
Member

rfay commented Sep 14, 2025

Moved to draft pending manual testing and discussion.

@cyppe
Copy link
Copy Markdown
Contributor Author

cyppe commented Sep 18, 2025

@rfay congrats on latest release! Is it possible to rebase and create new artifacts based on latest version just released? So I can continue testing based on latest version?

@stasadev
Copy link
Copy Markdown
Member

I'll do rebase with push shortly.

@stasadev stasadev force-pushed the feature/mysql-server-id-override branch from 2dd6039 to 8f5e415 Compare September 18, 2025 15:43
@stasadev
Copy link
Copy Markdown
Member

@cyppe, done, run this to pull new images after switching the binary:

ddev debug download-images --all

@rfay
Copy link
Copy Markdown
Member

rfay commented Sep 19, 2025

  • Please update the OP to show current state of the PR
  • Please provide detailed manual testing instructions that preferably don't require setting up replication
  • Please show your manual testing results using some versions of mysql and mariadb

Thank you!

@cyppe
Copy link
Copy Markdown
Contributor Author

cyppe commented Sep 19, 2025

Will do!

A bit busy for a couple of days now, but will be finished during next week for sure.

Will be pretty quick to test, as basically either both mariadb and mysql starts with default value or not. And then it should be possible to override default in the custom cnf file. And then how everyone chooses to use this possibility is probably up to the users. So I agree, we should avoid replication specific test cases as that is more of a possibility enabled by this change. But as replication is in my own interest I will for sure test it and let you know how it works out.

But will report back in a couple of days according to your list.

@cyppe
Copy link
Copy Markdown
Contributor Author

cyppe commented Sep 19, 2025

Took a moment now anyway and did all the relevant tests.

Tested old + new version of both MySQL and MariaDB as we expect old to get default server id = 0, and new versions to get server id = 1. And it works.

And I updated PR description a few days ago, so its up to date as I see it.
Let me know if you spot something that needs to be updated.

UPDATE: Actually found a typo in docs + my PR description example where I wrote server_id in the custom cnf file, but it should be server-id. Updated both now.


MariaDB 11.4

Configuration:

database:
  type: mariadb
  version: 11.4

Test Cases

1. Default Configuration

Query:

SELECT @@server_id;

Expected: 1
Actual: 1

2. Custom Configuration

Configuration file: custom.cnf

[mysqld]
server-id = 2

Query:

SELECT @@server_id;

Expected: 2
Actual: 2


MariaDB 5.5

Configuration:

database:
  type: mariadb
  version: 5.5

Test Cases

1. Default Configuration

Query:

SELECT @@server_id;

Expected: 0
Actual: 0

2. Custom Configuration

Configuration file: custom.cnf

[mysqld]
server-id = 2

Query:

SELECT @@server_id;

Expected: 2
Actual: 2


MySQL 5.7

Configuration:

database:
  type: mysql
  version: 5.7

Test Cases

1. Default Configuration

Query:

SELECT @@server_id;

Expected: 0
Actual: 0

2. Custom Configuration

Configuration file: custom.cnf

[mysqld]
server-id = 2

Query:

SELECT @@server_id;

Expected: 2
Actual: 2


MySQL 8.4

Configuration:

database:
  type: mysql
  version: 8.4

Test Cases

1. Default Configuration

Query:

SELECT @@server_id;

Expected: 1
Actual: 1

2. Custom Configuration

Configuration file: custom.cnf

[mysqld]
server-id = 2

Query:

SELECT @@server_id;

Expected: 2
Actual: 2

@rfay rfay marked this pull request as ready for review September 29, 2025 14:48
@rfay rfay force-pushed the feature/mysql-server-id-override branch from 21c9210 to 43ff377 Compare September 29, 2025 14:48
@rfay
Copy link
Copy Markdown
Member

rfay commented Sep 29, 2025

Rebased and moved to ready to review. Time to test with the suggested techniques of @cyppe . @nickchomey since you're super interested could you do manual testing as well?

@nickchomey
Copy link
Copy Markdown
Collaborator

Gladly! I'll try to check it out sometime today.

Copy link
Copy Markdown
Member

@stasadev stasadev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, thank you for this simplification.

I tested it with MySQL 8.0 and MariaDB 11.8.

I checked the actual value with:

ddev mysql -e 'SELECT @@server_id;'

Now it uses the default when not set (as expected) and updates on modification in .ddev/mysql/*.cnf.

@nickchomey
Copy link
Copy Markdown
Collaborator

I only tried mariadb 11.4 and it is fine for me. I suspect if there might be any issues, they'd be related to old versions of mysql.

Copy link
Copy Markdown
Member

@rfay rfay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Congrats! As far as I can tell this involves no risk at all and will enable people to do some new and interesting things like you're doing.

@rfay rfay merged commit 83bcc49 into ddev:main Sep 30, 2025
28 of 29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

custom my.cnf doesn't load server_id value

4 participants