Containerizing Drupal CMS

26 September 2025

Why Drupal CMS?

While Drupal Core provides a powerful, flexible canvas, starting with a more feature-rich base like the Drupal CMS distribution can significantly accelerate development. It comes pre-packaged with essential modules and configurations for common needs like advanced SEO, content workflows, and media management. While an official image for Drupal Core is readily available on Docker Hub, one for Drupal CMS was not, which prompted me to build my own.

Step 1: Building a Reproducible Docker Image

Previously, my workflow involved manually creating the Drupal CMS project locally with DDEV and then having Docker copy the finished code. While functional, this approach is not ideal for automation, as it relies on the local files being in a perfect state.

To create a more robust and portable solution, I rewrote the Dockerfile to handle the entire build process internally. This is a best practice for CI/CD pipelines, as it ensures a consistent result every time, regardless of the machine it's built on.

The new Dockerfile (which you can find in the drupal-cms-docker GitHub repo) now uses a multi-stage build:

  1. Builder Stage: This first stage starts with a PHP base image and installs Composer. It then runs the composer create-project drupal/cms . command inside the container. This downloads the Drupal CMS project template and all its dependencies into a temporary build environment. Any additional modules, like drush/drush or drupal/samlauth, are also added here with composer require.
  2. Final Stage: The second stage builds the final production image. It starts with a clean PHP-FPM image, installs Nginx, and copies the fully-built application code from the builder stage.

This approach produces a self-contained image with a single command, removing the need for any manual setup steps. The final image is then pushed to a container registry.

docker buildx build --platform linux/amd64,linux/arm64 -t your-username/drupal-cms:latest --push .

Step 2: Deploying with Terraform to Azure Container Apps

With the Drupal CMS image available in a registry, the final step was to deploy it using my drupal-aca Terraform project. Because the project was designed with flexibility in mind, mostly only minor adjustments were needed to accommodate the new image.

The main differences between my CMS image and the Core image I'd previously used were some of the paths used, so the init_container and the mount points in my terraform code needed conditionals to ensure the right paths were being used.

Conclusion

By moving the application build process entirely inside the Dockerfile, we create a more resilient and automated workflow. This removes the dependency on a local development environment for creating the final deployment artifact, which is a key step toward a true GitOps or CI/CD approach for managing a web application.