There are several ways of setting up your dev environment on Windows OS. The one we opted to showcase here is using the Windows Subsystem for Linux (we'll use WSL2 or just WSL through this document). Under the hood is a plain Ubuntu 20.04, so you should be able to set up the entire LEMP stack without a problem.
There are two ways you can try to set up the dev environment on WSL, one is more foolproof, but takes a bit longer, the other is by just trying to set up valet-wsl. It is faster, but we've had issues with it in the past.
Installing the required tools
WSL2
Make sure you have either Windows 10 or 11 installed.
For Windows 10, make sure you have:
- For x64 systems: version 1903 or higher, with build 18362 or higher
- For ARM64 systems: version 2004 or higher, with build 19041 or higher
- Builds lower than 18362 do not support WSL2, please update your Windows.
To check the version and build number, either open up a start menu and in the search bar, search for: winver
, or open a run window (Windows key + R) and type winver
.
Make sure you have virtualization enabled in your BIOS, otherwise WSL won't work as intended.
Note
In some cases, you might have to enable certain Windows features on. Most notably you should have Virtual Machine Platform
and Windows Subsystem for Linux
enabled in the Windows features list (they are located in the Control Panel -> Programs -> Turn Windows features on or off link).
Open the PowerShell or Windows Command Prompt as an administrator and type
wsl --install
This command will enable the required optional components, download the latest Linux kernel, set WSL 2 as your default, and install a Linux distribution for you (Ubuntu by default).
The first time you launch a newly installed Linux distribution, a console window will open, and you'll be asked to wait for files to de-compress and be stored on your machine. All future launches should take less than a second.
In the case there was some issue you can check the default WSL version with
wsl -l -v
and if it's not set to 2, set it using the following command:
wsl --set-default-action 2
In the case WSL is already installed you can check the list of available distros with wsl --list --online
and you can run
wsl --install -d <DistroName>
to install a desired Linux distro.
For more information on the WSL installation you can check the following link.
Terminal
Windows has the new Windows Terminal app that you can download from the Windows Store. Once WSL is installed, it should show up in the Terminal app.
Once you've set up the terminal, follow the Git Setup chapter, to set Git up on your system.
PHP
To have more flexibility in choosing your PHP version we are going to use [phpenv](https://github.com/phpenv/phpenv)
. In your terminal app type the following:
curl -L https://raw.githubusercontent.com/phpenv/phpenv-installer/master/bin/phpenv-installer | bash
sudo apt update && sudo apt install -y build-essential re2c libxml2-dev libssl-dev libsqlite3-dev bison pkg-config libbz2-dev libcurl4-gnutls-dev libjpeg-dev libpng-dev libmcrypt-dev libtidy-dev libxslt1-dev zlib1g-dev libcurl4-openssl-dev libonig-dev php-imagick libedit-dev libreadline-dev libxslt-dev libzip-dev autoconf
The sudo apt install
part will install all the necessary libs for the PHP to work.
If you are getting
E: You must put some 'source' URIs in your sources.list
Error when installing the packages run the following:
sudo cp /etc/apt/sources.list /etc/apt/sources.list~
sudo sed -Ei 's/^# deb-src /deb-src /' /etc/apt/sources.list
sudo apt-get update
This should fix the missing sources for the packages
If some package fails to install
Try to install the package manually, and resolve the dependencies. After that you can rerun the sudo apt update && sudo apt install -y build-essential...
command (you can just continue after the package that failed).
Then run the following commands:
PHP_BUILD_CONFIGURE_OPTS="--with-webp" phpenv install 7.4.30
phpenv global 7.4.30
The PHP_BUILD_CONFIGURE_OPTS="--with-webp"
part will enable the WebP support in your PHP.
Make sure you add ~/.phpenv/bin
to your $PATH
for access to the phpenv command-line utility:
echo 'export PATH="$HOME/.phpenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(phpenv init -)"' >> ~/.bashrc
If you are using zsh
replace .bashrc
with .zshrc
.
Then, restart your shell using
exec $SHELL -l
After the installation, you'll need to check the user and group in the ~/.phpenv/versions/$VERSION/etc/php-fpm.d/*.conf
and change it to your current user (which you can check using whoami
command).
Composer
Once you have php set up, you can install Composer using phpenv-composer.
cd ~/.phpenv/plugins
git clone https://github.com/ngyuki/phpenv-composer.git
phpenv rehash
phpenv global 7.4.30
composer --version
You should see the latest Composer version outputted.
Nginx
To install and set up Nginx type the following commands in your terminal:
sudo apt-get -y install curl gnupg2 ca-certificates lsb-release ubuntu-keyring
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \\
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
gpg --dry-run --quiet --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \\
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \\
| sudo tee /etc/apt/sources.list.d/nginx.list
echo -e "Package: *\\nPin: origin nginx.org\\nPin: release o=nginx\\nPin-Priority: 900\\n" \\
| sudo tee /etc/apt/preferences.d/99nginx
sudo apt-get install -y nginx
In order to set up your webserver, you should use nginx config. You can find good config here.
nginx -s stop
cd /etc
mv nginx nginx-previous
git clone https://github.com/h5bp/server-configs-nginx.git nginx
# install-specific edits
nginx
You can modify the default template, located in /etc/nginx/conf.d/templates/example.com.conf
to look like this:
# ----------------------------------------------------------------------
# | Config file for actual-hostname host |
# ----------------------------------------------------------------------
#
# This file is a template for an Nginx server.
# This Nginx server listens for the `actual-hostname` host and handles requests.
# Replace `actual-hostname` with your hostname before enabling.
# Choose between www and non-www, listen on the wrong one and redirect to
# the right one.
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#server-name-if>
server {
listen [::]:443 ssl http2;
listen 443 ssl http2;
server_name www.example.test;
include h5bp/tls/ssl_engine.conf;
include h5bp/tls/certificate_files.conf;
include h5bp/tls/policy_balanced.conf;
return 301 $scheme://example.test$request_uri;
}
server {
# listen [::]:443 ssl http2 accept_filter=dataready; # for FreeBSD
# listen 443 ssl http2 accept_filter=dataready; # for FreeBSD
listen [::]:443 ssl http2;
listen 443 ssl http2;
# The host name to respond to
server_name example.test;
include h5bp/tls/ssl_engine.conf;
include h5bp/tls/certificate_files.conf;
include h5bp/tls/policy_balanced.conf;
# Path for static files
root /home/YOURUSERNAME/Sites/example;
# Custom error pages
include h5bp/errors/custom_errors.conf;
# Include the basic h5bp config set
include h5bp/basic.conf;
# Favicon notices
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location / {
# URLs to attempt, including pretty ones.
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \\.php$ {
#NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
try_files $uri =404;
fastcgi_split_path_info ^(.+\\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_intercept_errors on;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Make sure you replace the YOURUSERNAME
with your actual username. From the config file, you can see that all the sites are located in the /home/YOURUSERNAME/Sites/
folder.
The above template will be used to set up a new site.
Whenever you are making changes to your Nginx config, you can test it using sudo nginx -t
command. If you make an error, the test command will point it out.
MySQL
⚠️ Make sure you don't have MySQL installed on the system already (from some out-of-the-box solutions like Local), as it will conflict with the WSL's installation and you probably won't be able to get it to work correctly.
You can install MySQL 8 by typing the following:
sudo apt-get install -y mysql-server
sudo /etc/init.d/mysql start
sudo mysql_secure_installation
After setup just change the root password by logging in using sudo mysql
command and running the following SQL:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '';
Since it's for local development, you don't need to have a password set up.
Troubleshooting MySQL authentication issues
When trying to access MySQL using the command line you may get an authentication error. In that case you may need to change the default authentication plugin using mysql_native_password
. You'll need to find where your my.cnf
configuration file is located.
mysql --help | grep cnf
You should get something like
order of preference, my.cnf, $MYSQL_TCP_PORT,
/etc/my.cnf /etc/mysql/my.cnf /opt/homebrew/etc/my.cnf ~/.my.cnf
Open your my.cnf
file (using sudo
), and add:
default_authentication_plugin=mysql_native_password
MySQL reports cannot change directory to /nonexistent: No such file or directory
This means that the mysql
user is looking for a home directory which hasn't been assigned. In that case execute:
sudo service mysql stop
sudo usermod -d /var/lib/mysql/ mysql
sudo service mysql start
NodeJS
To install and use NodeJS, you can use nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
or
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
if curl
isn't available.
After nvm has been installed, you may need to run exec $SHELL
(it's a kin to 'restarting' your current shell, so any additions are recognized). Then you can install and use the LTS version of node:
nvm install 16
nvm use 16
Node v16 is the current (at the time of writing) the latest stable version (LTS), so we should always use it.
WP-CLI
WP-CLI is our bread and butter, so you should install it on your system. You can do it by typing the following commands in your terminal:
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
Adding sites
In order to quickly be able to add the sites you can add the following functions in your .bashrc
or .zshrc
file:
# WP-CLI site setup
# Usage create-site {folder name} {database name} {site title}
create-site () {
if [ $1 ]
then
mkdir $1 && cd $1
wp core download --skip-content;
wp config create --dbname=$2 --dbuser=root;
wp config set DB_HOST "127.0.0.1"
mkdir wp-content && mkdir wp-content/themes && mkdir wp-content/plugins;
(
wp db create || true
wp core install --url="$1"".test" --title=$3 --admin_user=admin --admin_password=password --admin_email=admin@sitetitle.com
)
cd /etc/nginx/conf.d
sudo cp templates/example.com.conf .$1.test.conf
sudo sed -i "s/example/$1/g" .$1.test.conf
sudo mv .$1.test.conf $1.test.conf
make-certificate $1.test
restart-server
cd "$HOME/Sites/$1"
wp search-replace http://$1.test https://$1.test
# ADD SITE TO etc/hosts on Windows: C:\\Windows\\System32\\drivers\\etc\\hosts !!!!
else
echo 'Missing arguments!'
echo 'Usage: site-create {folder-name} {database_name} {site title}'
fi
}
# Restart server
restart-server() {
PHP_VERSION=$(php -r 'echo PHP_VERSION;')
# Restart mysql
sudo service mysql restart
# Restart nginx
sudo service nginx restart
# Restart PHP-FPM
sudo ~/.phpenv/versions/$PHP_VERSION/etc/init.d/php-fpm restart
}
# Generate a self-signed SSL certificate
# Usage make-certificate site-name
make-certificate() {
cd /etc/nginx
if [ ! -d "/etc/nginx/certs" ]
then
sudo mkdir /etc/nginx/certs
fi
sudo openssl req -newkey rsa\\:2048 -nodes -keyout /etc/nginx/certs/$1.key -x509 -days 365 -out /etc/nginx/certs/$1.crt
}
Once you've added it, don't forget to run exec $SHELL
to 'restart' your shell.
Then, in your ~/Sites
folder you can just type:
create-site infinum-project infinum_project "Infinum WordPress Project"
This command will:
- Create a directory called
infinum-project
- Download WordPress core
- Create wp-config.php file
- Create a database called
infinum_project
- Install WordPress that will point to
infinum-project.test
URL - Setup Nginx template
- Create SSL certificate for the site (Just fill in the details, it's self-signed certificate)
- Restart the local server so that everything is connected
Last piece of puzzle to do is to go to the C:\Windows\System32\drivers\etc\hosts
file, and add the site to the hosts file
127.0.0.1 infinum-project.test
This part cannot be automated from the terminal, because you'd have to allow terminal to access the Windows system, and that's a security risk.
Troubleshooting
Because WSL is a subsystem, there are some things you need to keep an eye out for, but we'll get to those in a little while.
Exposing hosts to your browser
In order to have the URLs exposed to your browser you need to modify your hosts file.
Open Notepad (or a similar text editor) with administrator access and open the C:\Windows\System32\drivers\etc\hosts
file.
Then, at the end of the file, you should just manually add
127.0.0.1 yoururlgoeshere.test
Changing PHP versions
When you have phpenv changing the PHP version is super easy. Say we want to install PHP 8.1. You'd run
phpenv install 8.1.0
phpenv global 8.1.0
You should use the restart-server
function to restart the webserver. Sometimes though, that may not work, so you might need to restart the WSL.
Internet is slow from WSL
Sometimes that happens in WSL. You can try to restart the wsl from the Powershell terminal by using wsl --shutdown
and restarting it again.
You may need to add the in your /etc/wsl.conf
the following:
[network]
generateResolvConf = false
And in your /etc/resolv.conf
(create it if it doesn't exist) add:
nameserver 8.8.8.8 # Or use your DNS server instead of 8.8.8.8 which is a Google DNS server
Make sure you change the attributes of the resolv file
sudo chattr +i resolv.conf
Then in the Powershell run wsl --shutdown
and restart the WSL2.
Additionally, you may need to run PowerShell as an admin and type the following commands:
Set-NetAdapterAdvancedProperty -InterfaceDescription 'Hyper-V Virtual Ethernet Adapter' -DisplayName 'Large Send Offload Version 2 (IPv6)' -DisplayValue 'Disabled' -IncludeHidden
Set-NetAdapterAdvancedProperty -InterfaceDescription 'Hyper-V Virtual Ethernet Adapter' -DisplayName 'Large Send Offload Version 2 (IPv4)' -DisplayValue 'Disabled' -IncludeHidden
The name of the adapter (Hyper-V Virtual Ethernet Adapter
) might be different. Link to full explanation.