Lando 3 and WSL 2 Preview

At time of posting my team at 4mation has been using Lando for 9 months to simplify onboarding and standardise development environments across projects. Despite some early pains attributed to the then non-final tool, the adoption has gone remarkably well and is now the preferred local development solution for websites built and maintained by PHP teams across the company.

When the adoption of Lando started, Microsoft announced WSL 2. This presented a clean solution for the double start script (run.sh and run.bat) maintenance problem, as it became possible to mostly use the bash start script as is. This bought time while WSL 2 matured and Docker developed the WSL 2 backend.

Getting Started

Through trial and error, the following setup process was developed. Note that this requires Windows 10 version 2004 (available via Insider builds at the time of writing, release is not far off).

WSL 2 Prep

First up, enable WSL 2 as described in the Microsoft documentation.

Then set WSL 2 as the default version with;

wsl --set-default-version 2

If a preferred distro is already installed but under WSL 1, upgrade it as follows (making a backup is recommended);

wsl --set-version <Distro> 2

Otherwise install a distro such as Ubuntu 20.04 from the Microsoft Store, and start it so that the first time setup can be completed.


The current stable Docker builds include support for WSL 2. Install from Docker Hub or use a package manager like Chocolatey. Docker should automatically use WSL 2 if available, double check in the Docker settings. If its not using WSL 2 enable it, Docker should provide all the guidance needed to resolve any issues that may crop up.

Once it is confirmed that the WSL 2 based engine is being used, go to "Resources" → "WSL INTEGRATION" and enable integration with the preferred Linux distro. This should be automatic for the default distro.

NOTE To use docker and docker-compose commands in the distro, Docker must be running. You may need to restart the terminal if Docker was started after.


Follow the installation guide in the Lando docs for your preferred Linux distro, you may need to instruct the package manager to ignore the Docker dependency.

WSL 2 Preview RAM Capping

When using Lando, particularly with large projects, you'll likely quickly discover a process in Task Manager called vmmem using massive amounts of memory. This process is a part of WSL 2. The problem is Linux includes optimisations such as keeping recently read file content in memory to provide significantly faster reads later on. Under a standard Linux install you'd likely never notice this as the used RAM is treated as free and can be quickly reclaimed on-the-fly as processes request RAM later on. Odds are Windows uses vacant RAM for similar benefits.

The crux of the problem however is balance, Linux doesn't want to drop its caches as it would hurt performance and consequently RAM doesn't get released back meaning Windows has less memory to work with.

The interim solution unfortunately also means removing one of the benefits of WSL, removing the need to manually manage memory as you would with a VM. This is done by creating a file called .wslconfig in your Windows profile folder (C:\Users\<username>\) with contents similar to the following;


WSL will need to be restarted (restart Windows or run wsl --shutdown) for these changes to take affect.


At this point everything should be working, but there is only one way to be sure! Run through the recipe at Running your first app in the Lando docs give it a go in the Linux distro. Lando may also be installed on the Windows side however the drive boundary will result in substantial performance penalties (think 100ms vs 1s response times, depending on the project).

NOTE Lando is sensitive to Docker changes and to that end MacOS and Windows installers bundle a known working version of Docker Desktop. If issues occur check the Docker Desktop version Lando currently works with and try matching it up.


Lando does a lot however there are some things which are outside the tools scope. For some projects this presents a problem for onboarding so an additional measure was taken, adding a bash script called run.sh where additional steps can be performed. All run scripts start Lando, some examples of project specific additions are below (in generalised form).

Custom Hosts

Out-of-the-box Lando offers custom hosts under the domain .lndo.site. Some projects however have specific domain requirements, to address this a custom host is specified in the Lando config and a snippet similar to the following is used.

declare -a LOCAL_DOMAINS=("local.example.com" "local.m.example.com")

# Add domains to hosts
for domain in ${LOCAL_DOMAINS[@]}
    # WSL
    if grep -q microsoft /proc/version; then
        declare -a windowsHosts=$(wslpath -u -a C:\\Windows\\System32\\drivers\\etc\\hosts);
        # TODO Can use PowerShell to elevate on demand https://www.raymond.cc/blog/trigger-uac-elevation-from-command-line-with-elevate-utility/
        # TODO This would avoid to need to run in an elevated terminal session
        if ! grep --quiet ${domain} $windowsHosts; then
            echo "Adding ${domain} to Windows hosts file..."
            echo " ${domain}" \
                | tee -a $windowsHosts \
                || { \
                    echo >&2 "Failed to add entry to hosts file - ${domain}, try running WSL terminal instance as admin" ; \
                    exit 1; \
    # Linux and MacOS
    if ! grep --quiet ${domain} /etc/hosts; then
        echo "Adding ${domain} to hosts file..."
        echo " ${domain}" \
            | sudo tee -a /etc/hosts \
            || { echo >&2 "Failed to add entry to hosts file - ${domain}" ; exit 1; }

Open In Browser

Usually placed at the end of the run script, not always what people want as it may result in focus theft (Zoom demonstrates this on a regular basis). Internally we wrap this in an if statement to offer a degree of control.

declare -a LOCAL_DOMAINS=("local.example.com" "local.m.example.com")

echo "Opening in browser..."
xdg-open "https://${LOCAL_DOMAINS[0]}" 2>/dev/null \
    || open "https://${LOCAL_DOMAINS[0]}" 2>/dev/null \
    || `#WSL` cmd.exe /C start "https://${LOCAL_DOMAINS[0]}" 2>/dev/null \
    || echo "I guess not opening in browser :("