Drupal 9 Docker

In the example command, 'some-drupal' is the name you want to assign to your container and 'drupal' is the tag that will be pulled from repository to build the container (if you didn't pulled it from repository at the beginning with 'docker pull drupal' command). To get the latest version (8) just use 'drupal' as tag. Once the installation is complete, the dsh script will be present in the local directory to start up the docker shell, and you can bring the new Drupal 9 site up with a couple of commands.

My last post talked about how Docker microcontainers speed up the software development workflow. Now it's time to dive into how all this applies to Drupal.

I created a collection of Docker configuration files and scripts to make it easy to run Drupal. If you want to try it out, follow the steps in the README file.

The repository is designed using the microcontainers concept, so that each Drupal site will end up with 3 containers of it's own (Apache, MySQL and Drush containers), which are linked together, to run our application. If you want to serve a new site, you need to create separate containers.

  1. A Drupal docker can also have services like SSH (for drush alias to work) and some important utilities like vim SQL Server: Choose your favourite SQL Service (MySQL or PostGRESQL or SQLite). I'm going to be using a MySQL docker.
  2. Docker run -name MYSQL-NAME -e MYSQLROOTPASSWORD=MYSQL-PASSWORD -d mysql:latest Deploying and Configuring Drupal with Docker. Our next step is to deploy a Drupal Docker container, connect it to your MySQL container, and finish configuring Drupal. Follow these steps. Run the following command.

In theory you could re-use containers for different web applications. However, in practice, Docker containers are resource-cheap and easy to spin up. So it’s less work to run separate containers for separate applications than it is to configure each application to play nice with the other applications running on the same container (e.g.: configuring VirtualHosts and port mappings). Or at least this is what my colleague M Parker believes.

Plus, configuring applications to play nice with each other in the same container kind of violates the “create once, run anywhere” nature of Docker.

How it works

My repository uses the docker-compose program. Docker-compose is controlled with the docker-compose.yml file, which tells Docker which containers to start, how to network them together so they serve Drupal, and how to connect them to the host machine. This means serving the Drupal repository filesystem and mapping a port on the host machine to one of the ports in one of the containers.

A useful tip to remember is that docker-compose ps will tell you the port mappings as shown in the screenshot below. This is useful if you don't map them explicitly to ports on the host machine.


If you've ever tried setting up a bunch of containers manually (without docker-compose), it is worth noting (and not very well documented in the Docker docs, unfortunately) that you don’t need to explicitly map port 3306:3306 for the mysql container, because docker-compose sets up a miniature network for containers run from the same docker-compose.yml. It also sets up hostnames between each container in the same docker-compose.yml. This means that the web container can refer to the mysql-server machine with the hostname mysql-server, and, even if you implicitly map 3306 to some random port on the​ host machine, web can talk to mysql-server on port 3306.

Note in this case that the container running MySQL is named db, so, when you're installing Drupal, on step 4 (“Database configuration”) of the Drupal 7 install script, you have to expand “Advanced options”, and change 'Database host” from localhost to db!


It is possible to put the Drupal filesystem into a container (which you might want to do if you wanted to deploy a container to a public server). However, it doesn't really make sense for development, because most of the time, you're changing the files quite frequently.

To get around this requirement for a development environment, we mount the current folder (often referred to as ‘.’) to /var/www/html in the container, which matches where the current directory is mounted in all three containers. This is done with the 'volumes' directive in the docker-compose.yml file. The ’working_dir’ directive says “when you run the Drush command in the Drush container, pretend it’s running from /var/www/html”, which is the equivalent of ‘cd /var/ww/html’ before you run a drush command.

So when you run the Drush command in the Drush container, it sees that it’s currently in a Drupal directory and proceeds to load the database connection information from sites/default/settings.php which tells it how to connect to the mysql on the db container with the correct credentials. (recall the links directive makes sure that the drush container can access the db container so it can connect to it on port 3306).

The Drush container

The drush container is a bit special because it runs a single command, and is re-created every time a Drush command is used.

If you look at the step 9 of my Docker configuration files you’ll see it says…

  • Run Drush commands with:{% highlight ruby %}USERID=$(id -u) docker-compose run --rm drush $restofdrushcommand{% endhighlight %}

… i.e.: docker-compose run --rm drushi.e. start the container named drush, pass it $rest_of_drush_command

If you look at the Dockerfile for Mparker's Dockerfile, you’ll see it contains a line saying ‘ENTRYPOINT ['drush']’. ENTRYPOINT is a variant of the CMD command which passes all the rest of the ‘docker run’ parameters to the command specified by the ENTRYPOINT line.

So what happens when you run that ‘docker-compose run’ line is that it creates a new container from the ‘mparker17/mush’ image, with all the configuration from the ‘docker-compose.yml’ file. When that container runs, it automatically runs the ‘drush’ command, and docker-compose passes ‘$restofdrush_command’ to the ‘drush’ command. When the ‘drush’ command is finished, the container stops, and the ‘--rm’ thing we specified deletes the container afterwards

Running USER_ID=$(id -u) before a command sets an environment variable that persists for that command; i.e.: when docker-compose runs, an environment variable $USERID exists; but $USERID goes away when docker-compose is finished running. You can leave out the USER_ID=$(id -u) if you add that line to your shell’s configuration. Essentially what that environment variable does is set the user account that the Drush command runs as. If you don’t specify the user account, then Docker defaults to root.

The main reason why I do this is so that if I ask Drush to make changes to the filesystem (e.g.: download a module, run drush make, etc.) that the files are owned by me, not root (i.e.: so I don’t have to go around changing ownership permissions after I run the drush command)

It may only be necessary on Windows/Macintosh, because the virtual machine that Docker runs in on Win/Mac has different user IDs — I think if you run a Docker command from a Linux machine, your user id is already correct; but because a Docker command on a Mac/Win is run with your Mac/Win user ID (e.g.: 501) but gets passed to the docker VM’s ‘docker’ user (which runs as user 1000), some problems arise unless you’re explicit about it.

AcknowledgementsLastly, I would like to thank Matt Parker here, who has been mentoring me since the beginning of setting up docker and telling me better ways to do it. He also recommends reading the Docker book if you want to explore this further.

Drupal VM can be used with Docker instead of or in addition to Vagrant:

  • You can quickly install a Drupal site (any version) using the official geerlingguy/drupal-vm image.
  • You can build a customized local instance using Docker, pulling the official geerlingguy/drupal-vm image.
  • You can 'bake your own' customized Drupal VM Docker image and reuse it or share it with your team.

Docker support is currently experimental, so unless you're already familiar with Docker, it might be best to wait until later versions of Drupal VM are released with more stable support.

Managing your hosts file¶

Before using Docker to run Drupal VM, you should edit your hosts file and add the following line:

(Substitute the IP address and domain name you'd like to use to access your Drupal VM container.)

You can also add other subdomains if you're using other built-in services, e.g. adminer.drupalvm.test, xhprof.drupalvm.com, etc.

If you're using Docker for Mac, you need to perform one additional step to ensure you can access Drupal VM using a unique IP address:

  1. Add an alias IP address on the loopback interface: sudo ifconfig lo0 alias
  2. When you're finished using the container, delete the alias: sudo ifconfig lo0 -alias (or restart your Mac).

You'll have to create the alias again after restarting your Mac. See this Docker (moby) issue for more details.

Method 1: Get a quick Drupal site installed with Drupal VM's Docker image¶

If you just want a quick, easy Drupal site for testing, you can run an instance of Drupal VM and install Drupal inside using the provided script.

  1. Run an instance of Drupal VM: docker run -d -v /sys/fs/cgroup:/sys/fs/cgroup:ro -p 80:80 -p 443:443 --name=drupalvm --privileged geerlingguy/drupal-vm
  2. Install Drupal on this instance: docker exec drupalvm install-drupal (you can choose a version using install-drupal [version], using versions like 9.x-dev or 8.x-dev).

You should be able to access the Drupal site at http://localhost. If you need to share a host directory into the VM, you can do so by adding another -v parameter, like `-v /path/on/host:/path/in/container.

If you only need a container to run your site, and you want to package up the container configuration with your project, you can add a Docker Compose file to your project's docroot like the following:

Then, run docker-compose up -d to bring up the container.

For an example use of the simple approach for a contributed module's local development environment, see the Honeypot module, where this approach was added in Add local test environment configuration.

If you need more flexibility, though, you use one of the other Docker container methods on this page.

Method 2: Build a default Drupal VM instance with Docker¶

The geerlingguy/drupal-vm image on Docker Hub contains a pre-built copy of Drupal VM, with all the latest Drupal VM defaults. If you need to quickly run your site in a container, or don't need to customize any of the components of Drupal VM, you can use this image.

For a reference installation that has configuration for running the local environment on either Vagrant or Docker, see the Drupal VM Live Site Repository.

(Optional) Add a Dockerfile for customization¶

If you need to make small changes to the official drupal-vm image (instead of baking your own fully-custom image), you can create a Dockerfile to make those changes. Mess adventures for mac os. In one site's example, ImageMagick was required for some media handling functionality, and so the following Dockerfile was placed in the project's root directory (alongside the docker-compose.yml file):

You can customize the official image in many other ways, but if you end up doing more than a step or two in a Dockerfile, it's probably a better idea to 'bake your own' Drupal VM Docker image.

Drupal Image

Add a docker-compose.yml file¶

Copy the example.docker-compose.yml file out of Drupal VM (or grab a copy from GitHub here), rename it docker-compose.yml, and place it in your project root.

  • If you are using your own Dockerfile to further customize Drupal VM, comment out the image: drupal-vm line, and uncomment the build: . line (this tells Docker Compose to build a new image based on your own Dockerfile).

For the volume: definition in docker-compose.yml, Drupal VM's default docroot is /var/www/drupalvm/drupal/web, which follows the convention of a typical Drupal project built with Composer. If you don't get your site when you attempt to access Drupal VM, you will either need to modify the volume: definition to match your project's structure, or use a custom Dockerfile and copy in a customized Apache vhosts.conf file.

You should also add a volume for MySQL data, otherwise MySQL may not start up correctly. By default, you should have a volume for /var/lib/mysql (no need to sync it locally). See Drupal VM's example docker-compose file for reference.

Run Drupal VM¶

Drupal 9 Docker Free

Run the command docker-compose up -d (the -d tells docker-compose to start the containers and run in the background).

This command takes the instructions in the Docker Compose file and does two things:

  1. Creates a custom Docker network that exposes Drupal VM on the IP address you have configured in docker-compose.yml (by default,
  2. Runs Drupal VM using the configuration in docker-compose.yml.

After the Drupal VM container is running, you should be able to see the Dashboard page at the VM's IP address (e.g., and you should be able to access your site at the hostname you have configured in your hosts file (e.g. http://drupalvm.test/).

Note: If you see Drupal's installer appear when accessing the site, that means the codebase was found, but either the database connection details are not in your local site configuration, or they are, but you don't have the default database populated yet. You may need to load in the database either via drush sql-sync or by importing a dump into the container. The default credentials are drupal and drupal for username and password, and drupal for the database name.

You can stop the container with docker-compose stop (and start it again with docker-compose start), or remove all the configuration with docker-compose down (warning: this will also wipe out the database and other local container modifications).

Using Drush inside Docker¶

Currently, the easiest way to use Drupal VM's drush inside a Docker container is to use docker exec to run drush internally. There are a few other ways you can try to get Drush working with a codebase running on a container, but the easiest way is to run a command like:

Method 3: 'Bake and Share' a custom Drupal VM Docker image¶

Drupal 9 Docker Download

If you need a more customized Drupal VM instance, it's best to build your own with Drupal VM's built-in Docker scripts.

Building ('baking') a Docker container with Drupal VM¶

After you've configured your Drupal VM settings in config.yml and other configuration files, run the following command to create and provision a new Docker container:

This will bake a Docker images using Drupal VM's default settings for distro, IP address, hostname, etc. You can override these options (all are listed in the provisioning/docker/bake.sh file) by prepending them to the composer command:

This process can take some time (it should take a similar amount of time as it takes to build Drupal VM normally, using Vagrant and VirtualBox), and at the end, you should see a message like:

Once the build is complete, you can view the dashboard by visiting the URL provided.

Saving the Docker container to an image¶

If you are happy with the way the container was built, you can run the following command to convert the container into an image:

You can override the default values for the image creation by overriding the following three variables inside config.yml:

Using the default settings, this command will tag your current version of the container as drupal-vm:latest (on your local computer), then store an archive of the image in an archive file, in the path 'docker_image_path/docker_image_name.tar.gz'.

Loading the Docker container from an image¶


On someone else's computer (or your own, if you have deleted the existing drupal-vm image), you can load an archived image by placing it in the path defined by 'docker_image_path/docker_image_name.tar.gz' in your config.yml file. To do this, run the command:

Using a baked Drupal VM image with docker-compose.yml

Drupal VM includes an example.docker-compose.yml file. To use the file, copy it to docker-compose.yml and customize as you see fit, making sure to change the image to the value of docker_image_name (the default is drupal-vm). Once you've configured the exposed ports and settings as you like, run the following command to bring up the network and container(s) according to the compose file:

(The -d tells docker-compose to start the containers and run in the background.) You can stop the container with docker-compose stop (and start it again with docker-compose start), or remove all the configuration with docker-compose down (warning: this will also wipe out the database and other local container modifications).