---
title: "Local Environment & Site Factory"
date: "2024-04-02T00:36:50+00:00"
summary:
image:
type: "article"
url: "/site-factory/help/90441-local-environment-site-factory"
id: "759b1c17-b8cf-417f-9ffa-2c88e6f60122"
---

Table of contents will be added

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:

1.  Configure Lando to support wildcard DNS.
2.  In settings.php, set an "App ID" to use in code for a specific multisite in any environment.
3.  In settings.php, set database, public files, memcache prefix, Solr core, and other settings per "App ID".
4.  Configure Drush aliases per multisite and per environment.
5.  Write bash scripts to push and pull sites using these aliases.

1.  Configure Lando with wildcard DNS
    ---------------------------------
    
    [Lando](https://lando.dev/) is a docker orchestration framework for development environments.
    
    There are alternatives to Lando like ddev or docksal, but my preference is Lando because:
    
    1.  It's [simple to configure with YAML](https://docs.lando.dev/core/v3/).
    2.  It supports [many other services](https://docs.lando.dev/core/v3/services.html) like memcache, solr, redis, elasticsearch, mssql, etc.
    3.  It runs on Macs, Windows, or Linux
    4.  Their [homepage](https://lando.dev/) is fancy.
    
    If you're unfamiliar with Lando, reference [their documentation](https://docs.lando.dev/getting-started/) before continuing here.
    
    [Acquia supports a "Lando recipe"](https://docs.lando.dev/plugins/acquia/) 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:
    
    1.  Lando configuration using Acquia recipe
    2.  Wildcard DNS for any subdomain of \*.client1.lndo.site
    3.  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.
        
    
2.  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:
    
    1.  Used in Drush aliases per environment
    2.  Used in settings.php for config overrides and settings
    3.  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](/node/57269).
    
    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.
    
3.  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
    
4.  Configure Drush aliases per multisite and per environment
    ---------------------------------------------------------
    
    Acquia provides Drush aliases per environment you can find at:
    
    1.  The "Credentials" tab of your Acquia Cloud account
    2.  Using "acli remote:aliases:download" on command line.
        1.  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](https://github.com/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
        
    
5.  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