Code Note

I use both PowerShell and Bash throughout this post. I tried to distinguish between the shells using symbols from their default values:

$ bash
> powershell

If you're thoroughly confused, you can always check the source. Each block is labelled.

I'd also like to apologize in advance for my PowerShell style. I don't have much experience with the shell, so my knowledge of conventions is pretty limited. I've tried to maintain consistent usage throughout, but it's all just my interpretation of what I was sourcing so it could be totally wrong.

Inside Windows

You can install Docker and the Docker Engine via your normal choice of package installation. If you're installing Docker on your production server to work with WSL, stop what you're doing, take the time you need to figure out PowerShell, and remove WSL from your production server. Since you're not, and you're setting it up for a dev environment, I recommend installing from prebuilt.

IMPORTANT

Search "Turn Windows features on or off" and open the Control Panel GUI result.

Make sure the following are enabled (requires reboot):

Hyper-V

Containers

A generic install from a stable package followed by toggling Switch to Windows Containers will achieve the same results. Because I like to tell myself I know what I'm doing, I started out with the more advanced installs and missed that.

dockerd From Docker

I did notice that the provided PATH addition, $env:ProgramFiles\Docker\Docker\Resources\bin doesn't include dockerd. The file, dockerd.exe is actually a directory above in Resources. To confirm,

I recommend going with the symlink because it's a bit more secure. Adding an unknown directory that you don't have control over to your path is, at best, a recipe for spending more time debugging than coding. Docker didn't expose the directory, which means Docker might update the directory with some executables that conflict with the normal path. If you symlink the executable, you're forced to review changes when updates break the symlink. I've checked as much of the source as I can; while Docker didn't expose dockerd, it is used heavily through the documentation, so you can safely export it knowing that your code will break if Docker changes it.

Aside: Things That Didn't Work Initially

I started with

> choco install docker-for-windows

but its installation matched almost zero documentation I found. It kept yelling at me when I tried to change hosts and wouldn't load an externally created config (both of which, I later discovered, were actual features of the edge version not covered the now outdated Microsoft docs).

This puts docker.exe and dockerd.exe into $env:ProgramFiles\Docker, creates $env:ProgramData\docker, and successfully registers a service via dockerd --register-service. However, I kept getting this error (and completely missed the whole ensure... note):

> dockerd --run-service
Error starting daemon: a required service is not installed, ensure the Containers feature is installed: failed to open service hns: The specified service does not exist as an installed service.

In hindsight, I think the key was actually installing Docker for Windows through an official source and following the tutorial to the letter first. After getting very frustrated with the choco and manual installations, I followed the MS install guide and was notified Containers would be turned on. I assumed that it had been enabled when docker-for-windows enabled Hyper-V, but I think I was wrong about that assumption. So, if you want to use either one of these options, you should probably start by enabling the features.

Settings

Once Docker is up and running, access its settings via the toolbar. You'll need to expose the daemon. You can use the defaults (pictured below) or use secure settings.
This poses a potential security risk, so be careful. WSL can be attacked. I'd highly recommend you change and secure the port instead (source):

Configure python

I used the vanilla python install with Ubuntu on Windows.

$ python --version
Python 2.7.12

If you've got other versions floating around, you probably already know how to handle dependencies. Docker recommends running a virtual environment (bonus: enforce virtualenv usage with pip) to install its scripts. I have no interest in messing with that many environments, so everything after this assumes a global install via sudo -H.

Basically, like many current WSL features, Docker support is just a really cool idea that doesn't work. Years of embrace and extend have completely butchered generally accepted higher APIs (I dare you to find in PowerShell), so outside devs porting the containerization at a lower level can't have an easy job. Microsoft is actively embracing containerization, and they're working hard to extend it via Windows Containers using their own proprietary stack. Either you see where this is going or you like Microsoft.

Granted, it's getting a ton of attention and dev focus, so it will probably be implemented sooner than, say, cron support.

Mostly

Right now, running Linux containers on Docker for Windows does work. However, the Windows configuration file is severely limited. Or, rather, Docker for Windows cannot use hosts, which I find severely limiting. Barring some Windows voodoo that I don't know about, your only option for WSL access is to expose a widely-documented default port without TLS authentication. This really concerns me for a few reasons:

Any attacker that knows how to pivot is also, in my opinion, intelligent enough to probe widely published and unchangeable defaults

In my dev world, I often reformat and try a new environment. Rebuilding wouldn't be a big deal. However, I also keep basically all config ever in dev, because, unsurprisingly, that's where I develop it. It's already hard enough to stay safe in a Windows world without worrying about exposing myself in brand new ways (in all fairness, Windows Defender does a good job and I don't often leave trusted domains like Steam, GitHub, StackExchange, etc.)

In any production world, that's so dumb it doesn't even merit a detailed rebuttal. If you're running Docker for Windows in production, use PowerShell.

If you don't think any of these issues are a big deal (e.g. dev exists on an air-gapped network that also contains carefully vetted private registries updated via thumbdrive), expose the port. Honestly, the chances of you being attacked are at least as low as a credit reporting agency's database being compromised.

Bespoke Solution

First, I want to point out how awesome the LinuxKit devs are. They're suffering through the pain of figuring out how to do this in Windows so we don't have to. Second, this is only bespoke right now, a few weeks after all the announcements. In two or three years, it might be default.

Bespoke Requirements

Windows Insider: whatever the settings you choose, you'll need at least Windows 10 Insider Preview RC16281. I set content to "Active..." and was able to get RC16299 with pace "Slow". I was also able to manually check for updates instead of waiting for a build. Downloading and installing it was slow, though.

A Word of Caution

Enabling Windows Insider changes a few things about your installation of Windows.

You will send data to Microsoft. There's no option to opt out. If you're concerned about privacy (protip: you should be), you should take the time to read the entire Program Agreement.

You will be forced to interact with ads while doing normal things like writing code. Again, there's no option to opt out. Because Windows Insider provides a fresh evaluation license, you don't even get to insist that you shouldn't be served ads because you paid for the copy of Windows you installed Windows Insider on. (Note I'm loosely grouping "free focus group" questions i.e. Feedback, one of which just popped over my editor, in with the normal Windows 10 ads for bloatware like Skype.)

Inside Windows

Installation

I took the code from the announcement and tried to expand on it just a little bit.

If you're using an edge version (e.g. from master) of Docker for Windows, I'd hazard a guess that you don't need to run separate dockerds and can see the features by setting $env:LCOW_SUPPORTED. I prefer discrete copies with distinct names for readability and maintenance reasons; your mileage may vary.

Bootstrapping

There's plenty that could be added to that script, but I didn't want to spend a Sunday off beating my head against my keyboard over Windows configuration (TypeScript issues, sure, but not Windows config). I started reading the docs on building a service out of commands and my eyes glazed over. Something like this in whichever of the million PowerShell profiles there are could do the trick instead:

I changed the host from a named pipe to TCP; I couldn't get WSL to recognize it. I've skimmed this issue before. I wouldn't be surprised if I had to read this entire wall of docs just to learn somewhere at the end that I can install a third-party application that will automatically configure them properly for me.

$env:LCOW_SUPPORTED is vital. Without it, dockerd-preview will function exactly like dockerd, i.e. only works as a Linux host with scary defaults. Ideally, you should only have it set before and during dockerd-preview execution, and unset it afterward. I don't think the two systems (dockerd and dockerd-preview) conflict with each other, but, if this ordeal has taught me anything, it's better to not make assumptions about Windows applications.

Another option I spent some time investigating is creating a task triggered by an event related to the Docker service. I was able to find a launch event when I bounced the service, but I haven't been able to find anything else. I'm personally wary of just running off the odd launch event, which was fired in my userspace while logged in, so I didn't pursue that.

A dependent service seems to be the best solution. I work with some rad dudes that grok Windows voodoo. If they have any suggestions this week, I'll add some updates (which means if you're still seeing this explanation I don't have updates).

Inside WSL

Everything from the native solution thankfully still applies. If you set up a decent PowerShell script and read the host from another file (preferably one that won't get locked by Windows permissions when dockerd-preview starts), you can even read it directly in your .whateverrc instead of maintaining the same constant in two locations.

Final Notes

I wouldn't use either of these solutions in a production environment. If Docker for Windows can safely run not-Windows images in PowerShell, that's a great solution for a ton of business applications, like easily running dev/test builds on a mandated-Windows office network. Natively connecting Docker to WSL is still (probably) a couple of years away. Plus there's the whole WSL malware exploit to address first. Adding a bespoke layer, no matter how awesome the devs are, makes the stack much too untenable. A better title for this post would have been "Why Docker for Windows is Scary Outside of PowerShell."

I'm currently running the bespoke solution on my personal dev box. I don't touch this machine very much outside of the weekends, so it might be some time before I really run it through the wringer. I was originally curious about adding some Dockerfiles to a repo for easy testing/demos. 12+ hours later, I've decided that I really need to spend the hour or so it will take me to finally install Linux on another drive for dev.

If you took something away from this, please take the time to pay it forward. The next time you discover an solution after reading a few threads that didn't have a good/non-deprecated solution, go back and share it in one or two them.