What is Docker? You already should to know the answer to this question, and if this is not clear, then you should see docker intro. In this blog post, we will spin PHP development environment (PHP Dev ENV). If you know what is docker, and you have it installed on your machine and all you need is to set up docker-compose.yml and create project file structure, then skip this post and go directly to post on spinning docker apache – PHP image and setting up php.ini file. If that’s not the case, then keep reading this tutorial for beginner on how to install docker on Windows.

Step 1 – Setup Docker on Windows 10

How to set up Docker on Windows 10?

Before you run docker, you need to enable Docker requirements in Windows features. Docker will not run on Widows 10 machine builds before 2014 so keep that in mind. If that’s the case, perhaps this is the time to make some updates. Docker also require from you to enable windows features like Virtual Machine Platform, Windows subsystem for Linux (WSL-2).

If you run Windows PRO version, you are also able to enable Hyper-V and Windows Hypervisor Platform. While Hyper-V is enabled only in Windows PRO version, it is not essential until you have enabled WSL 2 which is default for docker anyway.

Enable fallowing features in Windows Features (if present)

Click on Windows Icon and type “Turn Windows Features”. Ten enable these features:

  • Virtual Machine Platform
  • Windows subsystem for Linux
  • Windows Hypervisor Platform
  • Hyper-V (only on Windows PRO) see: https://www.youtube.com/watch?v=MOuTxfzCvMY

See also https://docs.docker.com/desktop/windows/troubleshoot/

Enable Virtualization. It is often required to enable this features via BIOS settings. To check if Virtualization is enabled, run Task Manager and see here (also required for VirtualBox):

Other issues:

Sometimes, Virtual-Box or other virtualization software might block hypervisor from starting on system start. To fix that, Open PowerShell terminal as admin and run:

bcdedit /set hypervisorlaunchtype auto

Step 2 – Getting to know important docker files

Basic Docker usage is simple. You just need to understand basic concept behind Docker.

How Docker works in a nutshell?
Basicaly you create Dockerfile to generate Docker image. From docker image you spin docker container (like Apache server, MySQL, Node … those can be run as separate containers). Containers can be maped so that we can access them in our host machine. We can also run multiple conainers which can access each other. Thas all from basic concept.

Now lets get to know important Docker files:

  1. Dockerfile
  2. docker-compose.yml
  3. .dockerignore

Dockerfile – In this file we give docker instruction to build image, execute CLI scripts and perform various of other actions. Containers with in this image are “standalone” and need to be yet related, so they can get accessed by other containers. This is the job of another important file, which is docker-compose.yml.

docker-compose.yml – This is the configuration file in which we can associate containers, set up ports and volumes to be able to persist data (changes to files and database modifications).

.dockerignore – Works like .gitignore. In this file, you can exclude files from being processed by the Dockerfile. For example, if you want to move composer.json but not the vendor file – You would list vendor directory inside .dockerignore as you would do that in .gitignore.

Step 3 – Differences when using Docker on Windows vs native Linux

When using Docker on Windows, you have to remember about a few very important differences.

1) On Windows, networking is managed by Windows operating system.

2) HOST path structure vary with regard to different subsystems and terminals. Linux/Windows, Linux CLI/PowerShell/cmd.exe/Git Bash. So keep that in mind when you run in to a path related issue or Volumes are not working. Rede more about path URL structure in the next section.

Understanding VOLUME path on Windows HOST

While docker works in multiple environments, there are some differences in creating a hosts’ path URL. Different operating system / CLI Ubuntu / windows, Linux terminal / Windows PowerShell / Git Bash works slightly different way. If you don’t know those differences, you will have some hard time trouble shutting tutorials made in different environment or using different set of tools.

On Linux:

docker run --rm -ti -v /home/user/work:/work con1

On Windows (cmd.exe):

docker run --rm -ti -v C:Usersuserwork:/work con1

On Windows (Git Bash):

docker run --rm -ti -v C:\Users\user\work:/work con1
docker run --rm -ti -v /$(pwd):/work con1 ls //work

Note how $(pwd) had to be escaped with “/” as well as ls /work command.

Testing PATH

To verify whether the errors are generated from your script, or from another source, you can use an environment variable. For example:

$ MSYS_NO_PATHCONV=1 docker run --rm -ti -v $(pwd):/work con1 ls /work

It only expects the environment variable here. The value doesn’t matter.

In some cases, MSYS also transforms colons to semicolon. Similar conversions can also occur when using ~ because the POSIX layer translates it to a DOS path. MSYS_NO_PATHCONV also works in this case.

[official Docker Docs on path troubleshoot]
[official Docker Docs on Volumes usage]

Step 4 – Build Dockerfile for PHP

Things you need to know.

Before you start, take a moment to get to know Docker common flags:

-it is short for --interactive + --tty :When you docker run with this command it takes you straight inside the container.

-d is short for --detach :which means you just run the container and then detach from it. Essentially, you run container in the background.

-p or -P are short for --publish :Ports exposed from Linux are forwarded to the host machine (Windows http://localhost:8080 in our case).

-v is short of --volume. Volumes are used to Volumes are the preferred mechanism for persisting data generated by and used by Docker containers. While bind mounts are dependent on the directory structure and OS of the host machine, volumes are completely managed by Docker. Volumes have several advantages over bind mounts [Read more on Volumes in Docker Docs]. Diffrence between –mount nad –volumens [Read more on Mounts in Docker Docs]

-w="", --workdir="" :Working directory inside the container. The default working directory for running binaries within a container is the root directory (/). It is possible to set a different working directory with the Dockerfile WORKDIR command.

-u="", --user="" :Sets the username or UID used and optionally the groupname or GID for the specified command. The followings examples are all valid:
–user=[ user | user:group | uid | uid:gid | user:gid | uid:group ]

[Get to know other Docker flags – in Docker Docs]

Now once you know all important things you can start playing with Dockerfile

In this file, we ask docker to pull node and PHP images and move index.php file ( “.” – current directory) to a location in the container which is “/var/www/html/”.

FROM php:7.4-cli
FROM php:7.4-apache
COPY . /var/www/html/
CMD [ "php", "./index.php" ]

To build this file, you would use docker build command like so:

$ docker build --tag img1 .

Now once we have build our image from Dockerfile we can list our images with docker images. If you wish to remove an image. You could use docker rmi command. Next run the image as container with run command. This could look something like this:

docker run -p 8080:80 --name con1 -d img1

Debugging container

If you would like to see container logs run docker logs con1.

Clean docker images and containers

Keep it clean!

Your docker images and containers can grow very fast. Especialy, when we are building and testing new images. Thats why is quite handy to know few commands. First will kill all running containers at once. Seckound will remove all unused containers and volumes. Those two commands will make your docker a very clean place.:

docker kill $(docker ps -q)
docker system prune -af

Now you are ready to work in cleaned up docker. Alternativly you could just use –rm flag when you need the container to be deleted after the task for it is complete. This is suitable for small testing or POC purposes and saves the headache for house keeping.


Compose file allow us to spin multiple containers and relate them in a way they can interact with each other. It also allows us to create volumes. Volumes allow us to persist information in our containers, so that they are not lost when we restart the container.

version: '3.1'

      context: .
      dockerfile: Dockerfile
    container_name: web
      - "8080:80"
    image: mongo:3.6.1
    container_name: db
      - mongodb:/data/db
      - mongodb_config:/data/configdb
      - 27017:27017
    command: mongodb


To run docker-compose.yml file do:

docker-compose up -d

Debugging docker-compose.yml:

docker-compose -f docker-compose.yml logs

Running docker on Windows – important note

Docker on Windows works best when run from wsl2. You should also create docker volumes and run docker from wsl2 terminal for overall better performance. Running docker directly from windows will more likely to cause a long delay.

! Note that on a Windows machine you might need to wait about 40 second or longer. Before, the http://localhost:8080 will be available. This is docker bridge networking issue, as it add another networking layer (you could use host networking layer instead of bridge for better performance: performance-issues-running-nginx-in-a-docker-container). However, according to docker docs, the host networking driver only works on Linux hosts, and is not supported on Docker Desktop for Mac, Docker Desktop for Windows, or Docker EE for Windows Server. This is why it might be a good reason to use docker directly from a Linux WSL2 machine which solve the performance issue.

Generally, you install docker desktop on Windows as usual. Then set docker desktop to use WSL2 (if not default), and then open terminal and create project with in wls2 directory. Then run docker from with in WLS2 cli.

Editing your project within WSL2 directory structure is very easy. VS Code supports editing of WSL2 projects like it would run editor locally on your widows machine. Also remember to mount your volumes in Linux style. This setup allows you to run docker on Windows with WLS2 without any performance issue, even when using bridge network.

Also note that you need to have Windows docker desktop app turned on before you will be able to access docker cmd with in your WLS2)

See also:


See also some more advance setup:


Install Linux dist on Windows (WSL): https://docs.microsoft.com/en-us/windows/wsl/install

Would love your thoughts, please comment.x