(See Updates section at end for latest notes.)


In the middle of the journey of our life I came to myself within a dark wood where I was no longer comfortable identifying myself as a Mac user. Superficially, the relationship with my MacBook air had been rewarding. It lasted all day, ran the necessary few bits of commercial software I needed, and otherwise did a great job of pretending to be running unix. Also, I felt a pleasant sense of well-moderated individuality when my fellow developers and I exchanged knowing glances at our discreetly weathered, FP-inflected decal collections.

But there is an equivalent, in the realm of our relationships with machinery, to the discovery that someone we know has seen Cats more than a dozen times or regards Robert Ludlum as a serious author. For me, that came with the breathless speculation about an upcoming Mac "refresh," said to feature an "e-Paper function key row" and better multimedia Siri integration (or something). This sort of vulgarity did not sit well with my professed enthusiasm for middle-period Mahler and/or the Oxford comma.

Also, I couldn't follow along with cool posts on strace.

If I had the courage of my convictions, I would just run linux and be done with it, but hewing that way without revealing myself as a pretentious idiot would be difficult. To use linux without embarrassment, one actually needs to know what one is doing, and I do not.

Armed with this self-knowledge, I hatched the idea of selling the Mac and buying a newish Windows 10 laptop. I would be a contrarian! Not only that, but, since I don't hang around with anyone who knows anything about developing on Windows, I could easily spin my accomplishments as impressive.

Reality bytes

Getting anything done on Windows is a bloody fucking nightmare. They made a big deal recently about WSL, the "Windows Subsystem for Linux". It's also known as "bash for windows", which is a grotesquely stupid name that belies the ambitious goal of executing undiluted linux elf64 binaries by fully emulating the linux kernel interface. Unfortunately, it doesn't really work, a fact which is both illustrated and exemplified by lists of programs you can run. Yeah, it does a decent job of letting you pipe about text, but, while you might evaluate "programs you can run" with the 80:20 rule, the decency of an API is better appraised with the 100:0 rule. Installing a mountain of dependencies and than discovering that the JVM hangs is the sort of experience that not even masochists enjoy.

If you are indeed mostly interested in a Potemkin village of apparently functional unix command-line utilities, there are less ambitious projects like cygwin and MinGW, the latter of which underlies git-bash. Unlike WSL, these do not attempt binary compatibility with actual linux, but require you to compile especially for the framework. The process for doing so is pretty well baked by now, so the likelihood that someone else has already run into and solved the dev-related issue you have is quite high. Generally speaking, google is your friend.

Sometimes, though, it's a friend in the misery-loves-company sense. It was, for example, pretty shocking to discover that python setuptools has been broken under MinGW for half a year, and nobody really cares. To install pelican (without which you would not be reading this deathless prose), it is actually necessary to patch two different setuptools source. Moreover, aborted setups will have left your installation in an inconsistent state that can only be repaired by manually removing files. (Doubly exasperatingly, another thing you can't install without patching is virtualenv, so there is truly no way to avoid a day of awfulness.)

Similar problems crop up with other, supposedly OS-independent languages and tools. For example scalameta examples don't run. These issues reflect the broad preference of the development community for working in linux-like environments. Windows problems get ironed out when its time to ship to end-users... but who cares about end users?


If WSL, MinGW and Cygwin are a little half assed, perhaps virtualization can provide the full buttock. The remainder of this post is a recipe for getting linux to run well and continuously enough under Windows 10's built-in "Hyper-V" that you truly have the best of both worlds. It's a little complicated.

Installing Hyper-V

Read about requirements first. In summary, you can't be running the cheaper "Home" variety of Windows, and systeminfo.exe must report that your hardware supports virtualization.

If you pass those hurdles, may need to make sure that virtualization is enabled in your BIOS. Then you can enable it in Windows from the "Turn Windows features on or off" screen, and reboot.

Setting up a network environment

You will want your linux installation to be able to communicate both with the outside world and with the windows host. What seems to be the easiest way to accomplish this is to create two "virtual switches", which will show up as two interfaces in linux.

Run the "Hyper-V Manager" and from the right-hand pane launch the "Virtual Switch Manager." Create two switches.

  1. Call the first "External Virtual Switch" and select an "External network", connected to your host's main network adapter.
  2. Call the second "Internal Virtual Switch" and select "Internal network" (not "Private network"; that's a network that not even the host can see).

It will turn out that this isn't quite enough, but we'll repair it later.

Create the virtual machine

Again from the right-hand Hyper-V pane, select "New Virtual Machine" and follow the wizard. Things to be careful about:

  1. You want a "generation 2" VM. Otherwise it's only going to know about quaint old things like floppies and CD-roms. This setting cannot be changed.
  2. You should disable secure boot, which apparently causes some versions of linux not to boot.
  3. You do not want dynamic memory, as appealing as that sounds, because there are memory corruption problems, supposedly related to linux issues. You will be able to adjust the amount of memory later, but if you're serious about living in both worlds simultaneously, you'll set it to half the available RAM.
  4. Create a new virtual disk. Have the courage to make it large.
  5. On the networking page, pick the External switch.
  6. Under "Installation options', you might as well enter the location of your linux .iso.

Now, from the lower right pane, start the VM and connect to it (I think one of the reasons Hyper-V is less resource intensive is that it doesn't bother emulating the display unless you're connected). Go through the full installation process, with as many reboots as it requires.

Fix the networking

At this point, you have a bridged networking environment. Both the VM and your host have DHCP leases from the network you're on. To connect from the host to the guest and vice-versa, you'll need to know the IP addresses you've been given, which will be a bit awkward (and it will be impossible to maintain a connection when you're away from WiFi).

Shut down the linux guest, and select its "Settings". Choose to "add hardware", and add the internal network switch. You'll also find an "automatic start" option, and it will be best to disable this entirely.

Now, in the Windows control panel, find the "Network Connections" page. You'll see a bewildering variety of little networky icons, two of which will have the names you gave to your virtual switches. Select "Properties" for the internal switch, highlight the "Internet Protocol Version 4" line, and select "Properties" again. Set a static IP address with a nice ring to it, preferably not one starting 192.168.1, as that will someday soon clash with a dynamic IP address you're handed on another network. The mask should be, and you can leave gateway blank.

Boot the guest; ifconfig should now show you two eth interfaces. Of those, one will have an IP address and the other won't. Whichever one doesn't have an address is the internal switch; note its hardware address. Depending on your distro, the next steps will be different. On Ubuntu, you'll right-click on the network icon on the menu bar and "Edit connections." There will be two connections, with unhelpful names. Open them in turn to find the one with the hardware device ID of the internal switch. Then, on that connection's IPv4 tab, manually set an address on the same subnet as you chose on windows (and the same subnet mask).

Verify now that you can ping the linux guest from the windows host. You might also install openssh-server and verify that you can ssh to the linux guest. Most likely, you will not be able to ping the windows guest from linux, however. This is due to the stupidity of the windows firewall.

Fix the firewall

Windows supports the notion of a network "profile", which can be either "private" or "public". When you connect to a network for the first time, you're given the option of allowing network discovery, and if you say yes, you'll have designated the network as private. Otherwise, it's public.

The Windows firewall can be turned on and off separately for public and private networks. You certainly don't want to turn it off for public networks, but turning it off for private networks is an acceptable compromise. That can be done from the control panel in a more or less obvious way.

You will find, with some annoyance, that Windows had decided to designate your internal switch as a public network. Changing this is incredibly difficult and will require the Windows abomination known as PowerShell. From the start menu (or whatever they call it these days), find PowerShell, right-click it and launch as administrator. Within PowerShell, type Get-NetConnectionProfile. You should see your external and internal switches; at least the internal one will show "NetworkCategory: Public".

You can change this state of affairs with something like

Set-NetConnectionProfile -InterfaceAlias "vEthernet (Internal Virtual Switch) 2" -NetworkCategory Private

(using the exact InterfaceAlias listed by the previous command).

This is nice, but you'll find that it gets reset every time you boot. We thus want to fix it every time we boot. Find a nice directory and create SetInternalPrivate.ps1, containing this nonsense (adjusting the first line as appropriate):

$log = "c:\Users\YOU\wherever\SetInternalPrivate.log"
New-Item -ItemType "File" $log -Force
Get-Date | Out-File $log -Append
Import-Module NetConnection
Get-Module | Out-File $log -Append

$profiles = Get-NetConnectionProfile | Where-Object {$_.InterfaceAlias -like "*Internal Virtual Switch*"}
$profiles | ForEach-Object {$_ | Set-NetConnectionProfile -NetworkCategory "Private"}
$profiles | Out-File $log -Append
Get-NetConnectionProfile | Out-File $log -Append

Set-NetFirewallProfile -Profile Private -Enabled False | Out-File $log -Append
Get-NetFirewallProfile  | Out-File $log -Append

The last line makes sure the firewall is off for private networks - another setting that seems to get undone by Windows on your behalf with startling frequency. If you run all of this in your administrator PowerShell window, you should now be able to ping the host from the guest.

In the same directory, create a SetInternalPrivate.bat containing

 powershell.exe -Command c:\Users\YOU\wherever\setInternalPrivate.ps1

Now find Task Scheduler in the start menu, right-click and run as administrator. There's a little folder structure on the left; create a new folder for yourself. In that folder, Create a Basic Task, triggered "When I log on", to "Start a Program" and choose your .bat file. Immediately select the new task's properties.

There are a few possible ways forward now, but the most robust seems to be the following. Under security options, run it as yourself and "Run only when the user is logged on", but check "Run with highest privileges." On the Conditions tab, you want to uncheck "Start the task only if the computer is on AC power" (you can imagine how much fun I had discovering that).

Now reboot the windows host. If all has gone well, when you log in, you'll see a black command window pop up to run the script, and you should then be able to verify that the internal switch is private and that the firewall is off for private networks.

From the Hyper-V console, start the VM. The reason we disabled auto-startup is that we wanted to make sure it got started after the network was fixed.

Sharing is caring

On Windows, make sure that your home directory, or some other directory is shared, and shared only with you. (I.e. not literally "shared", but "made available in some sense that we don't wish to describe using the English language".)

On linux, we invoke the ritual incantations to mount this shared drive. The first horror is that we'll have to store our windows password in plain-text. Create something like /home/you/.credentials-cifs containing


and chmod it to 0600. (Hopefully, you chose to encrypt your windows disk with bitlocker, because otherwise, yes, your password is potentially visible to anyone with physical access to your computer.)

Now (sudo'd) edit /etc/fstab and add

// /media/win cifs uid=you,credentials=/home/you/.credentials-cifs,iocharset=utf8,sec=ntlm 0 0

Barring typos, you should now be able to sudo mount -a and see your windows home directory under /media/win.

la dolce vita

Once the VM is launched, I find that everything else just works. I usually don't bother to "Connect" to the VM at all, but just keep a putty window open, with tmux running on the guest. Because of the internal switch, I can remain connected across suspensions and multiple wifi networks. The one main change to my development workflow is that I run emacs in text mode (under tmux), as I haven't found a glitch-free combination of emacs builds and windows X-servers, but I've found I actually prefer it this way.


Sun Oct 30 14:42:27 EDT 2016

It is suggested that one not select "secure boot" when setting up the VM. Apparently, some versions of linux will not boot. I obeyed and didn't investigate.

There is a trove of perplexing information on Ubuntu under Hyper-V in this technet article After reading it, I switched to the supposedly lighter weight virtual kernel:

  sudo apt-get install linux-virtual-lts-xenial
  sudo apt-get install linux-tools-virtual-lts-xenial linux-cloud-tools-virtual-lts-xenial

Despite this, uname still reports 4.4.0-45-generic, but nothing seems to be broken.

I've recently been setting X11 forwarding in putty and running the VcXrv X11 server, which seems to be XMing but compiled directly with VC++ and supposedly more performant. It seems to work.

After some research and fiddling, I discovered that exported X11 emacs will render properly if you


It still produces a lot of GLib and GTk warnings on the console.


comments powered by Disqus