diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..2ebb1e9b34247c445520a8e53260f4e04601a92d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.vagrant
+*.retry
+inventory/vagrant_ansible_inventory
diff --git a/README.md b/README.md
index 6709c67b3220b0e608aa537708fb29ce4b60d954..8e4f8ae39b656a9ef6ae4bbf5af7bccdb720788d 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,8 @@
 - Support most popular **Linux distributions**
 - **Continuous integration tests**
 
+To create a cluster in vagrant simply run `vagrant up`
+
 For an easy way to use it, check out [**kargo-cli**](https://github.com/kubespray/kargo-cli) </br>
 A complete **documentation** can be found [**here**](https://docs.kubespray.io)
 
diff --git a/Vagrantfile b/Vagrantfile
new file mode 100644
index 0000000000000000000000000000000000000000..c5cc7f5fda6c5d7e4ecdad5d3655cc19dbbdde8a
--- /dev/null
+++ b/Vagrantfile
@@ -0,0 +1,124 @@
+# -*- mode: ruby -*-
+# # vi: set ft=ruby :
+
+require 'fileutils'
+
+Vagrant.require_version ">= 1.8.0"
+
+CONFIG = File.join(File.dirname(__FILE__), "vagrant/config.rb")
+
+# Defaults for config options defined in CONFIG
+$num_instances = 3
+$instance_name_prefix = "k8s"
+$vm_gui = false
+$vm_memory = 1024
+$vm_cpus = 1
+$shared_folders = {}
+$forwarded_ports = {}
+$subnet = "172.17.8"
+
+host_vars = {}
+
+if File.exist?(CONFIG)
+  require CONFIG
+end
+
+# if $inventory is not set, try to use example
+$inventory = File.join(File.dirname(__FILE__), "inventory") if ! $inventory
+
+# if $inventory has a hosts file use it, otherwise copy over vars etc
+# to where vagrant expects dynamic inventory to be.
+if ! File.exist?(File.join(File.dirname($inventory), "hosts"))
+  $vagrant_ansible = File.join(File.dirname(__FILE__), ".vagrant",
+                       "provisioners", "ansible")
+  FileUtils.mkdir_p($vagrant_ansible) if ! File.exist?($vagrant_ansible)
+  if ! File.exist?(File.join($vagrant_ansible,"inventory"))
+    FileUtils.ln_s($inventory, $vagrant_ansible)
+  end
+end
+
+Vagrant.configure("2") do |config|
+  # always use Vagrants insecure key
+  config.ssh.insert_key = false
+
+  config.vm.box = "ubuntu-14.04"
+  config.vm.box_url = "https://storage.googleapis.com/%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant.json" % [$update_channel, $image_version]
+
+  ["vmware_fusion", "vmware_workstation"].each do |vmware|
+    config.vm.provider vmware do |v, override|
+      override.vm.box_url = "https://storage.googleapis.com/%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant_vmware_fusion.json" % [$update_channel, $image_version]
+    end
+  end
+
+  config.vm.provider :virtualbox do |v|
+    # On VirtualBox, we don't have guest additions or a functional vboxsf
+    # in CoreOS, so tell Vagrant that so it can be smarter.
+    v.check_guest_additions = false
+    v.functional_vboxsf     = false
+  end
+
+  # plugin conflict
+  if Vagrant.has_plugin?("vagrant-vbguest") then
+    config.vbguest.auto_update = false
+  end
+
+  (1..$num_instances).each do |i|
+    config.vm.define vm_name = "%s-%02d" % [$instance_name_prefix, i] do |config|
+      config.vm.hostname = vm_name
+
+      if $expose_docker_tcp
+        config.vm.network "forwarded_port", guest: 2375, host: ($expose_docker_tcp + i - 1), auto_correct: true
+      end
+
+      $forwarded_ports.each do |guest, host|
+        config.vm.network "forwarded_port", guest: guest, host: host, auto_correct: true
+      end
+
+      ["vmware_fusion", "vmware_workstation"].each do |vmware|
+        config.vm.provider vmware do |v|
+          v.vmx['memsize'] = $vm_memory
+          v.vmx['numvcpus'] = $vm_cpus
+        end
+      end
+
+      config.vm.provider :virtualbox do |vb|
+        vb.gui = $vm_gui
+        vb.memory = $vm_memory
+        vb.cpus = $vm_cpus
+      end
+
+      ip = "#{$subnet}.#{i+100}"
+      host_vars[vm_name] = {
+        "ip" => ip,
+        "access_ip" => ip
+      }
+      config.vm.network :private_network, ip: ip
+
+      # Only execute once the Ansible provisioner,
+      # when all the machines are up and ready.
+      if i == $num_instances
+        config.vm.provision "ansible" do |ansible|
+          ansible.playbook = "cluster.yml"
+          if File.exist?(File.join(File.dirname($inventory), "hosts"))
+            ansible.inventory_path = $inventory
+          end
+          ansible.sudo = true
+          ansible.limit = "all"
+          ansible.host_key_checking = false
+          ansible.raw_arguments = ["--forks=#{$num_instances}"]
+          ansible.host_vars = host_vars
+          ansible.groups = {
+            # The first three nodes should be etcd servers
+            "etcd" => ["k8s-0[1:3]"],
+            # The first two nodes should be masters
+            "kube-master" => ["k8s-0[1:2]"],
+            # all nodes should be kube nodes
+            "kube-node" => ["k8s-0[1:#{$num_instances}]"],
+            "k8s-cluster:children" => ["kube-master", "kube-node"],
+          }
+        end
+      end
+
+    end
+  end
+end
diff --git a/inventory/inventory.example b/inventory/inventory.example
index 90bc3d5ac394a436a5e3f7b5b9aac650981c28f3..7c1c9a5f374a8e2be90e0da46166b7a84615aafe 100644
--- a/inventory/inventory.example
+++ b/inventory/inventory.example
@@ -1,29 +1,29 @@
-[kube-master]
-node1 ansible_ssh_host=10.99.0.26
-node2 ansible_ssh_host=10.99.0.27
-
-[etcd]
-node1 ansible_ssh_host=10.99.0.26
-node2 ansible_ssh_host=10.99.0.27
-node3 ansible_ssh_host=10.99.0.4
-
-[kube-node]
-node2 ansible_ssh_host=10.99.0.27
-node3 ansible_ssh_host=10.99.0.4
-node4 ansible_ssh_host=10.99.0.5
-node5 ansible_ssh_host=10.99.0.36
-node6 ansible_ssh_host=10.99.0.37
-
-[paris]
-node1 ansible_ssh_host=10.99.0.26
-node3 ansible_ssh_host=10.99.0.4 local_as=xxxxxxxx
-node4 ansible_ssh_host=10.99.0.5 local_as=xxxxxxxx
-
-[new-york]
-node2 ansible_ssh_host=10.99.0.27
-node5 ansible_ssh_host=10.99.0.36 local_as=xxxxxxxx
-node6 ansible_ssh_host=10.99.0.37 local_as=xxxxxxxx
-
-[k8s-cluster:children]
-kube-node
-kube-master
+#[kube-master]
+#node1 ansible_ssh_host=10.99.0.26
+#node2 ansible_ssh_host=10.99.0.27
+#
+#[etcd]
+#node1 ansible_ssh_host=10.99.0.26
+#node2 ansible_ssh_host=10.99.0.27
+#node3 ansible_ssh_host=10.99.0.4
+#
+#[kube-node]
+#node2 ansible_ssh_host=10.99.0.27
+#node3 ansible_ssh_host=10.99.0.4
+#node4 ansible_ssh_host=10.99.0.5
+#node5 ansible_ssh_host=10.99.0.36
+#node6 ansible_ssh_host=10.99.0.37
+#
+#[paris]
+#node1 ansible_ssh_host=10.99.0.26
+#node3 ansible_ssh_host=10.99.0.4 local_as=xxxxxxxx
+#node4 ansible_ssh_host=10.99.0.5 local_as=xxxxxxxx
+#
+#[new-york]
+#node2 ansible_ssh_host=10.99.0.27
+#node5 ansible_ssh_host=10.99.0.36 local_as=xxxxxxxx
+#node6 ansible_ssh_host=10.99.0.37 local_as=xxxxxxxx
+#
+#[k8s-cluster:children]
+#kube-node
+#kube-master
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..74b24ce8c8cf481509025cbd42462fcea838deed
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+ansible
+netaddr
diff --git a/roles/kubernetes/master/tasks/main.yml b/roles/kubernetes/master/tasks/main.yml
index 109ada3ea5dcecbd7da3d1351ca753d47e9fa25b..deaf017f3c0250a41a49926ddeb94d113881c667 100644
--- a/roles/kubernetes/master/tasks/main.yml
+++ b/roles/kubernetes/master/tasks/main.yml
@@ -58,7 +58,7 @@
   command: "{{ bin_dir }}/kubectl get ns kube-system"
   register: 'kubesystem'
   changed_when: False
-  ignore_errors: yes
+  failed_when: False
   run_once: yes
 
 - name: wait for the apiserver to be running
diff --git a/roles/kubernetes/secrets/files/.gitignore b/roles/kubernetes/secrets/files/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..eb876248b961c6eb4c00f701a56c1fc6e16231ba
--- /dev/null
+++ b/roles/kubernetes/secrets/files/.gitignore
@@ -0,0 +1,4 @@
+tokens/*.token
+tokens/known_tokens.csv
+certs/*.pem
+openssl.conf