# Not Sure How To Get Multisite Functional in Dev Environment

**URL:** https://discourse.roots.io/t/not-sure-how-to-get-multisite-functional-in-dev-environment/15701
**Category:** trellis
**Created:** 2019-05-29T16:45:58Z
**Posts:** 8
**Showing post:** 2 of 8

## Post 2 by @knowler — 2019-05-29T18:13:09Z

Ok, so from the sounds of it, you want to host a Bedrock multisite network where the domains are all unique (not subdomains or subdirectories). For multisite installs WordPress gives you two options, subdomains or subdirectories. In order to do domain mapping — i.e. using custom domains for each site — we need to go with the subdomain option. This means we just need one Bedrock install. When you initially create a site in the network admin, you will need to create the domain as: `<domain-i-want>.<main-domain>.<tld>`, but after the site is created, WordPress lets you change that domain to `<domain-i-want>.<tld>`.

Here’s an example Trellis config for a couple of environments:

> **trellis/group_vars/production/wordpress_sites.yml (production sites)**
>
> ```
> wordpress_sites:
> example.com:
> site_hosts:
> - canonical: example.com
> redirects:
> - www.example.com
> - canonical: client.com # ← Add your custom domains like this.
> - www.client.com
> local_path: ../site
> repo: git@github.com:agency/wp-network.git
> repo_subtree_path: site
> branch: master
> multisite: # ← Multisite config stuff for Trellis
> enabled: true
> subdomains: true
> ssl:
> enabled: true
> provider: letsencrypt
> # Disable HSTS – I’m doing this since I don’t want HSTS on all of the domains.
> hsts_max_age: 0
> cache:
> enabled: false
> domain_current_site: example.com # ← This is the domain for the network admin.
> ```

> **trellis/group_vars/development/wordpress_sites.yml (development sites)**
>
> ```
> wordpress_sites:
> example.com:
> site_hosts:
> - canonical: example.test
> redirects:
> - www.example.test
> - canonical: client.test
> redirects:
> - www.client.test
> local_path: ../site
> admin_email: admin@example.test
> multisite:
> enabled: true
> subdomains: true
> ssl:
> enabled: false
> provider: self-signed
> cache:
> enabled: false
> ```

In development, since you are using multisite, Trellis will using the Vagrant Landrush plugin instead of the host manager to set the development domains. You do need to ensure you have the plugin installed first. You can do so with: `vagrant plugin install landrush`. Vagrant Landrush creates entries in your host machine’s DNS resolver for each domain you specify. You can double check it’s working by checking the contents of the `/etc/resolver` directory. After running `vagrant up` (wait to do this though, there is more important config below), you should see:

```
/etc/resolver
├── client.test
└── example.test
```

Ideally, the plugin should just work and you shouldn’t need to manually do this, but I found it helpful to know what magic was occurring and how I could debug it.

Now, the code that tells WordPress to be multisite needs to go into the Bedrock application config. I’ve included the actually snippet of code (for copy & pasting) and then a diff demonstrating where that code should (probably) be added:

> **site/config/application.php (snippet)**
>
> ```
> // Allow Multisite
> Config::define('WP_ALLOW_MULTISITE', true);
> 
> /**
> * Multisite
> */
> define('MULTISITE', true);
> define('SUBDOMAIN_INSTALL', true);
> define('DOMAIN_CURRENT_SITE', env('DOMAIN_CURRENT_SITE'));
> define('PATH_CURRENT_SITE', env('PATH_CURRENT_SITE') ?: '/');
> define('SITE_ID_CURRENT_SITE', env('SITE_ID_CURRENT_SITE') ?: 1);
> define('BLOG_ID_CURRENT_SITE', env('BLOG_ID_CURRENT_SITE') ?: 1);
> 
> Config::define('COOKIE_DOMAIN', $_SERVER['HTTP_HOST']);
> ```

> **site/config/application.php (full)**
>
> ```
> <?php
> /**
> * Your base production configuration goes in this file. Environment-specific
> * overrides go in their respective config/environments/{{WP_ENV}}.php file.
> *
> * A good default policy is to deviate from the production config as little as
> * possible. Try to define as much of your configuration in this file as you
> * can.
> */
>   
> use Roots\WPConfig\Config;
>   
> /** @var string Directory containing all of the site's files */
> $root_dir = dirname( __DIR__ );
>   
> /** @var string Document Root */
> $webroot_dir = $root_dir . '/web';
>   
> /**
> * Expose global env() function from oscarotero/env
> */
> Env::init();
>   
> /**
> * Use Dotenv to set required environment variables and load .env file in root
> */
> $dotenv = Dotenv\Dotenv::create($root_dir);
> if (file_exists($root_dir . '/.env')) {
> $dotenv->load();
> $dotenv->required(['DB_NAME', 'DB_USER', 'DB_PASSWORD', 'WP_HOME', 'WP_SITEURL']);
> }
>   
> /**
> * Set up our global environment constant and load its config first
> * Default: production
> */
> define('WP_ENV', env('WP_ENV') ?: 'production');
>   
> /**
> * URLs
> */
> Config::define('WP_HOME', env('WP_HOME'));
> Config::define('WP_SITEURL', env('WP_SITEURL'));
>   
> /**
> * Custom Content Directory
> */
> Config::define('CONTENT_DIR', '/app');
> Config::define('WP_CONTENT_DIR', $webroot_dir . Config::get('CONTENT_DIR'));
> Config::define('WP_CONTENT_URL', Config::get('WP_HOME') . Config::get('CONTENT_DIR'));
>   
> /**
> * DB settings
> */
> Config::define('DB_NAME', env('DB_NAME'));
> Config::define('DB_USER', env('DB_USER'));
> Config::define('DB_PASSWORD', env('DB_PASSWORD'));
> Config::define('DB_HOST', env('DB_HOST') ?: 'localhost');
> Config::define('DB_CHARSET', 'utf8mb4');
> Config::define('DB_COLLATE', '');
> $table_prefix = env('DB_PREFIX') ?: 'wp_';
>   
> /**
> * Authentication Unique Keys and Salts
> */
> Config::define('AUTH_KEY', env('AUTH_KEY'));
> Config::define('SECURE_AUTH_KEY', env('SECURE_AUTH_KEY'));
> Config::define('LOGGED_IN_KEY', env('LOGGED_IN_KEY'));
> Config::define('NONCE_KEY', env('NONCE_KEY'));
> Config::define('AUTH_SALT', env('AUTH_SALT'));
> Config::define('SECURE_AUTH_SALT', env('SECURE_AUTH_SALT'));
> Config::define('LOGGED_IN_SALT', env('LOGGED_IN_SALT'));
> Config::define('NONCE_SALT', env('NONCE_SALT'));
>   
> /**
> * Custom Settings
> */
> Config::define('AUTOMATIC_UPDATER_DISABLED', true);
> Config::define('DISABLE_WP_CRON', env('DISABLE_WP_CRON') ?: false);
> // Disable the plugin and theme file editor in the admin
> Config::define('DISALLOW_FILE_EDIT', true);
> // Disable plugin and theme updates and installation from the admin
> Config::define('DISALLOW_FILE_MODS', true);
> // Allow Multisite
> + Config::define('WP_ALLOW_MULTISITE', true);
> +
> + /**
> + * Multisite
> + */
> + define('MULTISITE', true);
> + define('SUBDOMAIN_INSTALL', true);
> + define('DOMAIN_CURRENT_SITE', env('DOMAIN_CURRENT_SITE'));
> + define('PATH_CURRENT_SITE', env('PATH_CURRENT_SITE') ?: '/');
> + define('SITE_ID_CURRENT_SITE', env('SITE_ID_CURRENT_SITE') ?: 1);
> + define('BLOG_ID_CURRENT_SITE', env('BLOG_ID_CURRENT_SITE') ?: 1);
> +  
> + /**
> + * Use the current HTTP host as the cookie domain. This ensures cookies and
> + * nonces are using the correct domain for the corresponding site. Without
> + * this, logins, REST requests, Gutenberg AJAX requests, and other actions
> + * which require verification will not work.
> + */
> + Config::define('COOKIE_DOMAIN', $_SERVER['HTTP_HOST']);
>   
> /**
> * Debugging Settings
> */
> Config::define('WP_DEBUG_DISPLAY', false);
> Config::define('SCRIPT_DEBUG', false);
> ini_set('display_errors', 0);
>   
> $env_config = __DIR__. '/environments/' . WP_ENV . '.php';
>   
> if (file_exists($env_config)) {
> require_once $env_config;
> }
>   
> Config::apply();
>   
> /**
> * Bootstrap WordPress
> */
> if (!defined('ABSPATH')) {
> define('ABSPATH', $webroot_dir . '/wp/');
> }
> ```

Note, I’m using `define()` rather than `\Roots\Config::define()` since Trellis will [temporarily add these constants](https://github.com/roots/trellis/blob/master/roles/deploy/files/tmp_multisite_constants.php) when it deploys. The latter will throw an error that stops the deploy while the former is silent (even though constants are being redefined).

Something else to note, is that I am setting the `COOKIE_DOMAIN` to `$_SERVER['HTTP_HOST']`. First of all, this is to avoid the login loop issue that happens in multisite installs. Often, guides to fix that issue will tell you to leave it as `define('COOKIE_DOMAIN', '')`, however, this causes issues with REST requests, which use cookie nonces to verify requests. If the domain of the current sub site (unique from the network domain) is not found the verification will fail (and Gutenberg or whatever won’t save), so setting this `$_SERVER['HTTP_HOST']` is the best way to remedy the issue.

As far as I know, that’s really all that’s required for a multisite install using custom domains. If you have issues, a good debugging step is to check what is set for the `domains` column in the `wp_blogs` table and the rows for the `siteurl` and `home` `option_value`s in the `wp_<#>_options` tables.

Let me know if there’s any issues you encounter as there may have been something that I’ve missed.

**Update** :

As the multisite docs note, I found adding the following to the `Vagrantfile` (line 62 or so) was important, otherwise, the VM couldn’t download anything.

```
config.landrush.guest_redirect_dns = false
```

---

_[View the full topic](https://discourse.roots.io/t/not-sure-how-to-get-multisite-functional-in-dev-environment/15701)._
