Do these errors look familiar to you?
MySQLInterfaceError: Can't connect to MySQL server on 'mysql'
mysqli::real_connect(): (HY000/2002): php_network_getaddresses: getaddrinfo failed: Name or service not known
Frustrating, I know.
Why is it so difficult to simply connect to a MySQL database? You could fix it if you delve deep into networking, but who has time for that?
Or maybe you just want to quickly install MySQL, without bloating your machine, and continue working on your application.
By the end of this tutorial, you will have clean installs of MySQL and phpMyAdmin that are configured properly to just work. You'll accomplish this with minimal effort using a single command to start the entire application stack.
Let's get started!
You don't need a lot of knowledge or tools to be able to follow along with this tutorial. What you'll need is:
- Docker installed on your machine. The official docs have an installation guide for macOS, Windows and Linux operating systems.
- (Optional) Docker Compose. It comes bundled with Docker Desktop.
Verify you have both installed by typing
docker -v and
docker-compose -v in your terminal. You should see a similar output:
$ docker -v Docker version 19.03.8, build afacb8b $ docker-compose -v docker-compose version 1.25.4, build 8d51620a
Basic experience using the Docker command line is useful, but not required. We'll go over each step in detail and explain what we're doing.
Without phpMyAdmin or MySQL preinstalled, our goal is to quickly have a local phpMyAdmin webserver running that's connected to a MySQL database. We will download and run both through Docker, and configure them so they can communicate properly.
As a bonus, you will see how to accomplish this with just one file and a single command using Docker Compose.
Let's start by installing the MySQL database first. We need to have it up & running before we can connect with phpMyAdmin.
We'll grab the image from the official MySQL repository on Docker Hub using
docker pull command. As of writing, the latest version of MySQL is 8.0.19. If you want to use MySQL 5, that will work as well.
# Download MySQL 8.0.19 image from Docker Hub docker pull mysql:8.0.19
You should see several progress bars and after a while it should have finished successfully:
Digest: sha256:b69d0b62d02ee1eba8c7aeb32eba1bb678b6cfa4ccfb211a5d7931c7755dc4a8 Status: Downloaded newer image for mysql:8.0.19 docker.io/library/mysql:8.0.19
With the MySQL image downloaded, let's run a container from it with the
docker run command. We'll give the container a name with the
--name flag and have it run in the background with the
-d flag, so we can keep using the same terminal window for subsequent commands.
MySQL requires us to provide a password for the
root database user to start the database. We can do this by assigning the password to the
MYSQL_ROOT_PASSWORD environment variable which we'll pass on to the container. Docker allows you to pass environment variables with the
-e flag. Our command to start MySQL is:
# Start a MySQL container docker run --name mysql -d -e MYSQL_ROOT_PASSWORD=my-secret-pw mysql:8.0.19
Run this command, and you should see a random and long string displayed. That's the unique ID assigned to the container by Docker. It means MySQL is successfully up and running.
To verify this, we can list all running containers with
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4e61b2cba425 mysql:8.0.19 "docker-entrypoint.s…" 3 seconds ago Up 3 seconds 3306/tcp, 33060/tcp mysql
Now we're ready to move on to phpMyAdmin. Let's download it from the phpMyAdmin repository on Docker Hub. At the moment of writing, 5.0.2 is the latest version. We'll use the
docker pull command again:
# Download phpMyAdmin 5.0.2 docker pull phpmyadmin/phpmyadmin:5.0.2
After some time, you should see a successful download:
Digest: sha256:46dfe47ca8d3a172e7a203049bd33f516b179dc4a7205a88a97ba0bf9fc94c11 Status: Downloaded newer image for phpmyadmin/phpmyadmin:5.0.2 docker.io/phpmyadmin/phpmyadmin:5.0.2
Similarly to MySQL, we run this container with the
-d flags. We give it a different name because container names have to be unique.
phpMyAdmin uses the
PMA_HOST environment variable to know where to connect to the MySQL database. We set this variable to be equal to the MySQL container name, namely
mysql. I explain why in the next section, where we will create a Docker network.
Moreover, we're going to map port 8080 on the host to port 80 inside the container. The host is simply the operating system where you've installed Docker. Mapping a port from the host to the container means that when we go to
localhost:8080 (in the browser for example), we want that to point to port 80 inside the phpMyAdmin container. To map a port, we use the
-p flag, and the syntax is
Let's see all of the above in action:
# Start a phpMyAdmin container docker run --name phpmyadmin -d -p 8080:80 -e PMA_HOST=mysql phpmyadmin/phpmyadmin:5.0.2
We use port 80 because that's the port the phpMyAdmin web server is listening on for incoming requests. Port 8080 on the host is chosen randomly and doesn't have a particular meaning. We could've used any available port on the host.
Let's verify both containers are running side to side with
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8980917d10d0 phpmyadmin/phpmyadmin:5.0.2 "/docker-entrypoint.…" 38 minutes ago Up 38 minutes 0.0.0.0:8080->80/tcp phpmyadmin 4e61b2cba425 mysql:8.0.19 "docker-entrypoint.s…" 2 hours ago Up 2 hours 3306/tcp, 33060/tcp mysql
There is one last thing left for us to do before we can navigate to the phpMyAdmin dashboard. At this point, phpMyAdmin can't connect to MySQL because the
mysql hostname we used in the previous step doesn't translate to the IP address of the MySQL container.
Translating hostnames to IP addresses is called DNS resolution. Fortunately, Docker provides DNS resolution out of the box when we create a network. All containers inside that network will be able to communicate with each other using their names. Let's create a network:
# Create a Docker network docker network create my-network
Now, let's connect both MySQL and phpMyAdmin containers to this network.
# Connect MySQL and phpMyAdmin containers to our network docker network connect my-network mysql docker network connect my-network phpmyadmin
You won't see anything printed out in the console, which is good. It means both containers are now part of our network and can communicate with each other using their names.
localhost:8080 in your browser, and log in with username:
root and password:
my-secret-pw. You should now be inside the phpMyAdmin dashboard. Hooray 🎉.
Docker Compose is a tool for defining and running multi-container Docker applications. With Compose, you use one file to configure all of your application's containers. Then, with a single command, you create and start all containers from your configuration. In our case, the application stack is two containers and a network that connects them.
The naming convention for the configuration file is
docker-compose.yml. Inside the file, containers are defined as
services. Let's combine all the steps from this tutorial and put them in a single file:
# We're using version 3.7 of the Docker Compose file format version: "3.7" # Define services/containers services: # MySQL container mysql: # Use mysql:8.0.19 image image: mysql:8.0.19 # Connect to "my-network" network, as defined below networks: - my-network # Pass a list of environment variables to the container environment: MYSQL_ROOT_PASSWORD: my-secret-pw # phpMyAdmin container phpmyadmin: # Use phpmyadmin/phpmyadmin:5.0.2 image image: phpmyadmin/phpmyadmin:5.0.2 # Connect to "my-network" network, as defined below networks: - my-network # Map port 8080 on the host to port 80 inside the container # Syntax is: "HOST_PORT:CONTAINER_PORT" ports: - "8080:80" # Pass a list of environment variables to the container environment: PMA_HOST: mysql # Wait for "mysql" container to start first depends_on: - mysql # Define networks networks: my-network:
When you pay close attention to each line, you can see how this translates to the commands we ran in previous steps. We define two containers,
phpmyadmin. For each container, we use a combination of keys and values instead of flags to configure them.
You might've noticed a configuration key we haven't used in our commands before:
depends_on. With this, we tell Docker Compose that phpMyAdmin relies on MySQL and it should start MySQL container first. This chronological startup sequence prevents connection problems from phpMyAdmin to a MySQL database that hasn't finished initialising yet.
To start the application stack, in the same folder as the
docker-compose.yml file, run:
# Start the application stack defined in docker-compose.yml docker-compose up -d
docker run, the
-d flag lets us run our application stack in the background. The command to stop and remove all containers and networks is:
# Stop the application stack docker-compose down
We ended up with a setup that has MySQL and phpMyAdmin running inside different containers connected through a shared network. Additionally, Docker Compose allows us to quickly start the entire application stack by using just a single command.