Skip to content

Local environment with Docker4Drupal

Docker4Drupal is an open-source project (GitHub page) that provides pre-configured docker-compose.yml file with images to spin up local environment on Linux, Mac OS X and Windows.



Database data persistence

By default Docker will create a persistent volume for your DB data and unless you explicitly remove volumes the files will not be deleted. However, if you run docker-compose down (it's ok to use stop though) these volumes will not be reattached when you run docker-compose up. If you want to have your DB data all-time persistent and attached, we recommend using a bind mount. To use a bind mount uncomment to corresponding line under db server's volumes: in your docker-compose.yml and update the host path to your data directory.

There are 2 options how to use docker4drupal – you can either run vanilla Drupal from the image or mount your own Drupal codebase:

Vanilla Drupal

  1. Clone docker4drupal repository and switch to the latest stable tag or download/unpack the source code from the latest release
  2. Optional: for Drupal 7 or 6 comment out corresponding DRUPAL_TAG and NGINX_TAG in .env file
  3. Configure domains
  4. From project root directory run docker-compose up -d or make up to start containers. Give it 10-20 seconds to initialize after the start
  5. That's it! Proceed with Drupal installation at http://drupal.docker.localhost:8000. Default database user, password and database name are all drupal, database host is mariadb
  6. You can see status of your containers and their logs via portainer: http://portainer.drupal.docker.localhost:8000

Mount my codebase

  1. If you're starting a new project we recommend you to fork drupal-composer/drupal-project project
  2. Download and unpack docker4drupal.tar.gz from the latest stable release to your project root
  3. Delete docker-compose.override.yml as it's used to deploy vanilla Drupal
  4. Ensure NGINX_SERVER_ROOT (or APACHE_DOCUMENT_ROOT) is correct, by default set to /var/www/html/web for composer-based projects where Drupal is in web subdirectory
  5. Ensure database access settings in your settings.php corresponds to values in .env file, e.g.:
    $databases['default']['default'] = array (
      'database' => 'drupal', // same as $DB_NAME
      'username' => 'drupal', // same as $DB_USER
      'password' => 'drupal', // same as $DB_PASSWORD
      'host' => 'mariadb', // same as $DB_HOST
      'driver' => 'mysql',  // same as $DB_DRIVER
      'port' => '3306', // different for PostgreSQL
      'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', // different for PostgreSQL
      'prefix' => '',
  6. Configure domains
  7. Optional: for Drupal 7 or 6 update NGINX_VHOST_PRESET correspondingly in your .env file
  8. Optional: uncomment lines in the compose file to run redis, solr, varnish, etc
  9. Optional: import existing database
  10. Optional: macOS users please read this
  11. Optional: Windows users please read this
  12. Run containers: make up or docker-compose up -d
  13. Your drupal website should be up and running at http://drupal.docker.localhost:8000
  14. You can see status of your containers and their logs via portainer: http://portainer.drupal.docker.localhost:8000

You can stop containers by executing make stop or docker-compose stop.

Optional files

If you don't need to run multiple projects and don't use docker-sync to improve volumes performance on macOS feel free to delete traefik.yml and docker-sync.yml that come with the docker4drupal.tar.gz

Get updates

We release updates to images from time to time, you can find detailed changelog and update instructions on GitHub under releases page


Traefik container used for routing. By default, we use port 8000 to avoid potential conflicts but if port 80 is free on your host machine just replace traefik's ports definition in the compose file.

By default BASE_URL set to drupal.docker.localhost, you can change it in .env file.

Add drupal.docker.localhost to your /etc/hosts file (some browsers like Chrome may work without it). Do the same for other default domains you might need from listed below:

Service Domain
nginx/apache http://drupal.docker.localhost:8000
pma http://pma.drupal.docker.localhost:8000
adminer http://adminer.drupal.docker.localhost:8000
mailhog http://mailhog.drupal.docker.localhost:8000
solr http://solr.drupal.docker.localhost:8000
nodejs http://nodejs.drupal.docker.localhost:8000
node http://front.drupal.docker.localhost:8000
varnish http://varnish.drupal.docker.localhost:8000
portainer http://portainer.drupal.docker.localhost:8000
webgrind http://webgrind.drupal.docker.localhost:8000


Xdebug troubleshooting

Enable xdebug logs to get more information by adding $PHP_XDEBUG_REMOTE_LOG=/tmp/php-xdebug.log environment variable to PHP container

Debugging web requests

  1. Uncomment these lines for PHP service in your docker-compose file
    PHP_XDEBUG: 1                 
  2. Restart containers (make)
  3. Start debugging in IDE
  4. Start your browser debug helper plugin (Chrome or Firefox) and open the page you want to debug. Alternatively, enable auto start by adding PHP_XDEBUG_REMOTE_AUTOSTART=1

Debugging CLI requests

  1. Enable Xdebug as described in the previous section
  2. Uncomment the following environment variables for PHP service in your composer file
    PHP_IDE_CONFIG: serverName=my-ide
  3. Configure your IDE
  4. Perform configuration as described below depending on your OS and Docker version:

Linux, Docker

  1. Uncomment PHP_XDEBUG_REMOTE_HOST: for PHP service
  2. Restart containers (make)

macOS, Docker

  1. Uncomment PHP_XDEBUG_REMOTE_HOST: host.docker.internal for PHP service (Docker 18.03+)
  2. Restart containers (make)


  1. Uncomment PHP_XDEBUG_REMOTE_HOST: host.docker.internal for PHP service (Docker 18.03+)
  2. Restart containers (make)
  3. Allow listen connection for your IDE in Windows Firewall > Allow an app ..

Also, you might need to update your hosts file.

IDE configuration

You must additionally configure your IDE to debug CLI requests.


  1. Open Run > Edit Configurations from the main menu, choose Defaults > PHP Web Page in the left sidebar
  2. Click to [...] to the right of Server and add a new server
    • Enter name my-ide (as specified in PHP_IDE_CONFIG)
    • Enter any host, it does not matter
    • Check Use path mappings, select path to your project and enter /var/www/html in the right column (Absolute path on the server)
  3. Choose newly created server in "Server" for PHP Web Page
  4. Save settings

Database import and export


Known issues with indexes rebuild

Issues have been reported when MariaDB does not build indexes when dump imported using mariadb-init bind mount. For safety use the workaround described at

if you want to import your database, uncomment the line for mariadb-init bind mount in your compose file. Create the directory ./mariadb-init in the same directory as the compose file and put there your .sql .sql.gz .sh file(s). All SQL files will be automatically imported once MariaDB container has started.

Exporting all databases:

docker-compose exec mariadb sh -c 'exec mysqldump --all-databases -uroot -p"root-password"' > databases.sql

Exporting a specific database:

docker-compose exec mariadb sh -c 'exec mysqldump -uroot -p"root-password" my-db' > my-db.sql


if you want to import your database, uncomment the line for postgres-init volume in your compose file. Create the volume directory ./postgres-init in the same directory as the compose file and put there your .sql .sql.gz .sh file(s). All SQL files will be automatically imported once Postgres container has started.

Make commands


We provide Makefile that contains commands to simplify the work with your local environment. You can run make [COMMAND] to execute the following commands:

Usage: make COMMAND

    help            List available commands and their description 
    up              Start up all container from the current docker-compose.yml 
    start           Start stopped containers 
    stop            Stop all containers for the current docker-compose.yml (docker-compose stop) 
    down            Same as stop
    prune [service] Stop and remove containers, networks, images, and volumes (docker-compose down)
    ps              List container for the current project (docker ps with filter by name)
    shell           Access PHP container via shell as a default user  (docker exec -ti $CID sh)
    logs [service]  Show containers logs, use [service] to show logs of specific service


make drush [command] Execute drush command (runs with -r /var/www/html/web, you can override it via DRUPAL_ROOT=PATH)

Docker for mac

There two major problems macOS users face with when using Docker for mac:

macOS permissions issues

To avoid any permissions issues caused by different user id (uid), group id (gid) between your host and a container use -dev-macos version of php image (uncomment the environment variables in .env files) where the default user wodby has 501:20 uid/gid that matches default macOS user.

Bind mounts performance

Out of the box Docker for mac bind mounts (volumes from host) have poor performance on sync. There are 2 ways how it can be improved.

User-guided caching

Since Docker for Mac 17.06 there's a new native :cached option available for bind mounts. You can find more information about this in docker blog.

Replace codebase volume definition of php and nginx/apache services with the option below marked as "User-guided caching".


Performance tests 2017 Docker-sync vs Native.

The core idea of this project is to use an external volume that will sync your files with a file synchronizer tool.

gem install docker-sync
  1. Download docker-sync.yml file (inside of docker4x.tar.gz archive) from the latest stable release
  2. Uncomment docker-sync volume definition in your compose file
  3. Replace volumes definition of php and nginx/apache services with the option below marked as "Docker-sync".
  4. Start docker-sync: docker-sync start
  5. In a new shell run after you started docker-sync docker-compose up -d

Now when you change your code on the host machine docker-sync will sync your data to php and nginx/apache containers.

For more information visit docker-sync project page.

Permissions issues

You might have permissions issues caused by non-matching uid/gid on your host machine and the default user in php container.


Since version 5.0 the default php container user wodby has uid/gid 1000 that matches the default uid/gid for most popular Linux distributions.


Use -dev-macos version of php image where default wodby user has 501:20 uid/gid that matches default macOS user.


Since you can't change owner of mounted volumes in Docker for Win, the only solution is to run everything as root, add the following options to php service in your docker-compose file:

    user: root
    command: "php-fpm -R"
      PHP_FPM_USER: root
      PHP_FPM_GROUP: root

Different uid/gid?

You can rebuild the base image wodby/php with custom user/group ids by using docker build arguments WODBY_USER_ID, WODBY_GROUP_ID (both 1000 by default)

Running multiple Projects

This project uses træfik to route traffic to different containers. Træfik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease. To understand the basics of Traefik it is suggested to check Træfik's documentation page:

Image: Multi-domain set-up example (Source:

There are two ways how you can run multiple projects:

Single port

In this case you will run a stand-alone traefik that will be connected to docker networks of your projects:

  1. Download traefik.yml file (part of docker4x.tar.gz archive). Place it separately from your projects, it will be a global traefik container that will route requests to your projects on a specified port
  2. Now we need to provide traefik names of docker networks of our projects. Let's say projects directories with docker-compose.yml named foo and bar. Docker Compose will create default docker networks for these projects called foo_default and bar_default. Update external networks names accordingly in traefik.yml
  3. In docker-compose.yml of your projects comment out traefik service and make sure traefik.backend= labels have ${PROJECT_NAME}_ prefix
  4. Make sure$PROJECT_BASE_URL and $PROJECT_NAME (in .env file) differ, both hosts point to in /etc/hosts
  5. Run your projects: make (or docker-compose up -d)
  6. Run stand-alone traefik: docker-compose -f traefik.yml up -d
  7. Now when you visit URL from $PROJECT_BASE_URL, traefik will route traffic to the corresponding docker networks

For macOS users with docker-sync

Make sure names of syncs in docker-sync.yml are unique per project. The recommended way is to run a stand-alone docker-sync with syncs definition for all projects. Do not forget to update src paths for projects

Different ports

Alternatively, instead of running a stand-alone traefik, you can just run default traefik containers on different ports. Just a few things to make sure:

  • Ports of traefik service in your docker-compose.yml files differ
  • traefik.backend= labels have ${PROJECT_NAME}_ prefix
  • $PROJECT_BASE_URL and $PROJECT_NAME (in .env file) differ, both hosts point to in /etc/hosts