Skip to main content
NB

In my day job I have worked on many Drupal or Laravel sites that were set up in a Docker container, but I had never set it up myself. I was recently asked by my wonderful wife to help her with a new feature on her business’ website that would effectively require a CMS backend to be added to the static site so I decided to pick Drupal back up.

When I was searching online for how to do it a lot of the tutorials I came across told me how to use the Drupal image to install Drupal in the container but it was never persistent. Each time the container was brought down all the updates were lost. I finally found a resource that explained it to me here on YouTube, and decided to write it up for my own future reference.

Requirements

  • Docker Desktop
  • Terminal

Setting up docker-compose

Create a new directory for the project. Then create a .env file to hold the database credentials & 2 directories to hold site code: drupal-data & postgres-data. The directory structure should now look like this:

- project_name
  - drupal-data/
  - postgres-data/
  - .env

The .env is a simple file that will hold the database credentials to keep them safe.

# .env
DB_NAME="database_name"
DB_USER="user"
DB_PASSWORD="password"

I also like to create a .env.example file with the values the site requires in a .env file but with the sensitive values all censored. That way any time the project is brought to a new system I can copy & paste the file for a quicker start, but this is optional for the Drupal setup.

# .env.example
DB_NAME="<DO_NOT_COMMIT_THIS_VALUE>"
DB_USER="<DO_NOT_COMMIT_THIS_VALUE>"
DB_PASSWORD="<DO_NOT_COMMIT_THIS_VALUE>"

Next, create a docker-compose.yml file at the root of the directory. The docker-compose.yml describes the containers that will be built and their contents. Fill out the file with the following code:

version: "3.8"
services:
  drupal:
    container_name: drupal
    image: drupal:latest
    ports:
      - "8080:80"
    volumes:
      - ./drupal-data:/opt/drupal
    depends_on:
      - postgres
    restart: always

  postgres:
    container_name: postgres
    image: postgres
    environment:
      POSTGRES_DB: "${DB_NAME}"
      POSTGRES_USER: "${DB_USER}"
      POSTGRES_PASSWORD: "${DB_PASSWORD}"
    volumes:
      - ./postgres-data:/var/lib/postgresql/data
    restart: always

In this file lies the secret to the code persistence I was looking for. In both containers under the volume property I am mapping a local directory on my machine to a directory in the container. The patter is: ”local_machine:container” so for the Drupal container I am mapping the drupal-data directory I created to the /opt/drupal directory in the container.

You can also see I have the Postgres database credentials being loaded in dynamically from the .env file. This keeps the sensitive credentials out of the git repository and allows for different values on different systems.

Installing Drupal

Once the docker-compose.yml file is set up, build/start the containers. Once the containers are running connect to the drupal container and install Drupal using composer.

# Build the containers
docker-compose up -d 

# Connect to the 'drupal' container
docker-compose exec drupal bash

# Install Drupal in the correct location
pwd # Should return '/opt/drupal'
composer create-project drupal/recommended-project .

When Drupal is installed in the /opt/drupal directory in the container the drupal-data directory will also start to fill with files (Persistence !).

It’s important to make sure that the vendor/ directory and .env file are both added to the .gitignore so they are excluded from Git. The core/ directory probably could be ignored as well because it can be downloaded again using Composer.

Next Steps

Now that Drupal is installed in a container with persistence the next step is setting up the site. Whether that’s creating content types, filling out page content, or developing custom modules with custom functionality the changes will now persist when the containers are brought down.

For me this means re-learning everything I had forgotten about Drupal themes and building out content types.