Install MySQL With PhpMyAdmin Using Docker
Tags: docker, webdev, beginners, tutorial
Published: 2020-04-11 16:06:39

*This article was originally published a day earlier at https://maximorlov.com/install-mysql-with-phpmyadmin-using-docker/*

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!

**Requirements**
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](https://docs.docker.com/get-docker/) 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:

```console
$ 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.

**Goals**
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.

## Step 1 — Installing MySQL

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](https://hub.docker.com/_/mysql) 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.

```bash
# 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:

```console
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:

```bash
# 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`:

```console
$ 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
```

## Step 2 — Installing phpMyAdmin

Now we're ready to move on to phpMyAdmin. Let's download it from the [phpMyAdmin repository on Docker Hub](https://hub.docker.com/r/phpmyadmin/phpmyadmin). At the moment of writing, 5.0.2 is the latest version. We'll use the `docker pull` command again:

```bash
# Download phpMyAdmin 5.0.2
docker pull phpmyadmin/phpmyadmin:5.0.2
```

After some time, you should see a successful download:

```console
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 `--name` and `-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 `-p <HOST_PORT>:<CONTAINER_PORT>`. 

Let's see all of the above in action:

```bash
# 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`:

```console
$ 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
```

## Step 3 — Creating a Docker network

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:

```bash
# Create a Docker network
docker network create my-network
```

Now, let's connect both MySQL and phpMyAdmin containers to this network.

```bash
# 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.

Navigate to `localhost:8080` in your browser, and log in with username: `root` and password: `my-secret-pw`. You should now see the phpMyAdmin dashboard. **Hooray** 🎉.

## Step 4 — (Bonus) Using Docker Compose

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:

```yaml
# 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, `mysql` and `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:

```bash
# Start the application stack defined in docker-compose.yml
docker-compose up -d
```

Similar to `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:

```bash
# Stop the application stack
docker-compose down
```

## Conclusion

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.

## Become a skilled Node.js developer

Every other Tuesday I send an email with tips on building solid Node.js applications. If you want to grow as a web developer and move your career forward with me, [drop your email here 💌](https://maximorlov.ck.page/0076aaf499).