diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d289fb9b1d042c42311d4b85c8baae3291e46fdb
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,41 @@
+sudo: required
+dist: trusty
+language: python
+python: "2.7"
+
+addons:
+  hosts:
+    - node1
+
+env:
+  - SITE=cluster.yml
+
+before_install:
+  - sudo apt-get update -qq
+
+install:
+  # Install Ansible.
+  - sudo -H pip install ansible
+  - sudo -H pip install netaddr
+
+cache:
+  directories:
+    - $HOME/releases
+    - $HOME/.cache/pip
+
+before_script:
+  - export PATH=$PATH:/usr/local/bin
+
+script:
+  # Check the role/playbook's syntax.
+  - "sudo -H ansible-playbook -i inventory/local-tests.cfg $SITE --syntax-check"
+
+  # Run the role/playbook with ansible-playbook.
+  - "sudo -H ansible-playbook -i inventory/local-tests.cfg $SITE --connection=local"
+
+  # Run the role/playbook again, checking to make sure it's idempotent.
+  - >
+    sudo -H ansible-playbook -i inventory/local-tests.cfg $SITE --connection=local
+    | tee /dev/stderr | grep -q 'changed=0.*failed=0'
+    && (echo 'Idempotence test: pass' && exit 0)
+    || (echo 'Idempotence test: fail' && exit 1)
diff --git a/inventory/local-tests.cfg b/inventory/local-tests.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..06fd3977b3f9d0e4c355c6ce0488a035d061882d
--- /dev/null
+++ b/inventory/local-tests.cfg
@@ -0,0 +1,17 @@
+node1 ansible_connection=local local_release_dir={{ansible_env.HOME}}/releases
+
+[downloader]
+node1
+
+[kube-master]
+node1
+
+[etcd]
+node1
+
+[kube-node]
+node1
+
+[k8s-cluster:children]
+kube-node
+kube-master
diff --git a/roles/dnsmasq/tasks/main.yml b/roles/dnsmasq/tasks/main.yml
index 2dda0f84028251d1ec9a37e189864170567924c2..73caa4f95149840675d11657f8c5e1881cd20bf5 100644
--- a/roles/dnsmasq/tasks/main.yml
+++ b/roles/dnsmasq/tasks/main.yml
@@ -53,11 +53,37 @@
     timeout: 100
   when: inventory_hostname in groups['kube-master']
 
-- name: update resolv.conf with new DNS setup
-  template:
-    src: resolv.conf.j2
-    dest: /etc/resolv.conf
-    mode: 644
+- name: check resolvconf
+  stat: path=/etc/resolvconf/resolv.conf.d/head
+  register: resolvconf
+
+- name: target resolv.conf file
+  set_fact:
+    resolvconffile: >
+      {%- if resolvconf.stat.exists == True -%}
+      /etc/resolvconf/resolv.conf.d/head
+      {%- else -%}
+      /etc/resolv.conf
+      {%- endif -%}
+
+- name: Add search resolv.conf
+  lineinfile:
+    line: search {{ [ 'default.svc.' + dns_domain, 'svc.' + dns_domain, dns_domain ] | join(' ') }}
+    dest: "{{resolvconffile}}"
+    state: present
+    insertafter: EOF
+    backup: yes
+    follow: yes
+
+- name: Add all masters as nameserver
+  lineinfile:
+    line: nameserver {{ hostvars[item]['ansible_default_ipv4']['address'] }}
+    dest: "{{resolvconffile}}"
+    state: present
+    insertafter: EOF
+    backup: yes
+    follow: yes
+  with_items: groups['kube-master']
 
 - name: disable resolv.conf modification by dhclient
   copy: src=dhclient_nodnsupdate dest=/etc/dhcp/dhclient-enter-hooks.d/nodnsupdate mode=u+x backup=yes
@@ -67,4 +93,9 @@
   copy: src=dhclient_nodnsupdate dest=/etc/dhcp/dhclient.d/nodnsupdate mode=u+x backup=yes
   when: ansible_os_family == "RedHat"
 
+- name: update resolvconf
+  command: resolvconf -u
+  changed_when: False
+  when: resolvconf.stat.exists == True
+
 - meta: flush_handlers
diff --git a/roles/dnsmasq/templates/resolv.conf.j2 b/roles/dnsmasq/templates/resolv.conf.j2
deleted file mode 100644
index f0b475b02a9b950e57817b3a78292635b5c8a29d..0000000000000000000000000000000000000000
--- a/roles/dnsmasq/templates/resolv.conf.j2
+++ /dev/null
@@ -1,9 +0,0 @@
-; generated by ansible
-search {{ [ 'default.svc.' + dns_domain, 'svc.' + dns_domain, dns_domain ] | join(' ') }}
-{% if inventory_hostname in groups['kube-master'] %}
-nameserver {{ ansible_default_ipv4.address }}
-{% else %}
-{% for host in groups['kube-master'] %}
-nameserver {{ hostvars[host]['ansible_default_ipv4']['address'] }}
-{% endfor %}
-{% endif %}
diff --git a/roles/kubernetes/master/tasks/main.yml b/roles/kubernetes/master/tasks/main.yml
index b1c94ac3da94b16d99d9f4a52d56df4d299a312c..d6e8b6cc4a38eac8efae4ba4d97cc2444ff90c6e 100644
--- a/roles/kubernetes/master/tasks/main.yml
+++ b/roles/kubernetes/master/tasks/main.yml
@@ -5,11 +5,17 @@
     dest: /etc/bash_completion.d/kubectl.sh
 
 - name: Install kubectl binary
-  copy:
-     src={{ local_release_dir }}/kubernetes/bin/kubectl
-     dest={{ bin_dir }}
-     owner=kube
-     mode=0755
+  synchronize:
+     src: "{{ local_release_dir }}/kubernetes/bin/kubectl"
+     dest: "{{ bin_dir }}/kubectl"
+     mode: pull
+     archive: no
+     checksum: yes
+     times: yes
+  delegate_to: "{{ groups['downloader'][0] }}"
+
+- name: Perms kubectl binary
+  file: path={{ bin_dir }}/kubelet owner=kube mode=0755 state=file
 
 - name: populate users for basic auth in API
   lineinfile:
diff --git a/roles/kubernetes/node/tasks/install.yml b/roles/kubernetes/node/tasks/install.yml
index 815a4bc46bf9999d9067d70e38f9adc93b1d8803..f98e672a494897d9ccad10beaf6bd5d054234c9f 100644
--- a/roles/kubernetes/node/tasks/install.yml
+++ b/roles/kubernetes/node/tasks/install.yml
@@ -1,36 +1,50 @@
 ---
-- name: Write kubelet systemd init file
+- debug: msg="{{init_system == "systemd"}}"
+- debug: msg="{{init_system}}"
+
+- name: install | Write kubelet systemd init file
   template: src=kubelet.service.j2 dest=/etc/systemd/system/kubelet.service backup=yes
   when: init_system == "systemd"
   notify: restart systemd-kubelet
 
-- name: Write kubelet initd script
+- name: install | Write kubelet initd script
   template: src=deb-kubelet.initd.j2 dest=/etc/init.d/kubelet owner=root mode=755 backup=yes
   when: init_system == "sysvinit" and ansible_os_family == "Debian"
   notify: restart kubelet
 
-- name: Write kubelet initd script
+- name: install | Write kubelet initd script
   template: src=rh-kubelet.initd.j2 dest=/etc/init.d/kubelet owner=root mode=755 backup=yes
   when: init_system == "sysvinit" and ansible_os_family == "RedHat"
   notify: restart kubelet
 
-- name: Install kubelet binary
-  copy:
-     src={{ local_release_dir }}/kubernetes/bin/kubelet
-     dest={{ bin_dir }}
-     owner=kube
-     mode=0755
+- name: install | Install kubelet binary
+  synchronize:
+     src: "{{ local_release_dir }}/kubernetes/bin/kubelet"
+     dest: "{{ bin_dir }}/kubelet"
+     mode: pull
+     times: yes
+     archive: no
+  delegate_to: "{{ groups['downloader'][0] }}"
   notify:
     - restart kubelet
 
-- name: Calico-plugin | Directory
+- name: install | Perms kubelet binary
+  file: path={{ bin_dir }}/kubelet owner=kube mode=0755 state=file
+
+- name: install | Calico-plugin | Directory
   file: path=/usr/libexec/kubernetes/kubelet-plugins/net/exec/calico/ state=directory
   when: kube_network_plugin == "calico"
 
-- name: Calico-plugin | Binary
-  copy:
-    src={{ local_release_dir }}/calico/bin/calico
-    dest=/usr/libexec/kubernetes/kubelet-plugins/net/exec/calico/calico
-    mode=0755
+- name: install | Calico-plugin | Binary
+  synchronize:
+    src: "{{ local_release_dir }}/calico/bin/calico"
+    dest: "/usr/libexec/kubernetes/kubelet-plugins/net/exec/calico/calico"
+    mode: "pull"
+    times: yes
+    archive: no
+  delegate_to: "{{ groups['downloader'][0] }}"
   when: kube_network_plugin == "calico"
   notify: restart kubelet
+
+- name: install | Perms calico plugin binary
+  file: path=/usr/libexec/kubernetes/kubelet-plugins/net/exec/calico/calico owner=kube mode=0755 state=file
diff --git a/roles/kubernetes/preinstall/tasks/main.yml b/roles/kubernetes/preinstall/tasks/main.yml
index f7d02ff6b118354fa7caaf9e09c0afd687dca304..d4ff74d523af6bbca062a5c04e0abf03f47062a0 100644
--- a/roles/kubernetes/preinstall/tasks/main.yml
+++ b/roles/kubernetes/preinstall/tasks/main.yml
@@ -1,7 +1,8 @@
 ---
 - name: "Identify init system"
   shell: >
-    if $(pgrep systemd > /dev/null); then
+    $(pgrep systemd > /dev/null && systemctl status > /dev/null);
+    if [ $? -eq 0 ] ; then
       echo systemd;
     else
       echo sysvinit;
diff --git a/roles/network_plugin/tasks/calico.yml b/roles/network_plugin/tasks/calico.yml
index 95803daf2e64664d02c04fb84e296abfc9ec2925..997b416d7f31499377301b6fadd85a8aee51e569 100644
--- a/roles/network_plugin/tasks/calico.yml
+++ b/roles/network_plugin/tasks/calico.yml
@@ -1,11 +1,18 @@
 ---
+
 - name: Calico | Install calicoctl bin
-  copy:
+  synchronize:
     src: "{{ local_release_dir }}/calico/bin/calicoctl"
-    dest: "{{ bin_dir }}"
-    mode: 0755
+    dest: "{{ bin_dir }}/calicoctl"
+    mode: pull
+    archive: no
+    times: yes
+  delegate_to: "{{ groups['downloader'][0] }}"
   notify: restart calico-node
 
+- name: Calico | install calicoctl
+  file: path={{ bin_dir }}/calicoctl mode=0755 state=file
+
 - name: Calico | Create calicoctl symlink (needed by kubelet)
   file:
     src: /usr/local/bin/calicoctl
@@ -39,7 +46,7 @@
   fail:
     msg: 'Only one network pool must be configured and it must be the subnet {{ kube_pods_subnet }}.
     Please erase calico configuration and run the playbook again ("etcdctl rm --recursive /calico/v1/ipam/v4/pool")'
-  when: ( calico_pools.json['node']['nodes'] | length > 1 ) or 
+  when: ( calico_pools.json['node']['nodes'] | length > 1 ) or
         ( not calico_pools.json['node']['nodes'][0]['key'] | search(".*{{ kube_pods_subnet | ipaddr('network') }}.*") )
   run_once: true
   delegate_to: "{{ groups['etcd'][0] }}"
diff --git a/roles/network_plugin/tasks/flannel.yml b/roles/network_plugin/tasks/flannel.yml
index a43be9e403212cc42e452dc87a7f3791eb2fdb21..69f63a749c5f22a33b7fa5d2be736c4858f94e55 100644
--- a/roles/network_plugin/tasks/flannel.yml
+++ b/roles/network_plugin/tasks/flannel.yml
@@ -3,14 +3,19 @@
   user: name=flannel shell=/bin/nologin
 
 - name: Install flannel binaries
-  copy: 
-     src={{ local_release_dir }}/flannel/bin/flanneld
-     dest={{ bin_dir }}
-     owner=flannel
-     mode=u+x
+  synchronize:
+     src: "{{ local_release_dir }}/flannel/bin/flanneld"
+     dest: "{{ bin_dir }}/flanneld"
+     mode: pull
+     archive: no
+     times: yes
+  delegate_to: "{{ groups['downloader'][0] }}"
   notify:
     - restart flannel
 
+- name: Perms flannel binary
+  file: path={{ bin_dir }}/flanneld owner=flannel mode=0755 state=file
+
 - name: Write flannel.service systemd file
   template:
     src: flannel/systemd-flannel.service.j2