Local environment with Docker4Drupal¶
Docker4Drupal is an open-source project (GitHub page) that provides pre-configured compose.yml file with images to spin up local environment on Linux, Mac OS X and Windows.
Requirements¶
- Install Docker (Linux, Docker for Mac or Docker for Windows (10+ Pro))
- For Linux additionally install docker compose
Usage¶
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 the corresponding line under db server's volumes: in your 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¶
- Clone docker4drupal repository and switch to the latest stable tag or download/unpack the source code from the latest release
- Configure domains
- Optional: if you want to deploy Vanilla Drupal (by default Drupal CMS deployed), adjust php and crond images in
compose.override.ymlfile. For vanilla Drupal 10 (11 by default) adjustDRUPAL_TAGandNGINX_VHOST_PRESETin.envfile - From project root directory run
docker compose up -dormake upto start containers. Give it 10-20 seconds to initialize after the start - 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 ismariadb
Mount my codebase¶
- If you're starting a new project we recommend you to use drupal/recommended-project
- Download and unpack
docker4drupal.tar.gzfrom the latest stable release to your project root - Delete
compose.override.ymlas it's used to deploy Drupal CMS and vanilla Drupal - Ensure
NGINX_SERVER_ROOT(orAPACHE_DOCUMENT_ROOT) is correct, by default set to/var/www/html/webfor composer-based projects where Drupal is inwebsubdirectory - Ensure database access settings in your
settings.phpcorresponds to values in.envfile, 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' => '', ); - Configure domains
- Optional: for Drupal 10 (11 by default) update
NGINX_VHOST_PRESETcorrespondingly in your.envfile - Optional: uncomment lines in the compose file to run valkey (redis), solr, varnish, etc
- Optional: import existing database
- Optional: macOS users please read this
- Optional: Windows users please read this
- Run containers:
make upordocker compose up -d - Your drupal website should be up and running at http://drupal.docker.localhost:8000
- 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 feel free to delete traefik.yml that comes within 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
Domains¶
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 127.0.0.1 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 |
mailpit |
http://mailpit.drupal.docker.localhost:8000 |
solr |
http://solr.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 |
opensearch |
http://opensearch.drupal.docker.localhost:8000 |
Mail sending¶
By default mailpit service enabled to catch all outbound emails, you can switch to opensmtpd (uncomment corresponding service in the compose file) if you need to actually delivery emails. OpenSMTPD can be used together with a third-party SMTP service for guaranteed email delivery by providing RELAY_ environment variables
If you want to use OpenSMTPD replace the following env vars:
MSMTP_HOST: opensmtpd
MSMTP_PORT: 25
Xdebug¶
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¶
Make sure you have PHP_EXTENSIONS_DISABLE env var overridden in your compose file to enable Xdebug (default value is xdebug,xhprof).
- Uncomment these lines for PHP service in your
compose.ymlfile (environment variables changed since xdebug 3.x)PHP_XDEBUG_MODE: debug - Restart containers (
make) - Start debugging in IDE
- 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_START_WITH_REQUEST: "yes"
Debugging CLI requests¶
- Enable Xdebug as described in the previous section
- Uncomment the following environment variables for PHP service in your composer file
PHP_IDE_CONFIG: serverName=my-ide - Configure your IDE
- Restart containers (
make)
Also, you might need to update your hosts file.
IDE configuration¶
You must additionally configure your IDE to debug CLI requests.
PHPStorm¶
- Open
Run > Edit Configurationsfrom the main menu, chooseDefaults > PHP Web Pagein the left sidebar - Click to
[...]to the right ofServerand add a new server- Enter name
my-ide(as specified inPHP_IDE_CONFIG) - Enter any host, it does not matter
- Check
Use path mappings, select path to your project and enter/var/www/htmlin the right column (Absolute path on the server)
- Enter name
- Choose newly created server in "Server" for PHP Web Page
- Save settings
Crond¶
Crond enabled by default and runs every hour. The default command is:
drush -r /var/www/html/web cron
www-data user.
Solr (Search API)¶
- Make sure your Drupal site already installed
- Uncomment
solrandzookeeperservices in yourcompose.yml, start it (make) and wait until both services fully started - Access Solr container via
make shell solrand runmake init -f /usr/local/bin/actions.mk. This will enable authentication for Solr Cloud mode and create a collection nameddefaultthat will use_defaultconfig set - Now access PHP container via
make shelland install Search API Solr modulecomposer require drupal/search_api_solr - Enable Search API Solr Admin module
drush en -y search_api_solr_admin - Go to
Home » Administration » Configuration » Search and metadata » Search APIand create a new server of typeSolr Cloud with Basic Authwith the following settings:HTTP protocol: http Solr host: solr Solr port: 8983 Default Solr collection: default --- HTTP BASIC AUTHENTICATION Username: solr Password: SolrRocks - After the server creation you should see the error message
You are using an incompatible Solr schema. - Click
+ Upload confiset, check the checkbox on the page and submit to override the configset - The Solr server is now ready to use!
Database import and export¶
MariaDB¶
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 https://github.com/wodby/mariadb/issues/11
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
PostgreSQL¶
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¶
Basic:
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
Commands:
help List available commands and their description
up Start up all container from the current compose.yml
start Start stopped containers
stop Stop all containers for the current 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 [service] Access a container via shell as a default user (by default [service] is php)
logs [service] Show containers logs, use [service] to show logs of specific service
mutagen Start mutagen container and runs mutagen project
Drupal-specific:
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¶
By default, we use :cached option on bind mounts to improve performance on macOS (on Linux it behaves similarly to consistent). You can find more information about this in docker blog. However, there's the synchronisation with Mutagen which is a faster alternative.
Mutagen¶
The core idea of this project is to use an external volume that will sync your files with a file synchronizer tool.
First, we must install mutagen and mutagen-compose. Mutagen Compose requires Mutagen v0.13.0+.
brew install mutagen-io/mutagen
brew install mutagen-io/mutagen/mutagen-compose
- Modify your
compose.ymlas following:- at the end of the file uncomment
x-mutagen:andvolumes:fields - replace volumes definitions under services that needs to be synced with the ones marked as "Mutagen"
- at the end of the file uncomment
- Make sure ids of
defaultOwneranddefaultGroupunderx-mutagen:match ids of the image you're using, e.g. uid501and gid20for-dev-macosimage by default - Start mutagen via
mutagen-compose up
Now when you change your code on the host machine Mutagen will sync your data to containers that use the synced volumed.
For more information visit Mutagen 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.
Linux¶
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.
macOS¶
Use -dev-macos version of php image where default wodby user has 501:20 uid/gid that matches default macOS user.
Windows¶
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 compose.yml file:
php:
user: root
command: "php-fpm -R"
environment:
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: https://docs.traefik.io/

Image: Multi-domain set-up example (Source: traefik.io)
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:
- Download
traefik.ymlfile (part ofdocker4x.tar.gzarchive). Place it separately from your projects, it will be a global traefik container that will route requests to your projects on a specified port - Now we need to provide traefik names of docker networks of our projects. Let's say projects directories with
compose.ymlnamedfooandbar. Docker Compose will create default docker networks for these projects calledfoo_defaultandbar_default. Update external networks names accordingly intraefik.yml - In
compose.ymlof your projects comment outtraefikservice and make suretraefik.http.*labels have${PROJECT_NAME}_prefix - Make sure
$PROJECT_BASE_URLand$PROJECT_NAME(in.envfile) differ, both hosts point to127.0.0.1in/etc/hosts - Run your projects:
make(ordocker compose up -d) - Run stand-alone traefik:
docker compose -f traefik.yml up -d - Now when you visit URL from
$PROJECT_BASE_URL, traefik will route traffic to the corresponding docker networks
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
traefikservice in yourcompose.ymlfiles differ traefik.http.*labels have${PROJECT_NAME}_prefix$PROJECT_BASE_URLand$PROJECT_NAME(in.envfile) differ, both hosts point to127.0.0.1in/etc/hosts