As a web developer, I frequently use a lot of command line tools to make my life easier. I prefer to be able to assemble a computer from whatever hardware I want, so I don’t like Macs and stuff like this. Linux desktops are good enough our days, but I love to play video games from time to time, and it wouldn’t be effortless to keep two different operating systems for work and entertainment. My best compromise was to use Windows.
But Windows command line sucks. It sucks a lot. And I don’t talk about the
cmd shell — it’s ridiculous to talk seriously about it in 2018. A little bit more modern command line attempt of Microsoft, the
PowerShell, has a lot of power, but it lacks the elegance and composability of Unix tools. It’s a whole new world where you need to learn funny wtf commands like
Remove-Item dir -recurse to make the simplest things happen. And, of course, nobody outside the geostationary orbit of Microsoft supports this. To be productive with a command line in Windows you need to do some research, and your road won’t be dull.
The first solution I found was pure
Cygwin: It provides a lot of Unix tools, fancy shells like bash, fsh or zsh. It’s nice enough, but it’s a pain to run something more complicated smoothly. Try, for example, to set up
byobu, you’ll find yourself in the middle of a night, drinking the fifth cup of coffee and trying to understand why the hell
make silently fails.
My second attempt to get a nice terminal on Windows was
babun. It’s a great project, trying to fit
Cygwin, modern looking terminal emulator named
mintty and even a command line package manager named
pact into a standard couple-of-clicks windows installer. But it’s still just a wrapper over Cygwin. It makes some things simpler while ignoring the helluva rest of other issues.
I also use Docker containers a lot. So the most simple and straightforward solution for me was to use a virtual machine to run a real Linux. The drawbacks are obvious: disk access performance and CPU performance are much lower than they would be on the bare metal system; file access is a quest; etc.
But things are going better with recent updates to WSL and Docker for Windows. We finally came to a point where those technologies together provide a better, more stable way to work. And I’d love to tell you how to set up an excellent development environment using Windows, WSL, and Docker for Windows.
Please, note! I tested all described operations on Windows 10 with Fall Creators Update installed. To be more specific, my Windows version is 10.0.16299.192. If you have older Windows, it is worth it to update first. Check the version with
WSL stands for a Windows Subsystem for Linux. It’s not another virtualization technology but a way to run Linux ELF binaries natively in Windows. It installs a real Linux distro to the system directory and provides a wrapper which can deceive programmes about their environment. In theory, there is no overhead for virtualization and programs should run as fast as they would on real Linux system. But the tech is very young, and performance is still to be improved.
The best way to install a Linux distro to WSL is to use
[LxRunOffline](https://github.com/DDoSolitary/LxRunOffline) tool. The official way still lacks a lot of features: for example, it doesn’t allow installation to a non-system drive (which was crucial for me).
- Go to “Turn Windows features on or off”, find “Windows Subsystem for Linux” and enable it. Restart the PC.
LxRunOfflinetool and place it to the volume where you plan to store WSL files. Note! It’s important because it’s not possible to move those files across volumes later. Download desired system image (you can look for supported images in LxRunOffline’s wiki). I selected Ubuntu image created by Microsoft especially for WSL. Place the image in the same volume.
- Install Ubuntu to WSL:
LxRunOffline install -n Ubuntu -f 16.04.2-server-cloudimg-amd64-root.tar.gz -d d:\wsl
- WSL is ready! It that simple.
Install Terminal Emulator
[wsltty](https://github.com/mintty/wsltty). It’s a modified version of
babun is using. It would get you straight to the new WSL bash prompt. I suggest installing zsh (oh-my-zsh) and byobu as those tools are great for productivity.
Install Docker for Windows
Docker for Windows from it’s official website. Unfortunately, Windows can’t run Linux containers natively — virtualization is inevitable.
Docker for Windows is based on Microsoft’s
Hyper-V technology, so it turn
Hyper-V on first.
dism.exe /Online /Enable-Feature:Microsoft-Hyper-V
Hyper-V must be disabled to use another virtual machine software. It’s not possible to run
VirtualBox at the same time. You can disable it later with the following command:
dism.exe /Online /Disable-Feature:Microsoft-Hyper-V
[Docker for Windows](https://docs.docker.com/docker-for-windows/) via GUI installer. Open settings via tray icon context menu and specify desired directory for Docker’s files and don’t forget to enable
Expose daemon on tcp://localhost:2375 without TLS option. It is required to connect from WSL.
Open the WSL command line. Install Docker client there (like you would in normal Linux). Execute the following command to specify Docker host.
echo "export DOCKER_HOST=tcp://0.0.0.0:2375" >> ~/.bashrc && source ~/.bashrc
The technology is still very young, it was a beta a few months before. There are issues which still need to be solved.
Docker volumes don’t work from WSL
Docker for Windows normally expects disks to be accessible by
/c path. However, disk C in WSL is accessible by
/mnt/c path. The simplest solution is to create a symlink. But be sure to run docker commands from
/c path, not
echo "sudo mount --bind /mnt/c /c" >> ~/.bashrc && source ~/.bashrc
Windows VPN breaks WLS DNS
The solution is to manually write
cp /etc/resolv.conf /etc/resolv.conf.new unlink /etc/resolv.conf mv /etc/resolv.conf.new /etc/resolv.conf
Open the file and remove the first line, which says ”# This file was automatically generated by WSL. To stop automatic generation of this file, remove this line.” Add following lines to the top, where IP is your VPN’s DNS:
options rotate nameserver IP