Simple Drupal Docker Development Environment
Using Docker for your development environment provides ease of setup, consistency among developers, and, most of all, environment parity.
Using Docker as part of your development environment is a smart and progressive move. It lets you, and other developers, easily and consistently setup a local development environment anytime without the hassle of going through multiple steps and documentation, and most of all, provides a high degree of Environment Parity.
I found a lot of great examples on how to leverage Docker for Drupal development, some of which I've used personally, so much so that I've decided to try and come up with the simplest and most straightforward setup I can. The idea is to reuse and extend existing solutions while having minimal custom code as much as possible. In doing so, I hope to provide a simple starting point for myself, and hopefully others as well, to build future projects on.
If you prefer to jump into the code, check out an example Drupal project I created that uses this approach.
Composer template for Drupal projects
This setup assumes that the Composer template for Drupal will be used. The template provides a starter kit for managing your site dependencies with Composer. It also comes with sensible default packages, structure, and tools, such as drush and Drupal console.
For this setup, we'll only need one
Dockerfile, which will be used to build the main image for our Drupal container.
FROM drupal:latest # Install MySQL client so we can use drush from inside the container, among other things. RUN apt-get update && apt-get install -y mariadb-client git # Install Composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer # Make a minor adjustment to the DocumentRoot so that it points to where our application codes are. RUN sed -i 's/DocumentRoot \/var\/www\/html/DocumentRoot \/var\/www\/html\/web/g' /etc/apache2/sites-available/000-default.conf # Mount point for the source codes. VOLUME /var/www/html
As I've mentioned earlier, I aim to reuse existing solutions where possible so where better to start than to use the official Docker images for Drupal. This image installs the basic Drupal requirements as well as apply some recommended settings, which means less code for us too.
In summary, this
Dockerfile does the following:
Use the latest official Docker image for Drupal.
mariadb-clientso that we can connect to the database using the command line from inside the container. This also allows us to use
drushcommands properly from inside the container.
Finally, since we're using the Composer template, Drupal will be located under the
websub-directory so we'll need to make a minor adjustment to the default
DocumentRootthat comes with most Debian-based OS.
Now that we have our basic
Dockerfile to define our Drupal container, we then need to define other services that we'll need for our application, such as a database service, by sourcing other containers.
If you haven't come across, Docker Compose basically allows you to piece together multiple containers that work together to form your application. It does so via a YAML file where we define and configure the necessary services that our application needs.
For our setup, our Compose file, named
docker-compose.yml, looks like this:
version: '3.3' services: # The database service. db: image: mariadb:latest volumes: - ./data:/var/lib/mysql restart: always environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: drupal MYSQL_USER: drupal MYSQL_PASSWORD: drupal # The Drupal service. drupal: build: . depends_on: - db ports: - "8080:80" volumes: - .:/var/www/html restart: always environment: MYSQL_DATABASE: drupal MYSQL_USER: drupal MYSQL_PASSWORD: drupal MYSQL_HOST: db
From the code, you might have guessed that we have 2 services - one that serves our application(
drupal) and a database service (
The database service
In the spirit of reusing existing solutions where possible, we use the latest official MariaDB Docker image instead of creating a custom image of the database ourselves. Furthermore, the image already allows us to set certain settings necessary for our application, such as the database credentials, without the need to further extend and customize the image.
Another option worth noting is the
volumes option. This lets us persist the database by mounting a path located in the host machine into the container. If the container is destroyed or rebuilt, our database is safe since it resides in the host machine. For this setup, our database files will be located in the
data sub-directory of the application root.
The Drupal service
Instead of using an existing image, like what we did with the database service, we want to use our own
Dockerfile to build the Drupal container, which we've defined earlier.
You may have also noticed that we defined the
environment option in a similar way as what we did in the database service. Being able to set our containers' environment variables is one of the best benefits of using Docker. This allows us to pass the same database credentials we used in our database service into our Drupal service. We can then change the database variable initialization in the
<?php $databases['default']['default'] = [ 'database' => getenv('MYSQL_DATABASE'), 'username' => getenv('MYSQL_USER'), 'password' => getenv('MYSQL_PASSWORD'), 'host' => getenv('MYSQL_HOST'), 'prefix' => '', 'port' => '3306', 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', 'driver' => 'mysql', ];
In most Drupal projects, you wouldn't want to commit the
settings.php file since this file will contain sensitive information such as your database credentials along with other non-sensitive settings. By using environment variables and stripping away only the sensitive parts and replacing them with environment variables, you can now safely commit this file and be assured that the rest of your team will have the same settings.
We've used other options in our Composer file such as
restart. Some may look self-explanatory but you can find out more about them from the Compose file reference.
You might be curious how can containers communicate with each other? In our case, how can the Drupal service access the database service? According to the docs:
By default Compose sets up a single network for your app. Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name.
It means that we can use the service names we defined in the Compose file as a hostname. In our
drupal service, we pass the hostname
db using the environment variable
MYSQL_HOST, which is then used in the
Setting up a local environment
With our Docker configurations out of the way, any developer can spin up a local development environment with just 2 steps!
1. Run the app
$ docker-compose up
This command will build and launch your application and database containers.
2. Install Drupal
This step only needs to be done once. Once you've installed Drupal, the database will persist even when the containers are destroyed or rebuilt since the database files are located in the host machine in the
SSH into the Drupal container
First you'll need to know the name of the Drupal container. You can do so by running
docker ps command. The name of the container will be indicated under the
NAMES column. You'll see an output similar to the one below:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b94ec144233a drupal-docker_drupal "docker-php-entrypoi…" 2 hours ago Up 2 hours 0.0.0.0:8080->80/tcp drupal-docker_drupal_1 d384c1b920ab mariadb:latest "docker-entrypoint.s…" 6 hours ago Up 2 hours 3306/tcp drupal-docker_db_1
In the example output above, the name of the Drupal container is
drupal-docker_drupal_1. So to SSH into the Drupal container, you'll need to run
docker exec -it drupal-docker_drupal_1 /bin/bash.
Once inside the container, you should immediately see yourself in the
/var/www/html directory. From here you can either do a fresh install or import a database dump.
Install dependencies via Composer
First of all, install all dependencies via
composer by running:
Fresh install via Drush
To do a fresh install, we can use
drush by running:
./vendor/bin/drush si standard --site-name="My Drupal 8 site"
Once done, you'll see an output similar to:
root@b94ec144233a:/var/www/html# ./vendor/bin/drush si standard --site-name="My Drupal 8 site" You are about to DROP all tables in your 'drupal' database. Do you want to continue? (yes/no) [yes]: > [notice] Starting Drupal installation. This takes a while. [success] Installation complete. User name: admin User password: kaEzmrBwAV
Importing a database dump
To import a database dump, you can run:
./vendor/bin/drush sql-cli < mysql-dump.sql
Finally, to access your local site, go to http://localhost:8080.
Gone are the days when a developer has to go through multiple lines of documentation and steps in order to set up a local development environment. In some cases, this can even take a full day. With the options we have today, such as Docker, setting up a local development environment has never been easier.
My original aim was a bare minimum setup that can be easily changed to suite the needs of a project and, so far, this is what I can come up with. With this setup, I hope that one can easily setup a local Drupal development environment anytime.
While I liked working with Docker, another viable option that I've used before and liked is DrupalVM, which uses Vagrant. I plan to write about this approach too so stay tuned!