Microsoft has finally delivered a fantastic solution for developing Linux applications on Windows. The Windows Subsystem for Linux, WSL2, is fairly easy to install and get up and running, especially if you are already familiar with Linux. Even if you are not, there are many very good articles about getting a basic installation up and running.
Developing Linux PHP applications using VSCode on Windows 10 is about as stable and seamless an experience one can get. Still, several “gotchas” I ran into were not described in any of the articles I found about setting up LAMP on Ubuntu and WSL2.
I had limited experience with Linux and depended heavily on articles written by those who came before me. While they got me most of the way there, I ran into several problems getting Drupal 8 running without errors and debugging working in VSCode. The solutions were found in the comments sections of questions posted on the internet. This took many hours of searching, and I hope to save people by presenting the solutions I found in this one article.
My environment is Windows 10 20H2, Ubuntu 20.04, PHP 7.3, MariaDB 10.4.17, Drupal 8.9.13, Xdebug 3.02, Windows Terminal, VSCode with Remote – WSL and PHP Debug by Felix Becker packages. I am running WSL from Powershell within Windows Terminal.
Before we get started, here are a few recommendations that may save you time.
Installing and using apt-fast instead of apt can really speed up installs and updates. Where I live, the internet is low bandwidth and slow, and apt-fast is a lot faster than apt.
You can “backup and restore” your Linux distribution using WSL Export and Import. As with any system, it is advisable always to maintain a current backup.
Mariadb Installs Fine, but Cannot Restart or Get Status
Mariadb installation went fine. No errors or warnings. When I tried to check the status, I got an error regarding the system.
System has not been booted with systemd as init system (PID 1). Can't operate.
The reason for this error is that Microsoft does not support systemd in WSL. Fortunately, Arkane Systems created a package system-genie to enable systemd . I suggest reading their web page thoroughly before trying the following instructions, which were taken from that page. There are slightly different instructions for distributions other than Ubuntu.
First, you need to Install the .Net 5.0 runtime
$>sudo sudo apt-fast install -y apt-transport-https
$>sudo apt-fast update
$>sudo apt-fast install -y dotnet-sdk-5.0
Next we need to Configure the wsl-transdebian Repository
$>wget -O /etc/apt/trusted.gpg.d/wsl-transdebian.gpg https://arkane-systems.github.io/wsl-transdebian/apt/wsl-transdebian.gpg
$>chmod a+r /etc/apt/trusted.gpg.d/wsl-transdebian.gpg
$>cat << EOF > /etc/apt/sources.list.d/wsl-transdebian.list
$>deb https://arkane-systems.github.io/wsl-transdebian/apt/ bullseye main
$>deb-src https://arkane-systems.github.io/wsl-transdebian/apt/ bullseye main
$>apt-fast update
Now we can install the system-genie package.
Exit your Linux shell then shut down WSL from Power shell
Restart WSL with a genie from the Powershell prompt.
You will see “Waiting for systemd….!!!!!!!!!!!!!!!”. It takes 180 seconds to load fully. Just wait for it to finish. When it is done, your new shell window should look like this:
Timed out waiting for systemd to enter running state.
This may indicate a systemd configuration error.
Attempting to continue.
Confirm genie installed and systemd is working:
You should get the status output for mariadb. Note that systemctl status mysql also works.
Arkane Systems recommends shutting down your WSL genie session with wsl –shutdown. This will free up all memory used by WSL in Windows.
Drupal installs But No CSS Gets Loaded
After running the basic install for Drupal 8, the pages had no formatting. Viewing Page Source showed that no CSS files were being loaded. It took me two days to figure this one out, but the short story is Drupal assumes apache2 is using the /tmp directory, but it is not. By default, apache2 is configured to use a private tmp directory. Strangely enough calling, sys_get_temp_dir( ) from php return /tmp, yet that is not what apache2 is using. When Drupal creates its optimized css and js files, it first tries writing them to the /tmp folder, then moves them to the destination folder, typically sites/default/files/css and /js. But apache2 is not using /tmp, so this process fails, and none of the css or js files. Unchecking Aggregate CSS and Javascript files will bypass this, but then all of the individual css and js files get loaded, so this is not a solution.
You can confirm this problem /tmp not being accessible with the following simple php file. It creates a tmpfile and displays the file name. Initially, the file name will be blank because the call to tmpfile() returns NULL. I put the following code in test.php and called it from my site, localhost/mysite/test.php
echo "<html>\n";
echo "<head>\n";
echo "<title>My Second PHP Example</title>\n";
echo "</head>\n";
echo "<body>\n";
echo "<p>If you view the page source \r\n you will find a newline in this string.</>";
echo "<p>testing</p> " ;
$tmpDir = sys_get_temp_dir( );
echo "<p>TMP direcory = '$tmpDir'</p> " ;
$file = tmpfile();
$path = stream_get_meta_data($file)['uri'];
echo "<p>Path of tmp file = '$path'</p> " ;
echo "</body>\n";
echo "</html>\n";
?>
This resulted in "Path of tmp file ="
I found a solution to this in the comments of Stackoverflow question by user One In a Million Apps. This solution changes the apache2 configuration from PrivateTmp=true to PrivateTmp=false. Note that changing apache2 to use a private tmp directory was done for security reasons, and most apps can be configured to use a different tmp folder. I tried that with Drupal but could not get it to work. This is my first attempt at running Drupal on Linux, and I wanted things to “just work” on my laptop with little concern for security.
First, look for the file containing PrivateTmp using this from the /lib directory:
This gave me a long list of matches. Look for the one containing the file apache2.service. In my case it was found at /usr/lib/systemd/system/apache2.service. copy this file to the /etc. directory. Edit /etc/apache2.services and change PrivateTmp=true to PrivateTmp=false, save and restart the apache2 service.
Re-run the test.php page again, and you should get the tmp file named displayed, confirming access to the /tmp folder.
Clear all the Drupal caches and reload the pages. They should now display correctly. I do not know why, but the Drupal Clear Cache function does not always work for me. Manually deleting all files in sites/default/files/css js, then using PhpMyAdmin to empty the cache tables always works.
Setting up VSCode Debugging
Configure Xdebug
First, install the Remote – WSL, and PHP Debug by Felix Becker packages to VSCode.
I then installed Xdebug
This installed version 3.02 of Xdebug.
I tried configuring it by following the many examples on the internet. Nothing worked. Turns out most of the examples are for Xdebug 2.x, and those configuration settings no longer work with 3.x
I finally got it working with the following php.ini settings.
I had to add the following to both /etc/php/7.3/apache2/php.ini and /etc/php/7.3/cli/php.ini on my system.
You can find the location of your xdebug.so by moving to the /lib directory file then running
zend_extension = ./lib/php/20180731/xdebug.so
xdebug.start_with_request = trigger
xdebug.mode = debug
xdebug.discover_client_host = 1
xdebug.log = /tmp/xdebug_remote.log
xdebug.client_port = 9003
Configure VSCode
Remote debugging in VSCode uses a launch.json file stored in the root of your project directory in .vscode/launch.json.
You can create the launch.json file through the VSCode UI, but I find it easier to create it manually. Move to the root of your website and create a .vscode directory. Create a launch.json file and load it in VSCode.
$>cd .vscode
$>touch launch.json
$>code launch.json
Put the following json in the file and save it.
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Listen for XDebug",
"type": "php",
"request": "launch",
"port": 9003,
"stopOnEntry": true,
"log": true,
"pathMappings":
{
"/var/www/html": "${workspaceRoot}"
}
},
{
"name": "Launch currently open script",
"type": "php",
"request": "launch",
"program": "${file}",
"cwd": "${fileDirname}",
"port": 9003
}
]
}
Note under pathMappings, where I have “/var/www/html”, you should put the full path to the root of your website.
Close VSCode. In your WSL Linux prompt move back to the root of your web site and load the project in VSCode. Assuming you are still in the .vscode directory,
$>code .
This should load the project in VSCode, and you should see the full directory tree of your project on the left. Open your start page, such as index.php, and add a breakpoint. Press F5 to start debugging. Go to a web browser and load the site. Switch back to VSCode, and you should see it stopped at your breakpoint.
Code Does Not Run With zsh Shell
By default, WSL is set up to work with the Bash shell, and it sees the path to the VSCode executable in the PATH. I switched to zsh, and VSCode would no longer run. The fix was to put an alias in .zshrc
$>code .zshrc
Add the following alias, which points to the full path to the code executable folder, as seen by Ubuntu in WSL. Replace YourUserName with your actual Windows user name.
You now need to reload the zsh configuration with
Code should now load from the zsh shell.
That’s it!! These steps finally got Drupal and VSCode debugging working correctly for me. It took me two days to figure this all out. I am a noob! Hopefully, this works for you and saves you some time.
Just a reminder of my environment. Windows 10 20H2, Ubuntu 20.04, PHP 7.3, MariaDB 10.4.17, Drupal 8.9.13, Xdebug 3.02, Windows Terminal, VSCode with Remote – WSL and PHP Debug by Felix Becker packages.
Happy Coding!