Skip to content
Go to file

Latest commit


Git stats


Failed to load latest commit information.

Starterkit for a complete Drupal 8 or 9 site



  • About
  • Quickstart
  • HTTPS quickstart
  • Initial installation on Docker
  • Incremental deployment (updating) on Docker
  • ./scripts/ instead of docker-compose
  • Environment types
  • Power up/power down the Docker environment
  • Uninstalling the Docker environment
  • Prescribed development process
  • Starter data
  • Patches
  • Development design patterns
  • Running automated tests
  • Security and maintenance updates
  • Theming
  • Acquia
  • Login links for Acquia environments
  • Linting
  • Getting a local version of the database
  • Logging
  • Logging emails during development
  • Troubleshooting


A starterkit to build a Drupal 8 or 9 project.

Which branch to use

  • The master branch of Drupal 8
  • The 9 branch of Drupal 9


  • Docker-based: only Docker is required to build a development environment.
  • One-click install: a full installation should be available simply by running ./scripts/
  • Dependencies and generated files are not included in the repo: modules, libraries, etc. should not be included in the repo. If you need to generate a directory with all dependencies and generated files, run ./scripts/
  • Sass is not used: to keep the workflow simple, this project does not use SASS; we encourage direct modification of CSS. See the section "No SASS", below.

Where to find the code


Step 1: Install Docker (nothing else required)

Step 2:

cd ~/Desktop && git clone
cd ~/Desktop/starterkit-drupal8site && ./scripts/

Step 3: Click on the login link at the end of the command line output and enjoy a fully installed Drupal 8 or 9 environment (depending on the branch used -- see above).

You can SSH into your container by running:


HTTPS quickstart

cd ~/Desktop/starterkit-drupal8site && ./scripts/

See the article Local development using Docker and HTTPS, Dcycle Blog, Oct. 27, 2018 for details on how this works.

Initial installation on Docker

To install or update the code, install Docker and run ./scripts/, which will give you a login link to a local development site.

Incremental deployment (updating) on Docker

Updating your local installation is the same command as the installation command:


This will bring in new features and keep your existing data.

Environment types

When you run ./scripts/, you can specify one of these environment types. If you do not specify an environment type, the "dev" environment type is used.

dev (default): used for local development.

  • Opcache is turned off: PHP parses your code at every call, insteading of using optimization cache.
  • Volumes are shared between your host and your container, meaning changes to your code on your host machine are used by your container.

build: used to build an image, potentially for storage on a Docker registry.

  • Opcache is turned on because "build" mode is not designed to do development.
  • Code is copied to the container, so your container will have everything it needs to run.

./scripts/ instead of docker-compose

The docker-compose command will, by default, use docker-compose.yml, but in our setup, docker-compose.yml by itself is not valid, because we have several environment types (dev, build, ...). Using ./scripts/ will find the right environment type based on the contents of the unversioned .env file. For example, if the environment type is "dev", we will use, automatically, docker-compose.yml but also as described in ./scripts/dev/env.txt.

Power up/power down the Docker environment

To shut down your containers but keep your data for next time:

./scripts/ down

To power up your containers:

./scripts/ up -d

Uninstalling the Docker environment

To shut down your containers and destroy your data:


If your project is the only project using the local https via the [Nginx Proxy], you might want to destroy the nginx-proxy container

docker kill nginx-proxy
docker rm nginx-proxy

You might also want to remove, from /etc/hosts, the line which contains your local development domain (use sudo vi /etc/hosts to edit that file); and also remove the certificate for your domain from ~/.docker-compose-certs.

Prescribed development process

The development cycle is as follows:

  • Download the latest code and run ./scripts/
  • Perform your local development on a branch my-feature.
  • Export your config changes to code using ./scripts/
  • Merge the latest version of master and run ./scripts/
  • Push to Github and open a pull request.
  • Self-review or have a colleague review code in the pull request.
  • Make sure automated tests and linting is passing using ./scripts/
  • Use Github's GUI to Squash and merge your branch for clean git history.
  • Use Github's GUI to delete your branch.
  • Delete your branch locally and switch back to master.
  • Pull the latest version of master.

Developers should also read this entire ./ file and add to it any information which may be useful for other developers.

Starter data

Our approach is to include everything necessary for development within our codebase, meaning that, if you use this project as a basis for your own projects, and follow the same approach, developers will almost never need to obtain a staging database to do work.

Here is how this works:

  • In ./drupal/starter-data, you will find a starter database and starter files such as images.
  • When you run ./scripts/, the code will use the starter data to populate the environment.
  • Developers who create, for example, a new content type xyz and a new view at, for example, /listing, will run ./scripts/ to export the content type and view.

The above will only create the functionality (configuration) associated with the new content type and view; however the next developer to run ./scripts/ on a new environment, or a testbot, will not have any dummy (starter) data associated with this new configuration. The following steps can be added to a development workflow to remedy this:

  • Developers can now create a few dummy nodes of type xyz, along with images in image fields.
  • They will then run ./scripts/ which will take this new data and make it part of the codebase.
  • This new data will now be available to new developers (and existing developers if they run ./scripts/, then ./scripts/ again).
  • This new data will be available to automated test code at ./tests/browser-tests/test01.js (which is called by ./scripts/ in the continuous integration process).
  • You will also be able to test for accessibility of your new code, with dummy data, at ./scripts/ (also called during the continuous integration process).

With this approach, functionality and configuration is deeply integrated with dummy data in the same codebase.


If you need to apply a patch, make sure it is published on in an issue, and apply it in the Dockerfiles. See ./Dockerfile for an example.

Development design patterns

If you are looking at the code for the first time, here are a few design patterns and approaches which explain some of the choices which were made during the development process:

Do not commit code which can be dowloaded or generated

We are not committing code such as downloaded modules, keeping the codebase rather small.


We do not use SASS in this project to avoid complexity. If you want to implement SASS support, consider the following questions:

  • Generated CSS should be created in the build phase by ./scripts/ Because we only allow Docker as a dependency of this project, you might consider a technique such as the one outlined in "Compass Sass to CSS using Docker", Feb. 9, 2016, Dcycle blog.
  • You should not commit generated CSS to this git repo. You might need to .gitignore certain files, or make sure they reside on the container, but not your local computer.
  • Do you need a real-time compiler directly embedded in your container?

Docker-based one-click development setup

./scripts/ is designed to use Docker to set up everything you need to have a complete environment.

Common code as traits

We make use of traits to allow classes to implement common code and the Singleton design pattern (see ./drupal/custom-modules/my_custom_module/src/App.php and ./drupal/custom-modules/my_custom_module/my_cystom_module.module for usage).

Running automated tests

Developers are encouraged to run ./scripts/ on their machine. If you have Docker installed, this will work without any further configuration.

This lints code (that is, it checks code for stylistic errors) and runs automated unit PHPUnit tests. If you get linting errors you feel do not apply, see a workaround in the "Linting" section, below.

We are not using Drupal's automated testing framework, rather:

  • PHPUnit is used to test the code;
  • Any Drupal-specific code is wrapped in methods ./drupal/custom-modules/my_custom_module/src/traits/Environment.php, then mocked in the unit tests. See ./drupal/custom-modules/my_custom_module/test/AppTest.php for an example.
  • Running tests is done using ./scripts/ (which includes linting) or ./scripts/ (which does not include linting), requiring only Docker and no additional setup.

When submitting a core or contrib patch, debugging failing tests

Let's use an example:

On Dec. 4th, 2020, this comment was submitted to a core issue, along with a patch. The test failed with 21 failures. Here is how I reproduced the failure locally for debugging purposes:

  • From the issue comment, click on the failing test, in this case PHP 7.3 & MySQL 5.7 27,916 pass, 21 fail.

  • In the resulting page, note what you'd like to debug. In this example, we can the class Drupal\Tests\locale\Functional\LocalePluralFormatTest seems to fail.

  • Make sure you have a running local environment; this can be done using ./scripts/deploy 9.

  • Make sure the patch in question is applied to the Drupal codebase on the container (note that a current limitation of this approach is that we are using the latest stable code, not the latest HEAD):


    curl -O patch -p1 < 2273889-42-core-plural-index.diff exit

  • Now run the failing test locally:

./scripts/ "Drupal\Tests\locale\Functional\LocalePluralFormatTest"

Voilà, you will now get the same results locally as you do on Drupal's testbot, making it easier to work on patches.

Security and maintenance updates

Because our git repo does not contain actual Drupal or module code, it downloads these each time ./scripts/ is run, so simply running ./scripts/ will update everything to the latest version.


We are using the CDN Drupal Bootstrap subtheme.


This project provides some integration with Acquia Cloud.

Prerequisites on the Acquia server

  • We need to install the same version of PHP as can be found on the containers by typing ./scripts/ exec drupal /bin/bash -c 'php -v'.
  • Make sure you are periodically calling "drush cron" from the command line using this technique on your Acquia site.

Notes about cron on Acquia

Acquia suggests this technique for setting up cron.

drush @MY-ACQUIA-ACCOUNT -dv -l cron &>> /var/log/sites/${AH_SITE_NAME}/logs/$(hostname -s)/drush-cron.log

Do not modify the Acquia git code directly, "build" it instead

Run ./scripts/

Login links for Acquia environments



We use a PHP linter and other linters in ./scripts/ If your code is not commented correctly or structured correctly, Circle CI continuous integration will fail. See the linter documentation on how to ignore certain blocks of code, for example t() is ignored in ./drupal-server/custom-modules/my_custom_module/src/traits/Utilities.php.

// @codingStandardsIgnoreStart
// @codingStandardsIgnoreEnd

Getting a local version of the database

If you have a .sql file with the database database, you can run:

./scripts/ exec drupal /bin/bash -c 'drush sqlc' < /path/to/db.sql

You can set up the Stage File Proxy module to fetch files from the live server instead of importing the files.


We are using syslog instead of dblog for speed and ease of use. This means you will not have access to log messages through the administrative interface.

To access logs you can then use

tail \
  -f ./do-not-commit/log/drupal.log \

Or the Mac OS X Console application.

Logging emails during development

We are using a dummy email server with a full GUI, Mailhog, as a destination for our emails during development. This is described in Debug outgoing emails with Mailhog, a dummy mailserver with a GUI, March 14, 2019, Dcycle Blog.

Here is how it works:

When you create or update your environment ./scripts/, or at any time by running ./scripts/, you will see something like:

=> Drupal:
=> Dummy email client:

The ports will be different each time, but using the above ports as an example, you can:

(1) visit (2) enter "admin" (3) you should see "Further instructions have been sent to your email address." (4) to view the actual email in a GUI, visit (in this example)


Do not use "docker-compose", use "./scripts/"

We construct our docker-compose environment based on an environment type (see above), therefore the docker-compose.yml file, on its own, is invalid and will produce:

ERROR: The Compose file is invalid because ...

The solution is to use, for example, ./scripts/ ps instead of docker-compose ps.

First steps if anything goes wrong

Make sure you completely delete your environment using:

./scripts/ down -v

Make sure you have the latest stable version of your OS and of Docker. If you're really in a bind, you can do a factory reset of Docker (which will kill your local data). Make sure Docker has 6Gb of RAM. (On Mac OS, for example, this can be done using Docker > Preferences > Advanced, then restarting Docker).

Rerun ./scripts/, and reimport the stage database if you need it (see "Getting a local version of the database", above).

./scripts/ down -v results in a network error

If you run docker network rm starterkit_drupal8site_default and you get "ERROR: network starterkit_drupal8site_default id ... has active endpoints", you might need to disconnect the Drupal 7 site from the Drupal 8 network first. This should be done in the migration process, but it might not have worked. If such is the case, type:

docker network disconnect starterkit_drupal8site_default $(./scripts/ ps -q database)

Beware case-sensitivity

Docker is meant to mimic the Acquia environment relatively well, but there can be certain differences, for instance:

  • if you use path/to/class instead of path/To/Class, it will work in Docker (local) and in automated tests, but fail on Acquia.

./scripts/ build --no-cache

Docker will, by default, used cached versions of each step of the build process for images. For example:

FROM ubuntu
RUN apt-get install something

If you know "something" has changed, you might want to run:

./scripts/ build --no-cache

Can't see the syslogs (see the "Logging" section, above)

Run ./scripts/, which will restart rsyslog.


Starterkit for a complete Drupal 8 project



No releases published


No packages published
You can’t perform that action at this time.