How do I know when my docker mysql container is up and mysql is ready for taking queries?
Solution 1
You can install mysql-client package and use mysqladmin to ping target server. Useful when working with multiple docker container. Combine with sleep and create a simple wait-loop:
while ! mysqladmin ping -h"$DB_HOST" --silent; do
sleep 1
done
Solution 2
This little bash loop waits for mysql to be open, shouldn't require any extra packages to be installed:
until nc -z -v -w30 $CFG_MYSQL_HOST 3306
do
echo "Waiting for database connection..."
# wait for 5 seconds before check again
sleep 5
done
Solution 3
This was more or less mentioned in comments to other answers, but I think it deserves its own entry.
First of all you can run your container in the following manner:
docker run --name mysql --health-cmd='mysqladmin ping --silent' -d mysql
There is also an equivalent in the Dockerfile.
With that command your docker ps
and docker inspect
will show you health status of your container. For mysql in particular this method has the advantage of mysqladmin
being available inside the container, so you do not need to install it on the docker host.
Then you can simply loop in a bash script to wait on the status to become healthy. The following bash script is created by Dennis.
function getContainerHealth {
docker inspect --format "{{.State.Health.Status}}" $1
}
function waitContainer {
while STATUS=$(getContainerHealth $1); [ $STATUS != "healthy" ]; do
if [ $STATUS == "unhealthy" ]; then
echo "Failed!"
exit -1
fi
printf .
lf=$'\n'
sleep 1
done
printf "$lf"
}
Now you can do this in your script:
waitContainer mysql
and your script will wait until the container is up and running. The script will exit if the container becomes unhealthy, which is possible, if for example docker host is out of memory, so that the mysql cannot allocate enough of it for itself.
Solution 4
I've found that using the mysqladmin ping
approach isn't always reliable, especially if you're bringing up a new DB. In that case, even if you're able to ping the server, you might be unable to connect if the user/privilege tables are still being initialized. Instead I do something like the following:
while ! docker exec db-container mysql --user=foo --password=bar -e "SELECT 1" >/dev/null 2>&1; do
sleep 1
done
So far I haven't encountered any problems with this method. I see that something similar was suggested by VinGarcia in a comment to one of the mysqladmin ping
answers.
Solution 5
Some times the problem with the port is that the port could be open, but the database is not ready yet.
Other solutions require that you have installed the mysql o a mysql client in your host machine, but really you already have it inside the Docker container, so I prefer to use something like this:
while ! docker exec mysql mysqladmin --user=root --password=root --host "127.0.0.1" ping --silent &> /dev/null ; do
echo "Waiting for database connection..."
sleep 2
done
Related videos on Youtube
Comments
-
haren almost 2 years
I am deploying a few different docker containers, mysql being the first one. I want to run scripts as soon as database is up and proceed to building other containers. The script has been failing because it was trying to run when the entrypoint script, which sets up mysql (from this official mysql container), was still running.
sudo docker run --name mysql -e MYSQL_ROOT_PASSWORD=MY_ROOT_PASS -p 3306:3306 -d mysql [..] wait for mysql to be ready [..] mysql -h 127.0.0.1 -P 3306 -u root --password=MY_ROOT_PASS < MY_SQL_SCRIPT.sql
Is there a way to wait for a signal of an entrypoiny mysql setup script finishing inside the docker container? Bash sleep seems like a suboptimal solution.
EDIT: Went for a bash script like this. Not the most elegant and kinda brute force but works like a charm. Maybe someone will find that useful.
OUTPUT="Can't connect" while [[ $OUTPUT == *"Can't connect"* ]] do OUTPUT=$(mysql -h $APP_IP -P :$APP_PORT -u yyy --password=xxx < ./my_script.sql 2>&1) done
-
fabrizioM over 9 yearsadd a bash loop to check mysql service status ? I think that is as good as it can get.
-
haren over 9 yearsThanks @fabrizioM. I went with this approach.
-
eexit about 7 yearsthis question has been answered here.
-
-
user2707671 almost 8 yearsNice one. I used it as a 1 liner: until nc -z $CFG_MYSQL_HOST 3306; do sleep 1; echo "Waiting for DB to come up..."; done
-
Nathan Arthur over 7 yearsThis is a beautiful thing. Works great for a Docker healthcheck, too:
docker run --health-cmd='mysqladmin ping --silent' -d mysql
-
ronkot over 7 yearsTo wait unit container is healthy I used script
while [ $(docker inspect --format "{{json .State.Health.Status }}" <container-name>) != "\"healthy\"" ]; do printf "."; sleep 1; done
-
Max over 6 yearsI found this to be the best approach as it depends only on Docker and should hence have less cross-platform issues. Only thing I encountered is that the image entrypoint spins up mysqlserver twice - once bare, and once initialized.
mysqladmin ping
catches the first spin up as well, which is likely what you don't want. In my case, running a dummy query with the schema you expect to be there worked best, i.e. change health command to bemysql -u root -e "use your_schema;"
. -
cweiske about 6 yearsThis does not work here;
mysqladmin ping
succeeds before I can actually use the database - I guess the container is still running is initialization.sql
scripts. -
VinGarcia about 6 yearsThis seems the best and shortest option, however just pinging was not working for me, it still wasn't ready afterwards so I used the command:
mysql -u root -proot -e 'status' &> /dev/null
instead ofmysqladmin ping
-
Quolonel Questions over 5 yearsJust because the port is available does not mean the server is ready to accept connections.
mysqladmin ping
is the correct answer here. -
engin over 5 yearsI didn't have mysqladmin installed in docker image, however, wget also did the job:
while ! wget mysql:3306; do sleep 1 done
-
Jesse Chisholm over 4 yearsThis works if you have a
healthcheck
for your container. But the.State.Health.Status
doesn't exist if you don't. You might have to settle for.State.Status
instead; but that saysrunning
too soon for this OP's needs. -
Andrew Savinykh over 4 years@JesseChisholm huh? What do you think
--health-cmd
is for? -
Luke almost 4 yearsIf you try it on localhost and it doesn't work, try "127.0.0.1". When "localhost" is used, mysqladmin is trying to connect using a socket instead over the default TCP-port 3306.
-
am70 over 3 yearsyou can use
healthcheck
also with docker-compose docs.docker.com/compose/compose-file/#healthcheck -
Michael P over 3 yearsusually containers are as minimal as possible and mysql is not available. @QuolonelQuestions you may be right for some rare cases. The nc solution is precised enough even for a predictable docker environment. I vote for the lightweight nc.
-
YetAnotherDuck about 3 yearsThis is the best solution by far. I would like to add that the container needs to have acces to curl.
RUN apt-get update && apt-get upgrade -y && apt-get install curl -y
-
mrodo over 2 yearsWhen and/or how should a script like this be executed? In the Dockerfile? I don't understand...
-
vincent31337 about 2 yearsMost elegant solution IMO.
-
ddrake12 about 2 yearsThis method was working great for me in scripts until I upgraded to MacOS Monterey (I think). Sadly now it seems not to work
-
Ben Slade almost 2 yearsRe: Database ping successful I think it really means "database server ping successful". The documentation for ping says it's checking if the connection is successful. It makes no difference what database you're connected to