I have a very simple docker-compose file like the one below.
version: '3.8'
services:
mysql:
image: mysql:latest
container_name: mysql
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_ROOT_PASSWORD: 'hello'
MYSQL_USER: myuser
MYSQL_PASSWORD: 'hello'
ports:
- 3306:3306
volumes:
- mysql-data:/var/lib/mysql
# - new-mysql-data:/var/lib/mysql
volumes:
mysql-data:
new-mysql-data:
As you can see in the file, it is set to MYSQL_ROOT_PASSWORD: 'hello'
.
I'm mapping this file to the mysql-data
volume. After creating mysql with this file, changing the
MYSQL_ROOT_PASSWORD
Changing it to password
does not take effect.
I did some research and found an article on the official mysql image on docker hub.
Environment Variables When you start the mysql image, you can adjust the configuration of the MySQL instance by passing one or more environment variables on the docker run command line. Do note that none of the variables below will have any effect if you start the container with a data directory that already contains a database: any pre-existing database will always be left untouched on container startup.
This seems to imply that once a container has been attached to a volume and created, changes to environment variables will not be applied.
So I ran a test where I mapped a new volume and let it run.
version: '3.8'
services:
mysql:
image: mysql:latest
container_name: mysql
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_ROOT_PASSWORD: 'password' # changed.
MYSQL_USER: myuser
MYSQL_PASSWORD: 'password' # changed.
ports:
- 3306:3306
volumes:
# - mysql-data:/var/lib/mysql
- new-mysql-data:/var/lib/mysql # changed.
volumes:
mysql-data:
new-mysql-data:
The result is that MYSQL_ROOT_PASSWORD: 'password'
was applied correctly.
in this situation, if I change volume from the new-mysql-data
to the mysql-data
, the MYSQL_ROOT_PASSWORD
that applies then will be the hello
that we registered before this.
The conclusion I came to was this: mysql's environment variables are stored on a volume.
Am I correct in my conclusion? If environment variables are stored in a volume, there may come a situation where you have to remove the volume to change the environment variables. I don't think this is efficient, and I'm confused if I'm understanding this correctly.
Are mysql's environment variables stored on a volume?
Thank you.
At a technical level, no. Volumes only store files, and environment variables are in-memory-only properties of processes.
The other important technical detail is that MySQL-the-database-server doesn't take settings like credentials from environment variables either. The actual database password is in the opaque database storage, and you need to use administrative tools like mysqladmin
or run an SQL ALTER USER
statement to set or change it. This can be done from outside Docker space by any user who can connect to the database and has the right privileges; even without restarting the database, the database credentials will not necessarily match what's in the environment variables.
So: the database credentials are stored in the on-disk database data. That data is in the volume.
The environment variables are used by a Docker-specific initialization script that only runs the first time the database is created. The script just checks to see if the database files exist, and if not, it runs its first-time initialization scripts. These include some SQL CREATE
statements derived from environment variables and the /docker-entrypoint-initdb.d
directory.
You could simulate this (without Docker) using a shell script. The actual value you're checking for comes from a file, and the file is initialized from environment variables, but only if it doesn't exist yet.
#!/bin/sh
# Create the settings file if it doesn't exist
if [ ! -f settings.txt ]; then
if [ "$SETTING" ]; then echo "SETTING=$SETTING" >> settings.txt
fi
# Getting the actual setting from the file and not the environment variable
grep SETTING settings.txt
If you run this once SETTING=foo ./get-setting
it will print out foo
as you expect, but changing the environment variable won't change the output, only editing the on-disk file. In Docker, that persistent file would need to be stored in a volume.
... a situation where you have to remove the volume to change the environment variables
If you want to change a database user's password, use an SQL ALTER USER
statement, the same way you would with any other database, Docker or otherwise. You don't need to delete anything in particular.
These same "only the first time" rules apply to the /docker-entrypoint-initdb.d
initialization scripts. If you need to, say, create a database table, again, you need to use an SQL CREATE TABLE
statement. You should generally do this via your application's database-migration framework, which won't be tied to Docker and can be re-run without destroying the database.
Yes, it is possible to change the database user's password by destroying the database and re-running its initialization script, but this loses absolutely all of the data in the database. You'll see recommendations to do this out there, and with that caveat it does in fact work. But it's not necessary, and you can use normal database tools to do normal database administration without doing anything Docker-specific.