Local environment for Acquia Site Factory
- Last updated
- 1 minute read
Goal
Set up a best practice local environment for an Acquia Site Factory platform.
Overview
The purpose of this tutorial is to explain how to set up a best practice local environment for Drupal multisite, including on Acquia Site Factory:
- Configure Lando to support wildcard DNS.
- In settings.php, set an "App ID" to use in code for a specific multisite in any environment.
- In settings.php, set database, public files, memcache prefix, Solr core, and other settings per "App ID".
- Configure Drush aliases per multisite and per environment.
- Write bash scripts to push and pull sites using these aliases.
-
Configure Lando with wildcard DNS
Lando is a docker orchestration framework for development environments.
There are alternatives to Lando like ddev or docksal, but my preference is Lando because:
- It's simple to configure with YAML.
- It supports many other services like memcache, solr, redis, elasticsearch, mssql, etc.
- It runs on Macs, Windows, or Linux
- Their homepage is fancy.
If you're unfamiliar with Lando, reference their documentation before continuing here.
Acquia supports a "Lando recipe" that configures a set of Docker services that resembles an Acquia Cloud environment with services for Apache / php-fpm, MySQL, memcache, and Solr. This recipe can be simply extended to support multisite, including multisite for Acquia Site Factory.
If you're using Cloud IDE, it does not support running multiple sites at once because it only has 1 hostname. It is possible to work around this with path based multisites, but we can cover that in a different tutorial if requested.
The below example shows:
- Lando configuration using Acquia recipe
- Wildcard DNS for any subdomain of *.client1.lndo.site
- Other useful tips
Copy this to the project root of your Site Factory platform and run: lando start
name: client1 recipe: acquia config: acli_version: latest ah_application_uuid: enter-a-valid-guid # Found in URL of Acquia Cloud. ah_site_group: acquia-machine-name # Found in git URL. php: '8.1' xdebug: true proxy: appserver: - client1.lndo.site - "*.client1.lndo.site" # Wildcard here supports any subdomain of client1. solr: - solr.client1.lndo.site:8955 services: appserver: xdebug: true overrides: environment: XDEBUG_CONFIG: "client_host=host.docker.internal discover_client_host=1 log=/tmp/xdebug.log remote_enable=true remote_host=host.docker.internal" solr: type: solr:7 portforward: 8955 # Needs to match the port in: config.proxy.solr core: lando database: portforward: 33065 # Allows you to connect to database from local client.
-
In settings.php, set an "App ID" to use in code for a specific multisite in any environment
It's very helpful to have an "App ID" or unique identifier per multisite:
- Used in Drush aliases per environment
- Used in settings.php for config overrides and settings
- Used in custom code if necessary
Add the below code to the top of settings.php to map an environment variable to this "App ID". If you're using Acquia Site Factory, ensure this settings.php is loaded from a post-settings-php factory hook.
The "App ID" is determined by the first piece of the hostname. This is the "site name" on Acquia Site Factory and is consistent between all environments, including in this Lando environment.
$parts = explode('.', $_SERVER['HTTP_HOST']); if ($parts[0] == 'www') { $aid = $parts[1]; } else { $aid = $parts[0]; } $_ENV['SYS_AID'] = $aid;
We'll use this environment variable in the next steps.
-
In settings.php, set database, public files, memcache prefix, Solr core, and other settings per "App ID"
Once your "App ID" is set, configure Lando resources per multisite. This sets the database name, public files directories, and memcache prefix to the "App ID".
// Configure unique database per multisite. The root user can access or create // any database in Lando. $databases['default']['default'] = [ 'database' => $_ENV['SYS_AID'], 'username' => 'root', 'password' => '', 'host' => 'database', 'port' => '3306', 'driver' => 'mysql', 'prefix' => '', 'collation' => 'utf8mb4_general_ci', ]; // Set public files path per multisite within the default files directory. $settings['file_public_path'] = "sites/default/files/{$_ENV['SYS_AID']}"; // Set temporary files path per multisite. $settings['file_temp_path'] = "/tmp/{$_ENV['SYS_AID']}"; // Set private files path per multisite. This could also live within // the directory /app/files-private. $settings['file_private_path'] = "/tmp/private-{$_ENV['SYS_AID']}";
If you need a Solr core per multisite, add these lines to your settings.php and .lando.yml:
settings.php
$config['search_api.server.acquia_search_server']['backend_config']['connector_config']['core'] = 'http'; $config['search_api.server.acquia_search_server']['backend_config']['connector_config']['host'] = 'solr'; $config['search_api.server.acquia_search_server']['backend_config']['connector_config']['port'] = 8983; $config['search_api.server.acquia_search_server']['backend_config']['connector_config']['core'] = $_ENV['SYS_AID'];
.lando.yml (create a core for each multisite)
services: solr: type: solr:7 portforward: 8955 core: lando run: - /opt/solr/bin/solr create -c client1 -d /solrconf/conf - /opt/solr/bin/solr create -c client2 -d /solrconf/conf
-
Configure Drush aliases per multisite and per environment
Acquia provides Drush aliases per environment you can find at:
- The "Credentials" tab of your Acquia Cloud account
- Using "acli remote:aliases:download" on command line.
- Ensure Acquia application UUID is set in .acquia-cli.yml at project root with config key "cloud_app_uuid".
But these aliases are not per multisite. You can pass the multisite URL per environment in the --uri option to Drush commands, but certain commands like "sql:sync" don't support the URI option and require specific aliases. And it's a hassle.
As a best practice, modify your Drush aliases file to have an alias per multisite per environment. It's super helpful both for manual use on CLI to target specific sites and for use in bash scripts and automation.
If it's a small number of sites, it's reasonable to do manually. If it's a large number of sites, you can write a script to do it. The acquia/acsf-tools project provides a YAML file you can use in a script with all multisites.
The below example shows multisite aliases for App IDs "site1" and "site2" of application "client1". After saving this, discover Drush aliases with: drush sa
local-site1: root: /app/docroot uri: 'https://site1.client1.lndo.site/' local-site2: root: /app/docroot uri: 'https://site2.client1.lndo.site/' 01live-site1: uri: https://site1.client1.acsitefactory.com/ root: /var/www/html/client1.01live/docroot ac-site: client1 ac-env: 01live ac-realm: enterprise-g1 01live.livedev: parent: '@client1.01live' root: /mnt/gfs/client1.01live/livedev/docroot host: client101live.ssh.enterprise-g1.acquia-sites.com user: client1.01live 01live-site2: uri: https://site2.client1.acsitefactory.com/ root: /var/www/html/client1.01live/docroot ac-site: client1 ac-env: 01live ac-realm: enterprise-g1 01live.livedev: parent: '@client1.01live' root: /mnt/gfs/client1.01live/livedev/docroot host: client101live.ssh.enterprise-g1.acquia-sites.com user: client1.01live 01test-site1: uri: https://site1.test-client1.acsitefactory.com/ root: /var/www/html/client1.01test/docroot ac-site: client1 ac-env: 01test ac-realm: enterprise-g1 01test.livedev: parent: '@client1.01test' root: /mnt/gfs/client1.01test/livedev/docroot host: client101test.ssh.enterprise-g1.acquia-sites.com user: client1.01test 01test-site2: uri: https://site2.test-client1.acsitefactory.com/ root: /var/www/html/client1.01test/docroot ac-site: client1 ac-env: 01test ac-realm: enterprise-g1 01test.livedev: parent: '@client1.01test' root: /mnt/gfs/client1.01test/livedev/docroot host: client101test.ssh.enterprise-g1.acquia-sites.com user: client1.01test 01dev-site1: uri: https://site1.dev-client1.acsitefactory.com/ root: /var/www/html/client1.01dev/docroot ac-site: client1 ac-env: 01dev ac-realm: enterprise-g1 01dev.livedev: parent: '@client1.01dev' root: /mnt/gfs/client1.01dev/livedev/docroot host: client101dev.ssh.enterprise-g1.acquia-sites.com user: client1.01dev 01dev-site2: uri: https://site2.dev-client1.acsitefactory.com/ root: /var/www/html/client1.01dev/docroot ac-site: client1 ac-env: 01dev ac-realm: enterprise-g1 01dev.livedev: parent: '@client1.01dev' root: /mnt/gfs/client1.01dev/livedev/docroot host: client101dev.ssh.enterprise-g1.acquia-sites.com user: client1.01dev 01dev: root: /var/www/html/client1.01dev/docroot ac-site: client1 ac-env: 01dev ac-realm: enterprise-g1 uri: client101dev.enterprise-g1.acquia-sites.com 01dev.livedev: parent: '@client1.01dev' root: /mnt/gfs/client1.01dev/livedev/docroot host: client101dev.ssh.enterprise-g1.acquia-sites.com user: client1.01dev 01devup: root: /var/www/html/client1.01devup/docroot ac-site: client1 ac-env: 01devup ac-realm: enterprise-g1 uri: client101devup.enterprise-g1.acquia-sites.com 01devup.livedev: parent: '@client1.01devup' root: /mnt/gfs/client1.01devup/livedev/docroot host: client101devup.ssh.enterprise-g1.acquia-sites.com user: client1.01devup 01live: root: /var/www/html/client1.01live/docroot ac-site: client1 ac-env: 01live ac-realm: enterprise-g1 uri: client101live.enterprise-g1.acquia-sites.com 01live.livedev: parent: '@client1.01live' root: /mnt/gfs/client1.01live/livedev/docroot host: client101live.ssh.enterprise-g1.acquia-sites.com user: client1.01live 01test: root: /var/www/html/client1.01test/docroot ac-site: client1 ac-env: 01test ac-realm: enterprise-g1 uri: client101test.enterprise-g1.acquia-sites.com 01test.livedev: parent: '@client1.01test' root: /mnt/gfs/client1.01test/livedev/docroot host: client101test.ssh.enterprise-g1.acquia-sites.com user: client1.01test 01testup: root: /var/www/html/client1.01testup/docroot ac-site: client1 ac-env: 01testup ac-realm: enterprise-g1 uri: client101testup.enterprise-g1.acquia-sites.com 01testup.livedev: parent: '@client1.01testup' root: /mnt/gfs/client1.01testup/livedev/docroot host: client101testup.ssh.enterprise-g1.acquia-sites.com user: client1.01testup 01update: root: /var/www/html/client1.01update/docroot ac-site: client1 ac-env: 01update ac-realm: enterprise-g1 uri: client101update.enterprise-g1.acquia-sites.com 01update.livedev: parent: '@client1.01update' root: /mnt/gfs/client1.01update/livedev/docroot host: client101update.ssh.enterprise-g1.acquia-sites.com user: client1.01update dev: root: /var/www/html/client1.dev/docroot ac-site: client1 ac-env: dev ac-realm: enterprise-g1 uri: client1dev.enterprise-g1.acquia-sites.com dev.livedev: parent: '@client1.dev' root: /mnt/gfs/client1.dev/livedev/docroot host: null user: null prod: root: /var/www/html/client1.prod/docroot ac-site: client1 ac-env: prod ac-realm: enterprise-g1 uri: client1prod.enterprise-g1.acquia-sites.com prod.livedev: parent: '@client1.prod' root: /mnt/gfs/client1.prod/livedev/docroot host: null user: null test: root: /var/www/html/client1.test/docroot ac-site: client1 ac-env: test ac-realm: enterprise-g1 uri: client1test.enterprise-g1.acquia-sites.com test.livedev: parent: '@client1.test' root: /mnt/gfs/client1.test/livedev/docroot host: null user: null
-
Write bash scripts to push and pull sites using these aliases
The below bash script shows how to use Drush aliases in multisite automation with environment and "App ID" passed as arguments.
#!/usr/bin/env bash # set -e project_root="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../" >/dev/null 2>&1 && pwd )" cd "${project_root}" multisite="${1:-main1}" env="${2:-01dev}" drush sql-sync @d.$env-$multisite @d.local-$multisite --create-db -y -v drush rsync @d.$env-$multisite:%files/ @d.local-$multisite:%files/ -y -v drush @d.local-$multisite cr # drush config:import -y # drush sitestudio:package:import drush @d.local-$multisite cohesion:rebuild # drush @d.local-$multisite search-api:clear # drush @d.local-$multisite search-api:reset-tracker # drush @d.local-$multisite search-api:index drush @d.local-$multisite uli cd -
Example usage would be something like:
bash /app/scripts/pull.sh site1 01live bash /app/scripts/pull.sh site2 01test bash /app/scripts/pull.sh site1 01dev