Run multiple sites in DDEV with a shared Drupal codebase and contrib modules.
Why?
With a multisite, the sites all share the core and contrib code.
This means that all sites are updated at the same time.
This could be a convenience, if everything is well tested, but could cause headaches when a change for one site disrupts the others.
You may also want to run a multisite for local development, where you can have a collection of modules and quickly experiment with ideas.
Drupal Multisite
A Drupal multisite is a single Drupal installation that hosts multiple websites.
https://www.drupal.org/docs/getting-started/multisite-drupal
By default, Drupal looks for a settings.php file in sites/default/settings.php.
To set up a multisite, $sites in /sites/sites.php is an associative array pointing host names to a subdirectory in /sites.
To add a new site, create the directory and add the site to /sites/site.php.
You will need a working host name and database.
sites.php
if (str_ends_with($http_host, '.mantra.ddev.site')) {
$sites[$http_host] = explode('.', $http_host)[0];
}
Set $sites['SITE.mantra.ddev.site'] = 'SITE'.
Add additional hostnames with:
$sites += [
'www.drupalarchitect.info' => 'drupalarchitect',
];
sites/SITE/settings.php
Include shared.settings.php, and set any site specific settings.
include "{$app_root}/sites/shared.settings.php";
$settings['trusted_host_patterns'][] = '^score\.andrewsclasses\.com$';
sites/SITE/settings.local.php
Use for any site + environment specific settings.
Not checked into git.
sites/development.settings.php
Set database and other settings for lando/ddev.
Incorporates example.settings.local.php
Incorporates settings.ddev.php.
# Local dev hash.
require "{$app_root}/sites/development.hash.php";
# Local host names.
if (in_array($local_env, ['ddev', 'lando'])) {
$settings['trusted_host_patterns'] = [
"^{$site}\.mantra\.*\.site$",
];
}
# Database settings
$databases['default']['default'] = array (
'database' => '',
'username' => '',
'password' => '',
'prefix' => '',
'host' => '',
'port' => '3306',
'driver' => 'mysql',
);
# Lando/DDEV.
switch ($local_env) {
case 'lando':
$host = $site == 'default' ? 'database' : $site;
$databases['default']['default']['host'] = $host;
$databases['default']['default']['database'] = $site;
$databases['default']['default']['username'] = $site;
$databases['default']['default']['password'] = $site;
break;
case 'ddev':
$databases['default']['default']['host'] = 'db';
$databases['default']['default']['database'] = $site;
$databases['default']['default']['username'] = $site;
$databases['default']['default']['password'] = $site;
break;
}
sites/shared.settings.php
/**
* Get site name from path.
*/
$site_path_split = explode('/', $site_path);
$site = array_pop($site_path_split);
/*
* Default settings.
*
* https://git.drupalcode.org/project/drupal/-/blob/11.x/core/assets/scaffold/files/default.settings.php?ref_type=heads
*/
$default_settings = $app_root . '/core/assets/scaffold/files/default.settings.php';
if (is_readable($default_settings)) {
require $default_settings;
}
/*
* File paths.
*/
$settings['file_assets_path'] = 'assets';
$settings['config_sync_directory'] = "../config/{$site}";
$settings['file_private_path'] = "../private/{$site}";
/*
* Filefield temp path - used for uploaded files before saving with adjusted name.
*/
$config['filefield_paths.settings']['temp_location'] = "private://filefield_paths_temp";
/*
* Local environment.
*/
$local_env =
getenv('IS_DDEV_PROJECT') ? 'ddev' : (
getenv('LANDO_INFO') ? 'lando' : ''
);
/*
* Prod urls.
*
* Additional hostnames defined in sites/SITE/settings.php.
*/
if (!$local_env) {
$settings['trusted_host_patterns'] = [
"^{$site_path}\.mantra\.network$",
];
}
/*
* Development settings.
*/
if ($local_env) {
$development_settings = $app_root . '/sites/development.settings.php';
if (is_readable($development_settings)) {
require $development_settings;
}
}
/*
* Include site local settings.
*/
$local_settings = $app_root . '/' . $site_path . '/settings.local.php';
if (is_readable($local_settings)) {
require $local_settings;
}
DDEV Config
.ddev/config.yaml
Set additional_hostnames to a wildcard subdomain.
additional_hostnames: ['*.mantra']
This will set up wildcard certs on DDEV so the browser doesn't show a SSL warning.
Databases
A basic option is to use a database prefix in each site's settings.php.
However, this could cause problems with DDEV's import-db command if remote sites aren't also prefixed.
Another option is to use the site name slug as the database name.
A new database can be created with drush @ddev.SITE sql:create
, but aliases need to be set up first.
Drush Aliases
Create /drush/sites/ddev.site.yml.
'*':
root: /var/www/html
uri: https://${env-name}.mantra.ddev.site
This will set up a @ddev.SITE drush wildcard alias that works for all local sites.
When the alias is used, the uri matches a definition in sites.php to determine the site directory.
Drush use @site
You can tell drush to use a site, so the alias doesn't have to be used in subsequent calls.
drush site:set @site
or drush use @site
.
To set this up in DDEV, add this environment variable in .ddev/config.yaml:
web_environment:
- DRUSH_SHELL_PID=PERMANENT
Drall
Drall is a tool for running drush commands on multisite installations.