A software engineer's nighttime workspace with Docker commands and a Drupal and MySQL Docker Compose file on a laptop

Editorial illustration generated for this article.

Setting up a local PHP and CMS development environment directly on your host machine can lead to dependency conflicts, mismatched PHP extensions and services that are difficult to maintain. Docker and Docker Compose solve much of this problem by packaging the web server, PHP runtime and MySQL database into isolated containers.

This guide walks through a local Drupal installation connected to MySQL with Docker Compose.

Prerequisites

Install Docker Desktop or Docker Engine on Linux, Docker Compose, and a terminal. Verify the installation:

docker --version
docker compose version

Step 1: Create the project directory

Create a dedicated directory for the project:

mkdir drupal-docker-local
cd drupal-docker-local

The Compose file below uses named volumes, so Docker manages persistent storage for the database and Drupal files.

Step 2: Create docker-compose.yml

Create docker-compose.yml in the project root:

services:
  database:
    image: mysql:8.0
    container_name: drupal_mysql
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: drupal_db
      MYSQL_USER: drupal_user
      MYSQL_PASSWORD: drupal_password
      MYSQL_ROOT_PASSWORD: root_secret_password
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - drupal_network

  drupal:
    image: drupal:10-apache
    container_name: drupal_web
    restart: unless-stopped
    depends_on:
      - database
    ports:
      - "8080:80"
    volumes:
      - drupal_data:/var/www/html
    networks:
      - drupal_network

volumes:
  drupal_data:
  db_data:

networks:
  drupal_network:
    driver: bridge

The database service runs MySQL 8.0, while drupal runs Drupal with Apache and maps host port 8080 to container port 80. Both services share drupal_network, so they can find each other by service name. Named volumes preserve the database and Drupal files when containers are recreated.

These credentials are suitable only for a local example. Replace them and keep secrets out of source control for a shared or production-like environment.

Step 3: Launch and verify the containers

Start both services in the background:

docker compose up -d
docker compose ps

You should see drupal_web and drupal_mysql running. To inspect MySQL startup:

docker compose logs -f database

Press Ctrl + C to stop following the logs; this does not stop the container.

Step 4: Understand the Docker database hostname

On a native installation, an application often connects to MySQL through localhost or 127.0.0.1. That assumption is wrong from inside the Drupal container: each container has its own network namespace and loopback address.

Because both services share drupal_network, Docker’s internal DNS resolves the MySQL service by its Compose name. Drupal should therefore use:

database

The internal MySQL port remains 3306. The 8080:80 mapping is for your browser, not for Drupal’s database connection.

Step 5: Complete the Drupal installation

Open http://localhost:8080 and follow the installer:

  1. Choose a language and select Save and continue.
  2. Select the Standard installation profile.
  3. Choose MySQL, MariaDB, Percona Server, or equivalent.
  4. Under Advanced Options, enter:
    • Database name: drupal_db
    • Database username: drupal_user
    • Database password: drupal_password
    • Host: database
    • Port: 3306
  5. Select Save and continue so Drupal can create its schema.
  6. Enter the site name, administrator email, username and password.

Do not use localhost or 127.0.0.1 for the database host. After the installer finishes, Drupal redirects you to the new site dashboard.

Useful maintenance commands

# Stop containers without removing data
docker compose stop

# Start stopped containers
docker compose start

# Remove containers but keep named volumes
docker compose down

# Follow Drupal logs
docker compose logs -f drupal

# Open a shell inside the Drupal container
docker compose exec drupal bash

To remove the containers and named volumes, use this only when you intentionally want to delete the local database and Drupal files:

docker compose down -v

Conclusion

Docker Compose gives Drupal and MySQL a repeatable local environment without installing every dependency directly on the host. The most important networking detail is simple: from Drupal, use the Compose service name database, not localhost.

Once the setup works, commit the Compose file and document the required commands for the team. That small investment makes onboarding, troubleshooting and rebuilding the local environment much easier.