Node on Ubuntu on Vagrant on Hyper-V

The following is how I got NodeJS running on Ubuntu using Vagrant in Windows with Hyper-V.

Why Hyper-V?

Paying for VMWare goes against my open-source leanings, and I'm a lone dev so any expenses have to be seriously considered.

Virtualbox is a great product and I've used it a lot in the past but I never liked the networking stuff it added to my system. I've tried using Vagrant with Virtualbox on Windows before and always come against folder and file permissions and syncing problems. I have problems with Oracle too.

Hyper-V on the other hand is baked natively in to Windows 8.1 and higher (I'm using Windows 10). It's straightforward to enable, configure and plays nicely with the Windows filesystem and networking stack.

Why Ubuntu?

I've used it more than any other Linux distribution. I'm very familiar with configuring it and running it in production. I dare say the steps I outline below are fairly 'box' agnostic though.

Step 1. Install Vagrant

Head on over to to download the installer, run it and wait for it to finish.

Step 2. Enable Hyper-V

Go to Uninstall or change a program, you can find this in the toolbar on This PC, via the control panel, or just search for it.

Next click on Turn Windows features on or off on the left side of the screen. And make sure Hyper-V is checked.

Enabled Hyper-V

You might have to reboot to fully enable Hyper-V but once it's enabled you can check it under the performance tab in the task scheduler

Check Hyper-V is enabled

Step 3. Create Hyper-V network switch

This step is really important. If you do not do this, Vagrant will not be able to connect to the box. So hit your Start button and search for Hyper-V Manager. Once in, find Virtual Switch Manager...

Hyper-V Virtual Switch Manager link

In the 'Virtual Switch Manager' select New virtual network switch, now you have three choices:

New virtual network switch

External creates a network switch that is connected to your system's physical network. This will allow your Vagrant box to connect to the outside world, and vice versa.

Internal creates a network switch that allows your host system and the virtual machines in Hyper-V to talk to each other. If you select this option, your Vagrant will not have internet access.

Private creates a network switch that can only be used by the virtual machines. This is useless for Vagrant.

I suggest using External as it means I can use apt-get etc. So select External and hit Create Virtual Switch. All you need do now is give your virtual switch a name. Hit 'OK' and close the Hyper-V Manager.

Step 4. The Vagrantfile

Now we have the host operating system set up and Vagrant installed it's time to actually create a Vagrant box.

In the directory your project with be in type the following command:

vagrant init  

This will create a single file called Vagrantfile in your directory. This file is all you need and is where you'll put your instructions for setting up your Vagrant box.

Ignoring all the comments and remmed out statements, the basic Vagrantfile looks like this:

Vagrant.configure(2) do |config| = "base"

That's it. The base box is the default and is all well and good, but I want 64bit Ubuntu. So change "base" to "hashicorp/precise64". If you want a different base system, you can find more pre-built boxes at

Next we have to tell Vagrant to use Hyper-V as I think it defaults to 'Virtualbox', so add the following line:

Vagrant.configure(2) do |config| = "hashicorp/precise64"
  config.vm.provider "hyperv"

Finally, we want to make sure the Vagrant box has access to the public network (the internet) so we can grab apt packages and the like. So add the following line:

Vagrant.configure(2) do |config| = "hashicorp/precise64"
  config.vm.provider "hyperv" "public_network"

This is the absolute basics we need. Save your Vagrantfile and we're ready to fire it up.

Step 5. vagrant up

One quirk of Vagrant on Hyper-V is that it must be run as Administrator. So whether you're using Command Prompt, Powershell, Cygwin, or Git Bash, you need to make sure you run it as Administrator.

So to get things going open your CLI window, navigate to your project folder and type:

vagrant up  

If you see an error regarding the provider, you may need to force the use of Hyper-V:

vagrant up --provider=hyperv  

If all goes well, you should see something along the lines of the following. It might take a while as it has to download the virtual hard drive for the Ubuntu version we selected. Also, because we're using Linux with Windows, Vagrant needs to set up a Samba share. So you'll need to enter your Windows credentials.

$ vagrant up
Bringing machine 'default' up with 'hyperv' provider...  
==> default: Verifying Hyper-V is enabled...
==> default: Importing a Hyper-V instance
    default: Cloning virtual hard drive...
    default: Creating and registering the VM...
    default: Successfully imported a VM with name: precise64
==> default: Starting the machine...
==> default: Waiting for the machine to report its IP address...
    default: Timeout: 120 seconds
    default: IP:
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address:
    default: SSH username: vagrant
    default: SSH auth method: password
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Preparing SMB shared folders...
    default: You will be asked for the username and password to use for the SMB
    default: folders shortly. Please use the proper username/password of your
    default: Windows account.
    default: Username: xxxxx
    default: Password (will be hidden):
    default: Password (will be hidden): xxxxxxxx
==> default: Mounting SMB shared folders...
    default: C:/Users/lewis/Desktop/vagrant test => /vagrant

If you see something like above then everything ran fine. You can SSH in to your box by typing vagrant ssh

Some useful Vagrant commands are:

vagrant upCreate a box or start from halt
vagrant haltGraceful shutdown
vagrant destroyRemove the box
vagrant suspendPause box at exact state
vagrant resumeResume from suspend
vagrant reloadReboot, maybe after a config change
vagrant provisionRe-run the provisioning stuff
vagrant sshSSH in to your box

More can be found on the Vagrant website

Step 6. Provisioning and NodeJS

Every time you create a box from a Vagrantfile or vagrant up after a vagrant destroy, Vagrant will create your box from scratch. While we could install all the software we need each time, it makes sense to tell Vagrant to do it for us. This is known as provisioning.

To get started with provisioning NodeJS, create a new file called This is a bash script where we'll put in the commands we need to run. I'm going to install NVM, the node version manager. This is because NodeJS release new versions very fast so I'm happy to actually select my NodeJS version manually.

#!/usr/bin/env bash

wget -qO- | bash  

Now in your Vagrantfile add the following line. The important part here is at the end, priveleged: false. By default provisioning scripts run as sudo but we want NVM installed as the vagrant user.

config.vm.provision :shell, path: "", privileged: false  

So now your whole Vagrantfile should look like this:

Vagrant.configure(2) do |config| = "hashicorp/precise64"
  config.vm.provider "hyperv" "public_network"
  config.vm.provision :shell, path: "", privileged: false

In your you would also put anything else you want run automatically. Such as setting environment variables, pulling from a remote repository, or even installing a database system like Redis.

All done

This is just basic steps to get NodeJS running in Ubuntu with Vagrant using Hyper-V on Windows. There is a lot more to Vagrant than I can cover here, so I suggest you start with their Getting Started guide.

Key only SSH

To cut back on the hacking attempts and make things just that little bit more secure, it's a good idea to disable the use of passwords to login via SSH.

Of course you'll need a way to access it so make sure you're public key is in your ~/.ssh/authorized_keys file.

To disable the use of passwords with SSH edit the sshd_config config file using something like nano. You'll need to run this as sudo.

sudo nano /etc/ssh/ssh_config  

Find the following lines and change them, or add them if they're missing:

RSAAuthentication yes  
PubkeyAuthentication yes  
ChallengeResponseAuthentication no  
PasswordAuthentication no  
UsePAM no  

One caveat here. I found on Ubuntu 12.04 that when I turn off UsePAM the banner I usually see when connecting with SSH is not shown.

To fix this I uncommented and ammended the line which reads #Banner /etc/

Banner /etc/motd  

Of course you'll need to restart sshd, depending on what service management system you use, enter the following:

sudo service ssh restart  
sudo /etc/init.d/sshd restart  

Important Don't lose your private keys which match the public keys you've used, or you'll never get back in!

Diffie-Hellman for TLS

After successfully enabling Let's Encrypt for my domain, I ran the test over at SSL Labs and was disappointed to see I only scored a C grade.

It seems that because my Nginx configuration supported SSLv3 it was susceptible to the POODLE attack. And thus regardless of anything else the grade was capped at C. This was easily fixed however by adding the following line to nginx.conf:

http {  
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

I ran the test again but this time only scored a B grade. Here's why:

Warning! This site uses a commonly-shared 1024-bit Diffie-Hellman group, and might be in range of being broken by a nation-state. It might be a good idea to generate a unique, 2048-bit group for the site.

Luckily, the kind folks over at provide a Guide to Deploying Diffie-Hellman for TLS. To summarise, do the following:

First thing to do is generate a dhparams.pem. I put it in my /etc/nginx/sites-available directory, but it doesn't really matter where you put it as long as it's a fairly safe location:

openssl dhparam -out /etc/nginx/sites-available/dhparams.pem 2048  

Now in the server block for your domain (by default this is in /etc/nginx/sites-available/default) add the following lines:

server {  
    ssl_prefer_server_ciphers on;
    ssl_dhparam /etc/nginx/sites-available/dhparams.pem;

Now reload Nginx:

sudo nginx -s reload  

These simple steps have garnered me an A grade.

A grade on SSL Labs

Implementing Let's Encrypt

Updated Now that Let's Encrypt is in public beta I've updated this to suit.

These are exciting times for the web. Secure connections have been around for a long time and are now expected and trusted by the public. Unfortunately they're also very expensive, usually around $99 per year.

But huzzah and hooray, we now have Let's Encrypt, a service from the EFF, Mozilla, Cisco and others to provide FREE TLS encryption for any website. You can read more about the mission here:

At the moment the service is in public beta which means they're still ironing out the bugs, but it's currently serving tens of thousands of certs and seems to be holding up fine.

For the purposes of this little walk-through I'm using Ubuntu 14.10 and Nginx.

One of the goals of Let's Encrypt is that it be automatic. In time this will include configuring the server too (be that Apache, Nginx, or another) and it already does a pretty good job but there's definitely more work to do.

I'm going to show how get just the cert files and configure Nginx manually. So to start with clone the Let's Encrypt software somewhere, I put it in my home directory:

git clone  

Ideally before the next step make sure you stop any service using port 80. There is a 'webroot' option to circumvent this and you can read more on the How it works page. But in my case I'm happy killing Nginx for a couple of minutes:

sudo service nginx stop  

Now cd in to the new directory and run the software with the following parameters:

cd letsencrypt  
./letsencrypt-auto certonly

The first time you run the program it will ask for an email address and ask that you agree with the T's and C's.

Lets Encrypt email address screen

Lets Encrypt terms and conditions screen

Finally it will ask for your domain(s). Separate these either with a space or a comma.

That's it! If all goes well the necessary files will be created here: /etc/letsencrypt/live/<yourdomain>/

To add to Nginx, add or change your sites-available file to look like this:

server {  
    listen 443 ssl;
    ssl_certificate     /etc/letsencrypt/live/;
    ssl_certificate_key /etc/letsencrypt/live/;

Next I created a server block to redirect to and one to redirect the insecure versions of those domains:

server {  
    listen       443 ssl;
    return       301$request_uri;
server {  
    listen       80;
    return       301$request_uri;

Restart Nginx and all should be well.

sudo service nginx restart  

Since Let's Encrypt certificates expire every ninety days you'll need to manually renew. Eventually this can be automated, but for now just run the following command again when your certs expire:

./letsencrypt-auto certonly

While you're at it, you may as well beef up the Diffie-Hellman cyphers to get that A-grade SSL.

SSH config

I first encountered the SSH config when I had to set up SSH for two BitBucket accounts. One work, and one personal. Requiring separate login credentials, setting the remote on Git repositories to ssh:// wasn't going to work.

Each BitBucket account requires a different public SSH key so for two accounts I needed to generate two sets of keys. You'll likely already have a keyset called id_rsa so call the new one something else:

$ ssh-keygen 
Generating public/private rsa key pair.  
Enter file in which to save the key (/Users/you/.ssh/id_rsa): bb-personal  

Now in your ~/.ssh/ directory create a new file simply called config, for my example it looks like this:

Host bitbucket-work  
  User git
  PreferredAuthentications publickey
  IdentityFile ~/.ssh/bb_work

Host bitbucket-personal  
  User git
  PreferredAuthentications publickey
  IdentityFile ~/.ssh/bb_personal

Now instead of in my git remote URIs I substitute the relevant Host from my config:


Of course, you need to ensure the public key is configured over on BitBucket, GitHub etc.

To use config with your own remote machines is just as easy. You can use any of the keys you already have on your system, or create a new one as shown above.

If it doesn't exist, create a file in the ~/.ssh/ directory on your remote machine called authorized_keys and copy the public key in to it, each one on a new line. Never the private key!

Now in your config file on your local machine create a new host for your remote machine:

Host me-production  
  User lewis
  Hostname <your host or ip>
  PreferredAuthentications publickey
  IdentityFile ~/.ssh/id_rsa

Strictly speaking, I don't think the User line is necessary.

Now all I need to type in the terminal to ssh in to my production box is:

$ ssh me-production