It is common knowledge among developers that setting up your development environment for a new project can be a time consuming task that no one really enjoys doing. Thankfully, tools such as Kubernetes and Docker allow for the easy automation of these setups which can be shared between all members of a team. Be that as it may, some challenges still arise in using these tools due to varying operating systems and the way in which they operate, especially when it comes to Windows. On a recent project, many issues came up when trying to set up a common JavaScript stack (Node + Angular) using Docker Compose in a Vagrant Ubuntu Virtual Machine (VM) with VirtualBox as a provider. Note that if you would like to simply see the code, skip to the end of this article.

If you have access to Docker for Windows then it would be easier to set up Docker compose on Ubuntu Bash as described in this article, but if you don't have access to Windows Premium (and thus no Hyper-V), this is a good alternative.

Background

This example project is set up in such a way that the backend, devops and frontend folders all exist within the same project folder. Within the backend and frontend folders are contained the source code folders and the node_modules folders (more on this later). The devops folder contains the docker-compose.yaml file which is used by Docker compose to set up the project using the docker-compose -f ./docker-compose.dev.yaml up --build command from within the devops directory. The project folder structure is illustrated below:

backend/
├── node_modules
├── src
└── etc...
devops/
├── docker-compose.yaml
└── etc...
frontend/
├── node_modules
├── src
└── etc...
Vagrantfile

Simple in practice, but try running this from an Ubuntu Vagrant machine using VirtualBox as a provider on a Windows host and all hell breaks loose.

Node Modules and Shared Folder Issues

By default, the folder that contains the Vagrantfile is mounted to /vagrant in the VM, which means that all of the folders within that same folder get shared between the Host machine (running Windows 10 in this case) and the guest VM (running Ubuntu 16). The same directory structure from the VM's perspective can be seen below:

vagrant/
├── backend/
│   ├── node_modules
│   ├── src
│   └── etc...
├── devops/
│   ├── docker-compose.yaml
│   └── etc...
└── frontend/
    ├── node_modules
    ├── src
    └── etc...

As some of you know, the first step in setting up a node project is to install the project package dependencies using the npm install command. This is where the first major issues occur as NPM packages generally use symlinks for binaries within the package. As previously stated, the node_modules folders get shared between the host and guest machines meaning that those files must adhere to the constraints from both operating systems in some cases. In this particular case, Windows has terrible support for symlinks which results in multiple different errors for multiple different packages, and thus a failed installation. Additionally, Windows also has a maximum path size of 260 characters which adds further complications.

A simple fix is to tell NPM not to use symlinks during installation using the npm install --no-bin-links command, but this generally does not work for more complex packages. A second fix, which sometimes works but is usually finicky, is to run Vagrant from a command prompt with administrator permissions after adding the following line to the Vagrant config file which allows for symlinks to be used on the VM:

Unfortunately this did not work for me, and this solution still doesn't help with the 260 character path limit imposed by Windows.

Generally, the content in the node_modules folder is not actually needed on the host machine. A better solution is, thus, to store the content of the node_modules folders within the VM and not have them synced across to the Windows host. This can be achieved by running the following commands from within the VM as shown here:

File Change Detection

File change detection is an important part of development and has many use cases such as allowing various components of the project to reload when the code has been altered. In our current example project, nodemon watches out for any changes in the backend code of our Node server, and the ng serve command watches out for any changes in the frontent code.

Unfortunately for us, file change events do not automatically get propagated from our Windows host to our guest Ubuntu VM. This means that unless we come up with a solution, the whole development environment will have to be re-built using the docker-compose command every single time we want our new code changes to reflect on the system, resulting in unnecessary delays.

Luckily though, a simple Vagrant plugin named fsnotify currently exists which solves this issue. It should be noted that a second Vagrant process needs to be running in parallel using the vagrant fsnotify command, but a pull request exists on the fsnotify repository which can allow the plugin to run as a daemon during the vagrant up command. To automatically download and use the plugin through the Vagrantfile, simply add the following lines to your Vagrant configuration file:

A drawback is that from the technique being used in fsnotify (touching modified files), when multiple file changes occur within a window of 2 seconds only the first file change will be detected. This constraint should not cause any issues in the context of projects similar to ours as files typically do not change that quickly during development. Alternatively, if this causes issues, polling can be used but keep in mind that this can potentially slow down the application significantly.

Final Vagrantfile and Shell Scripts

Find below the Vagrantfile and associated shell scripts which were used to set up this project environment.