Correctly launch MySQL on Docker for Windows, avoiding 'Bind on unix socket' error

; Date: May 6, 2020

Tags: Docker »»»» Docker for Windows

Docker is an excellent tool for launching Linux-containerized applications, and it even runs on Windows. But running Docker containers on Windows has a few unexpected rough edges. One will come if you try the default way to launch MySQL doesn't work on Windows. Instead of the expected successful launch you might instead be told Can't start server : Bind on unix socket and asked whether there is another MySQL server running. That misleading error can send you on a tangential wild goose chase.

Because databases are eternal and containers are ephemeral. Therefore, when we launch a Docker container for a database it is required to mount the database data directory from the host file system. That way we can destroy and recreate the container at will, and the data directory will live on.

For example in my book, Node.js Web Development, I recommend this:

docker run --name db-userauth \
    --env MYSQL_USER=userauth \
    --env MYSQL_PASSWORD=userauth \
    --env MYSQL_DATABASE=userauth \
    --mount type=bind,src=`pwd`/userauth-data,dst=/var/lib/mysql \
    --network authnet -p 3306:3306 \
    --env MYSQL_ROOT_PASSWORD=w0rdw0rd \
    mysql/mysql-server:8.0 \
    --bind_address=0.0.0.0

For Windows the src= to the --mount option needs to be a little different, such as a hard-coded pathname. But this should otherwise work unchanged between macOS, Linux, and Windows.

Instead, on Windows we are greeted with this result:

2020-05-06T18:12:35.737416Z 0 [ERROR] [MY-010270] [Server] Can't start server : Bind on unix socket: Input/output error
2020-05-06T18:12:35.737500Z 0 [ERROR] [MY-010258] [Server] Do you already have another mysqld server running on socket: /var/lib/mysql/mysql.sock ?
2020-05-06T18:12:35.737585Z 0 [ERROR] [MY-010119] [Server] Aborting
2020-05-06T18:12:35.877930Z 0 [ERROR] [MY-010946] [Server] Failed to start mysqld daemon. Check mysqld error log.

No, there isn't another MySQL server running, etc. The thing to note is the pathname, /var/lib/mysql/mysql.sock. Pondering that might help you avoid wasting time on irrevelant issues because most of the advice regarding this error message involves detecting the other MySQL daemon on the host.

The problem in this case is not about another MySQL daemon. The generic error messages printed here will mislead you. The problem is that the mysql.sock file got put into /var/lib/mysql, which is mounted into a Windows host system directory that does not support Unix domain sockets.

Let's back up a little and explain this. A Unix domain socket is a socket, a.k.a. an endpoint for communication, that exists solely within a Unix (or macOS or Linux or FreeBSD) system. It acts like a TCP/IP socket, in that software can connect to it and communicate. The advantage is speed, and the fact that the Unix domain socket is not exposed over the network.

MySQL has traditionally used a Unix domain socket for local communication to the database engine. One common security measure is to turn off its TCP/IP socket and rely solely on the Unix domain socket.

A common default place for this socket is /var/run/mysqld/mysql.sock. I am not understanding why the MySQL team is putting the socket into /var/lib/mysql when it is deployed as a Docker container. If the purpose for using a Unix domain socket is security, then shouldn't it be kept safely hidden inside the container? Or maybe the idea is that closely allied Docker containers can use this socket and avoid exposing the MySQL service on a TCP/IP socket?

Let's get back to the command above. It includes this option:

--mount type=bind,src=/path/to/userauth-data,dst=/var/lib/mysql

The idea is to mount a host directory into the database container. As I said earlier, it's so that we can treat the data directory as an immortal thing as we delete/recreate the database container for service or upgrades.

The MySQL documentation contains an (dev.mysql.com) out-of-the-way easy-to-miss page saying this:

If you are bind-mounting on the container's MySQL data directory (see Persisting Data and Configuration Changes for details), you have to set the location of the server socket file with the --socket option to somewhere outside of the MySQL data directory; otherwise, the server will fail to start. This is because the way Docker for Windows handles file mounting does not allow a host file from being bind-mounted on the socket file.

That's simple enough, then. Use the --socket option to change the location of the MySQL Unix domain socket. Indeed, we can modify the above command to this:

PS C:\Users\david> docker run --name db-userauth \
    --env MYSQL_USER=userauth --env MYSQL_PASSWORD=userauth \
    --env MYSQL_DATABASE=userauth \
    --mount type=bind,src=C:\Users\david\userauth-data,dst=/var/lib/mysql \
    --network authnet -p 3306:3306 \
    --env MYSQL_ROOT_PASSWORD=w0rdw0rd \
    mysql/mysql-server:8.0 \
    --socket=/tmp/mysql.sock \
    --bind_address=0.0.0.0
``

The server will now start correctly.

This is unchanged except for the addition of `--socket=/tmp/mysql.sock`.  That is a MySQL daemon option, not a Docker option, and it tells the daemon to use a different location for the Unix domain socket.

About the Author(s)

David Herron : David Herron is a writer and software engineer focusing on the wise use of technology. He is especially interested in clean energy technologies like solar power, wind power, and electric cars. David worked for nearly 30 years in Silicon Valley on software ranging from electronic mail systems, to video streaming, to the Java programming language, and has published several books on Node.js programming and electric vehicles.