Set up a local development environment

Last updated on
27 December 2025

This documentation is deprecated.

Most of the steps needed are command-line commands. Press Ctrl-Alt-T to open the terminal and use them. The advantage is that you can use the same commands also to set up a remote server when connecting through SSH.

Composer (includes also cURL)

Pulls in required dependencies defined in a composer.json file in the root of a project. This is the common way to install Drupal projects as it excludes most of the code that is not under your version control from your repository, getting external packages only when necessary. In the steps below Composer is used to install Drupal 8, Drush, and Code Sniffer.

Guide to follow

How To Install and Use Composer on Ubuntu 22.04 | DigitalOcean. Steps 1 and 2. Read the other steps later as good info.

NOTE:

It might be needed to move the installer file:

mv composer-setup.php /tmp/composer-setup.php

Alternatively: Composer | getcomposer.org

Then

Change the owner to the current user to avoid using sudo:

sudo chown -R $USER .composer/

Test

composer --version

Output (example)

Composer version 2.0.11 2021-02-24 14:57:23

Useful suggestions

  • Troubleshooting - Composer, especially note the use of composer diag that provides a bunch of useful system version info
  • Get a list of all globally installed packages: composer global show
  • Update all globally installed packages: composer global update
  • Update Composer itself: sudo composer self-update or re-install Composer if running into issues.
  • php - How to downgrade or install a specific version of Composer? - Stack Overflow
  • Create a new project:
    composer create-project drupal/recommended-project my-project

    (further documentation)

  • Add development dependencies like PHPUnit (on your local machine):
    composer require --dev drupal/core-dev
  • Install all except development dependencies (on your server):
    composer install --no-dev # Skip installing packages listed in require-dev

    (recommended)

  • Parallel package downloads (more information): composer global require hirak/prestissimo
  • When in a feature branch on a project, you use the below a lot (look them up in the docs above):
    composer validate # To run before you commit your composer.json file.
    composer install # Always safe to run from the project root.
    composer update --lock #  Only updates the lock file hash to suppress warning about the lock file being out of date.

    Remember not to run composer installwhen inside a module folder that contains a composer.json. That does not harm but it would create an unnecessary vendor folder there. Be aware that composer validatechecks apart from a project's composer.json also your global Composer configuration at ~/.composer/config.json.

  • Do NOT use composer update without any options or without specifying a specific project. It will upgrade core, all contributed modules, and vendor libraries in one go, which is a terrible idea for big projects. Instead, you want to update each independently so you can test and make sure things don't break. Read more.
  • Example to install a dev branch module over a stable release:
    composer require drupal/entityqueue:1.x-dev#3ace5b7 # Adding the commit hash avoids upgrading the dev version to possibly unstable future versions.

    Source

  • Instead of using git clone when developing on a dependency package itself (for module developers), you might want to use Composer to make sure the module gets also added to the composer.json file. Example with the Git Info Report module:

    composer require drupal/gitinfo:1.x-dev --prefer-source
    cd web/modules/contrib/gitinfo
    git checkout 8.x-1.x # Change from the default branch (latest stable) to the development branch
    git remote set-url origin git@git.drupal.org:project/gitinfo.git # Use SSH to connect to the remote
    git pull # Verify the connection
  • Remove a package/module:

    drush pmu gitinfo # Uninstall the module first
    composer remove drupal/gitinfo
  • How to find the exact version of a package? - Stack Overflow
  • composer show package/name
  • How to resolve a "Can only install one of:" conflict? - Stack Overflow
    To see where the problematic package is referenced from in your project, run:

    composer why package/name -t
  • While the above command shows the current chain of requirements that is requesting a package,

    composer why-not package/name:[next.version.number] -t

    It shows if the next version of a package is allowed. The version should be the next version you intend to install, not the currently installed version. If the output is identical to the command above (composer why), it should be fine to upgrade.

    --tree (-t): Prints the results as a nested tree and recursively resolves up to the root package, showing also the dependencies of the dependencies, up to the project root composer.json (the one you usually act on).

    The command composer why is also known as composer depends, while composer why-not is the same as composer prohibits.
    See Command-line interface / Commands - Composer.

  • Start clean (upgrade, downgrade, or remove unused packages):

    rm -rf vendor
    composer install 
  • It is recommended to add a composer.json file to your modules. 
  • Last but not least, Composer is also the recommended way to apply patches to your codebase, both for the core as contrib projects:
    Apply Drupal 8 Patch by Composer without update Drupal Core | Drupal Groups
    It depends on:
    cweagans/composer-patches: Simple patches plugin for Composer
    Additionally:
    How do I apply patch in vendor directory using composer? - Drupal Answers
    Note: To make the patch valid, it seems that the a/ and b/ prefixes have to be removed.
    For example:
    diff --git docroot/.htaccess docroot/.htaccess
    index 27e19a0..03eacf5 100644
    --- docroot/.htaccess
    +++ docroot/.htaccess
    @@ -149,6 +149,8 @@ ....

More resources on Composer

Apache2 and PHP

Apache is an open-source HTTP server. It is the most commonly used web server for Drupal. Drupal will work on Apache 2.x hosted on UNIX/Linux, OS X, or Windows. PHP 7.4 is the default PHP version in Ubuntu 20.04. Please check Web server requirements | System requirements | Drupal Wiki guide on Drupal.org and PHP requirements | System requirements | Drupal Wiki guide on Drupal.org to see the recommended versions for the Drupal core you intend to use.

PHP is a server-side scripting language designed primarily for web development. PHP 7.3 or higher is recommended for current release versions of Drupal 8 and 9.

Guide to follow

How To Install Linux, Apache, MySQL, PHP (LAMP) stack on Ubuntu 20.04 | DigitalOcean

Test

http://localhost/

Output

Screenshot

Then

In /etc/php/7.2/apache2/php.ini set:

short_open_tag = On

Test Apache

 To just check the Apache 2 version, independent from the service running or not:

apache2ctl -v

The expected output looks like this:

Server version: Apache/2.4.29 (Ubuntu)
Server built:   2020-08-12T21:33:25

Test PHP

How To Install Linux, Apache, MySQL, PHP (LAMP) stack on Ubuntu 16.04 - Step 4 Test PHP | DigitalOcean (still valid for 20.04, open for discussion)

Then visit http://localhost/info.php.

Useful suggestions

How to switch between PHP 7 and PHP 5.6 on Ubuntu 16.04? - Ask Ubuntu (seems still valid for 18.04, open for discussion).
Related to that How to add PHP extensions (e.g. CURL) for a multi-version PHP setup.

Adjust PHP's memory limit

Quickly check your current PHP version:

php -v

Quickly check your current PHP extensions:

php -m

If you miss extensions after switching PHP version, just install them relative to the new version, for example, if missing a PostgreSQL driver:

sudo apt-get install php7.4-pgsql
sudo systemctl restart apache2

For Drupal >= 8.5 PHP 7.2 is recommended, however, drupal/core 8.5.x requires gd so do:

sudo apt-get install php-gd

We might as well install other common PHP extensions that might be missing:

sudo apt-get install -y php-xml php-mbstring php-curl php-bz2 php-zip

It might be needed to enable some of those:

sudo phpenmod dom xml

Check if the MySQL Improved Extension is enabled:

php -m | grep mysqli

Be aware that if you run PHP from the command line, it uses cli/php.ini and apache2/php.ini when running it through Apache (used by the browser). Modifying the last one requires restarting Apache to get changes applied:

systemctl restart apache2

TIPSafeguard against breaking php.ini changes.

Other useful resources

MySQL

An open-source relational database management system.

Note that Drupal can also run on MariaDB, Percona Server, PostgreSQL, SQLite, and others. Please check Database server | Drupal 8 guide on Drupal.org to see the recommended versions for the Drupal core you intend to use.

Guide to follow

How To Install MySQL on Ubuntu 20.04 | DigitalOcean

Test

mysql --version

Output (example)

mysql Ver 8.0.23-0ubuntu0.20.04.1 for Linux on x86_64 ((Ubuntu)

Set Up Apache Virtual Hosts

A method for hosting multiple domain names on a server.

Guide to follow

How To Set Up Apache Virtual Hosts on Ubuntu 16.04 | DigitalOcean (seems still valid for 18.04, open for discussion) with a slight variation using the location /var/www/html as the location for our sites instead of /var/www. The reasons behind it are best described in Where to place my local website starting with the 2.4.7 version of apache2? - Ask Ubuntu. Furthermore, we do not use the public_html folder therefore for this example the index.html file should be at /var/www/html/example.localhost/index.html.

An example test index.html file

/var/www/html/example.localhost/index.html

<html>
  <head>
    <title>Welcome to Example.localhost!</title>
  </head>
  <body>
    <h1>Success! The virtual host is working!</h1>
  </body>
</html>

An example Virtual Host File

/etc/apache2/sites-available/example.localhost.conf

<VirtualHost *:80>
  ServerName example.localhost
  ServerAlias www.example.localhost
  ServerAdmin webmaster@localhost
  DocumentRoot /var/www/html/example.localhost
  ErrorLog /var/www/html/logs/example.localhost/error.log
  CustomLog /var/www/html/logs/access.log combined
</VirtualHost>

To copy the above file under another name to create another site:

cd /etc/apache2/sites-available
sudo cp example.localhost.conf new.localhost.conf
sudo nano new.localhost.conf
# In the newly created file replace all instances of 'example' with 'new'.
^\
example
new
^x

One time

sudo mkdir /var/www/html/logs
# Create an empty access log.
sudo touch /var/www/html/logs/access.log

Then for each site

sudo mkdir /var/www/html/logs/example.localhost
# Create an empty error log.
sudo touch  /var/www/html/logs/example.localhost/error.log
# Enable the new site.
sudo a2ensite example.localhost.conf

Then

sudo systemctl restart apache2

If you get any errors on Apache restart, you can debug with:

systemctl status apache2.service

Test

Go to http://localhost/example.localhost/. If that does not result in the below output check the existence of /etc/apache2/sites-enabled/example.localhost.conf. If it exists delete it, check the content of /etc/apache2/sites-available/example.localhost.conf, modify any mistakes, and repeat sudo a2ensite example.localhost.confand sudo systemctl restart apache2.

Output

Screenshot

Add a single domain to resolve to localhost

In the case of a Drupal multisite setup, you would have a folder name identical to the domain. Add the single domain to the /etc/hosts file:

127.0.0.1       localhost example.com some.other.example.com

The drawback is that you need another machine to check the "live" site with the same domain name. The use of the TLD extension .localhost as described below, is preferred.

Use dnsmasq to resolve ALL .localhost-domains to localhost

DNS forwarding.

Note that this is a one-time action that does not need to be executed again once done successfully.

sudo apt-get install dnsmasq

Guide to follow

From the yum command onwards (only for CentOS, we used the above command instead), follow Make all *.dev domains resolve to localhost - hutter.io but instead of .dev use .localhost.

Note that using the .local extension results in problems. Do not use it. From the list of Reserved TLDs .localhost is the most appropriate to use.

Test

Go to http://www.example.localhost/. If that does not result in the below output then dnsmasq is still not configured right.

Output

Screenshot

Enable clean URLs

sudo nano /etc/apache2/apache2.conf

Open /etc/apache2/apache2.conf in your editor and in the section <Directory /var/www/>set AllowOverride All.

sudo a2enmod rewrite
sudo systemctl restart apache2

More detailed info: Enable clean URLs | Drupal.org

Set the permissions of /var/www/html right

Setting the permissions on the Apache website hosting directory.

Guide to follow

Permissions problems with /var/www/html and my own home directory for a website document root - Ask Ubuntu until step 4.

Then

In Nautilus (Ubuntu) go to /var/www/html and add the location as a bookmark to have a shortcut.

When on a remote server for the live site

To set up a remote server the same instructions as above can mainly be followed but with a few distinctions. You connect to your remote first, using:

ssh root@[your-ip-address]

An extended example Virtual Host File, including port 443 for HTTPS

NameVirtualHost *:80
<VirtualHost *:80>
   ServerName example.com
   Redirect / https://example.com/
</VirtualHost>

<VirtualHost *:443>
  ServerName example.com
  ServerAlias example.com
  ServerAdmin admin@example.com
  DocumentRoot /var/www/html/example.com/web
  SSLEngine on
  SSLCertificateFile /etc/apache2/ssl/example.com.crt
  SSLCertificateKeyFile /etc/apache2/ssl/example.com.key
  SSLCertificateChainFile /etc/apache2/ssl/example.com.ca-bundle
  ErrorLog /var/log/apache2/example.com/error.log
  CustomLog /var/log/apache2/example.com/access.log combined
</VirtualHost>

Notice that the differences compared with our local setup are due to using HTTPS instead of HTTP. The three certificate files will be generated when you create that (out of the scope of this manual).

SSL renewal

  1. Reissue the certificate.
  2. Validate all SAN URLs, preferably by uploading a validation file on each domain under [domain]/.well-known/pki-validation/[token-file-name].txt. The file can be downloaded once you submit an activation request and the status switches to “pending”. When uploading the file, do not change the name or its content. The status will change to “activated” after validation is successful.
  3. After that, the new certificate can be downloaded. Only the CRT file needs to be uploaded under the same name as the previous one, see the configuration files in the /etc/apache2/sites-enabled folder.
  4. Restart Apache. It might be necessary to restart your browser as well if you are still seeing the site in unsafe mode (HTTP).

Alternatively, to have both automatic renewal and a free SSL certificate, use Certbot.


Next: Set up Drupal 8 >>

Help improve this page

Page status: Deprecated

You can: