Web development on Windows used to be annoying, since the web environment has to be deploy on a remote server or a VM. But with the release of WSL 2 and the integration of the docker backend, thing goes simple now.
Before doing that…
This post assume that you have already got a WSL 2 distro installed. If you don’t or you have a WSL 1 distro, follow the guide on Microsoft Docs to get an install or upgrade.
Phyllali
Download then install the latest Docker Desktop Installer first. After that, launch the Docker Desktop App and go to settings, to ensure that the Use the WSL 2 based engine check box has been checked.

Then go to Resources -> WSL INTEGRATION tab, make sure the distro you want to use is enabled.

Last, open the Windows Terminal and try to run docker command in it. If it works, you can now setup the LNMP environment.
Something else
Nginx
Pull the docker image of Nginx first.
docker pull nginx
Create a work directory for Nginx then.
cd ~
mkdir test-docker && cd test-docker
mkdir www && cd www
Now create a simple index.html for testing.
nano index.html
<html>
<head>
<title>Docker Nginx Testing</title>
</head>
<body>
<div>
<h3>Hello, world!</h3>
</div>
</body>
</html>
CTRL + O then CTRL + X to save. And then go back to create a configuration file nginx-test.conf for Nginx.
cd ..
mkdir nginx && cd nginx
mkdir conf && cd conf
nano nginx-test.conf
server {
listen 80;
location / {
root /www;
index index.html index.htm;
}
}
CTRL + O then CTRL + X to save.
Note, the root parameter in the conf file is not the path on your machine (Neither WSL path above), it’s the “virtual” path in docker container. Don’t worried about this path, we are goanna map the “physical” path to it when running the docker run command.
Finally, run the docker container. As you can see, with the parameter after -v, the “physical” path “/home/user/test-docker/www” was mounted in the specified path “/www” in the container and mapped the 80 port of container to the 8080 port of local machine.
docker run --name nginx -p 8080:80 -v /home/user/test-docker/nginx/conf:/etc/nginx/conf.d -v /home/user/test-docker/www:/www nginx
Try accessing the localhost:8080 from your windows browser. You should see the hello world page now.
PHP
Unlike the Nginx, PHP version maters a lot. So, we are goanna specify the version of PHP image.
docker pull php:7.1-fpm
Then crate the work folder for PHP. The folder etc is to store the config file for PHP, named php.ini.
cd ~
cd test-docker
mkdir php && cd php
mkdir etc
To integrate MySQL to PHP later, the PHP installation must have the pdo_mysql extension installed. So, we need to write a Dockerfile to build our own PHP image. Before doing that, we need a PHP config file storing locally first. Since it’s hard to write a PHP config file manually, we are going to start the PHP container first then copy the default config file out of that.
docker run --name php -d php:7.1-fpm
docker cp php:/usr/local/etc/php/php.ini-development ./etc/php.ini
Then write the Dockerfile. The first line indicates that the new docker image to build is based on the existing image named “php:7.1-fpm”, and the last line means that pdo_mysql extension is to be installed.
nano Dockerfile
FROM php:7.1-fpm
COPY ./etc/php.ini /usr/local/etc/php/php.ini
RUN docker-php-ext-install pdo_mysql
CTRL + O then CTRL + X to save.
Now build our own PHP image, and name it as “php:my-php”.
docker build -t php:my-php .
And now we are able to start the container.
docker run --name php -p 9000:9000 -v /home/user/test-docker/www:/www php:my-php
Connect Nginx and PHP
Nginx don’t know how to handle a .php file, so we need to tell Nginx to send the .php file to PHP when finding one.
Crate a network bridge to link the two containers first. Then connect them to the same network bridge.
docker network create --driver bridge lnmp
docker network connect lnmp php
docker network connect lnmp nginx
After that, changing the Nginx config file to forward php script to php-fpm. Because we have already connected the two containers to a same “network”, so it’s ok for Nginx to find php-fpm by container name.
cd ~
cd test-docker
cd nginx
cd conf
nano nginx-test.conf
server {
listen 80;
location / {
root /www;
index index.php index.html index.htm;
}
location ~ \.php$ {
root /www;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Reload the config file then.
docker exec -it nginx nginx -s reload
After reloading the config file, go to www folder to crate phpinfo.php file testing if the php-fpm working properly.
cd ~
cd test-docker
cd www
nano phpinfo.php
<?php
phpinfo();
CTRL + O then CTRL + X to save. Try accessing the localhost:8080/phpinfo.php from your windows browser. You should see the PHP Version page now.
MySQL
Pull the docker image of MySQL first, since MySQL version matters a lot, specify the version of MySQL image is necessary too.
docker pull mysql:5.7
Then crate the work folder for MySQL.
cd ~
cd test-docker
mkdir mysql && cd mysql
Crate a database file for testing.
nano test.sql
create table student(
id int not null primary key auto_increment comment 'KEY',
name varchar(20) not null comment 'NAME',
age int not null comment 'AGE'
)engine=InnoDB default charset=utf8 comment 'STUDENTTABLE';
insert into student(name, age) values('A', 23);
insert into student(name, age) values('B', 18);
Next, we need to write a Dockerfile to config the password, database name, and copy SQL file inside.
FROM mysql:5.7
ENV MYSQL_ROOT_PASSWORD=123456 MYSQL_DATABASE=test
COPY ./test.sql /var/data/test.sql
Now build our custom MySQL image.
docker build -t mysql:my-mysql .
Then start the container, and attach it to lnmp network.
docker run --name mysql --network lnmp -p 3306:3306 mysql:my-mysql
Then enter the container, and import the test.sql to the database. Enter “123456” as password after running the second command.
docker exec -it mysql bash
mysql -u root -p
mysql> use test
mysql> source /var/data/test.sql
Then crate testmysql.php at the www folder for testing the MySQL server.
cd ~
cd test-docker
cd www
nano testmysql.php
<?php
$dsn = 'mysql:dbname=test;host=mysql';
$user = 'root';
$password = '123456';
try {
$dbh = new PDO($dsn, $user, $password);
$sql = 'SELECT * FROM student where id=?';
$sth = $dbh->prepare($sql);
$sth->execute([2]);
$result = $sth->fetch(PDO::FETCH_ASSOC);
var_dump($result);
} catch (PDOException $e) {
echo 'Error: ' . $e->getMessage();
}
CTRL + O then CTRL + X to save. Try accessing the localhost:8080/ testmysql.php from your windows browser. You should see the SQL output now.
The lnmp environment is now fully configured.