diff --git a/docs/ansible.md b/docs/ansible.md
index 0786a7764663e9a5adec84ba367e189f5a17506c..101c0a0754e1400ec6153ff978170df95b761480 100644
--- a/docs/ansible.md
+++ b/docs/ansible.md
@@ -4,14 +4,14 @@ Ansible variables
 
 Inventory
 -------------
-The inventory is composed of 3 groups: 
+The inventory is composed of 3 groups:
 
 * **kube-node** : list of kubernetes nodes where the pods will run.
-* **kube-master** : list of servers where kubernetes master components (apiserver, scheduler, controller) will run. 
+* **kube-master** : list of servers where kubernetes master components (apiserver, scheduler, controller) will run.
   Note: if you want the server to act both as master and node the server must be defined on both groups _kube-master_ and _kube-node_
 * **etcd**: list of server to compose the etcd server. you should have at least 3 servers for failover purposes.
 
-Below is a complete inventory example: 
+Below is a complete inventory example:
 
 ```
 ## Configure 'ip' variable to bind kubernetes services on a
diff --git a/inventory/group_vars/all.yml b/inventory/group_vars/all.yml
index e82c8314733431066610706aa607f9f69471e372..e9b138c923d7abd5e8091ce927cdeaa755d2cf33 100644
--- a/inventory/group_vars/all.yml
+++ b/inventory/group_vars/all.yml
@@ -51,6 +51,23 @@ cluster_name: cluster.local
 # but don't know about that address themselves.
 # access_ip: 1.1.1.1
 
+# Service endpoints. May be a VIP or a load balanced frontend IP, like one
+# that a HAProxy or Nginx provides, or just a local service endpoint.
+#
+# Etcd endpoints use a local etcd-proxies to reach the etcd cluster via
+# auto-evaluated endpoints. Those will reuse the access_ip for etcd cluster,
+# if specified, or defer to the localhost:2379 as well.
+
+# Etcd access modes:
+# Enable multiaccess to configure clients to access all of the etcd members directly
+# as the "http://hostX:port, http://hostY:port, ..." and ignore the proxy loadbalancers.
+# This may be the case if clients support and loadbalance multiple etcd servers  natively.
+etcd_multiaccess: false
+
+#
+# TODO apiserver localhost:8080 and localhost:443 endpoints for kubelets and
+# (hyper)kube-* and networking components.
+
 # Choose network plugin (calico, weave or flannel)
 kube_network_plugin: flannel
 
diff --git a/roles/etcd/handlers/main.yml b/roles/etcd/handlers/main.yml
index 334a4c396d84d164407485e10a80010661e058be..cf0aaa5bf25aa0d878d655b21ff3bc73b7f8355b 100644
--- a/roles/etcd/handlers/main.yml
+++ b/roles/etcd/handlers/main.yml
@@ -6,6 +6,13 @@
     - start etcd
     - reload etcd
 
+- name: restart etcd-proxy
+  command: /bin/true
+  notify:
+    - reload systemd
+    - start etcd-proxy
+    - reload etcd-proxy
+
 - name: reload systemd
   command: systemctl daemon-reload
   when: ansible_service_mgr == "systemd"
@@ -15,8 +22,23 @@
     name: etcd
     enabled: yes
     state: started
+  when: is_etcd_master
+
+- name: start etcd-proxy
+  service:
+    name: etcd-proxy
+    enabled: yes
+    state: started
+  when: is_etcd_proxy
 
 - name: reload etcd
   service:
     name: etcd
     state: "{{ 'restarted' if etcd_deployment_type == 'host' else 'reloaded' }}"
+  when: is_etcd_master
+
+- name: reload etcd-proxy
+  service:
+    name: etcd-proxy
+    state: "{{ 'restarted' if etcd_deployment_type == 'host' else 'reloaded' }}"
+  when: is_etcd_proxy
diff --git a/roles/etcd/tasks/configure.yml b/roles/etcd/tasks/configure.yml
index 818559239e84c85fdd4bd034e37c41510cfeb070..b665acceb2b61d7d26a435ce156ca21d399b2a57 100644
--- a/roles/etcd/tasks/configure.yml
+++ b/roles/etcd/tasks/configure.yml
@@ -15,7 +15,7 @@
     src: "etcd-{{ etcd_deployment_type }}.service.j2"
     dest: /etc/systemd/system/etcd.service
     backup: yes
-  when: ansible_service_mgr == "systemd"
+  when: ansible_service_mgr == "systemd" and is_etcd_master
   notify: restart etcd
 
 - name: Configure | Write etcd initd script
@@ -24,5 +24,21 @@
     dest: /etc/init.d/etcd
     owner: root
     mode: 0755
-  when: ansible_service_mgr in ["sysvinit","upstart"] and ansible_os_family == "Debian"
+  when: ansible_service_mgr in ["sysvinit","upstart"] and ansible_os_family == "Debian" and is_etcd_master
   notify: restart etcd
+
+- name: Configure | Copy etcd-proxy.service systemd file
+  template:
+    src: "etcd-proxy-{{ etcd_deployment_type }}.service.j2"
+    dest: /etc/systemd/system/etcd-proxy.service
+    backup: yes
+  when: ansible_service_mgr == "systemd" and is_etcd_proxy
+  notify: restart etcd-proxy
+- name: Configure | Write etcd-proxy initd script
+  template:
+    src: "deb-etcd-proxy-{{ etcd_deployment_type }}.initd.j2"
+    dest: /etc/init.d/etcd-proxy
+    owner: root
+    mode: 0755
+  when: ansible_service_mgr in ["sysvinit","upstart"] and ansible_os_family == "Debian" and is_etcd_proxy
+  notify: restart etcd-proxy
diff --git a/roles/etcd/tasks/main.yml b/roles/etcd/tasks/main.yml
index 060d3708b100b9c4ff986a33bb8db83c6483b0cf..34d94ab738df0706744d739cde43ce14e7329d0e 100644
--- a/roles/etcd/tasks/main.yml
+++ b/roles/etcd/tasks/main.yml
@@ -8,7 +8,12 @@
 - name: Restart etcd if binary changed
   command: /bin/true
   notify: restart etcd
-  when: etcd_deployment_type == "host" and etcd_copy.stdout_lines
+  when: etcd_deployment_type == "host" and etcd_copy.stdout_lines and is_etcd_master
+
+- name: Restart etcd-proxy if binary changed
+  command: /bin/true
+  notify: restart etcd-proxy
+  when: etcd_deployment_type == "host" and etcd_copy.stdout_lines and is_etcd_proxy
 
 # Reload systemd before starting service
 - meta: flush_handlers
@@ -18,6 +23,14 @@
     name: etcd
     state: started
     enabled: yes
+  when: is_etcd_master
+
+- name: Ensure etcd-proxy is running
+  service:
+    name: etcd-proxy
+    state: started
+    enabled: yes
+  when: is_etcd_proxy
 
 # After etcd cluster is assembled, make sure that
 # initial state of the cluster is in `existing`
diff --git a/roles/etcd/tasks/refresh_config.yml b/roles/etcd/tasks/refresh_config.yml
index 701a1d149e574e2f50ee5ad2e07d77134f46cf2d..178466153d2600fae342e430416eaca0e7cbf291 100644
--- a/roles/etcd/tasks/refresh_config.yml
+++ b/roles/etcd/tasks/refresh_config.yml
@@ -4,3 +4,11 @@
     src: etcd.j2
     dest: /etc/etcd.env
   notify: restart etcd
+  when: is_etcd_master
+
+- name: Refresh config | Create etcd-proxy config file
+  template:
+    src: etcd-proxy.j2
+    dest: /etc/etcd-proxy.env
+  notify: restart etcd-proxy
+  when: is_etcd_proxy
diff --git a/roles/etcd/tasks/set_facts.yml b/roles/etcd/tasks/set_facts.yml
index 415ed85c633040b9d46a537c51228f92fcfda7a4..5dd283fef5ba8162bbfa930d0851b9777506e41d 100644
--- a/roles/etcd/tasks/set_facts.yml
+++ b/roles/etcd/tasks/set_facts.yml
@@ -1,17 +1,28 @@
 ---
-- set_fact: etcd_access_address="{{ access_ip | default(ip | default(ansible_default_ipv4['address'])) }}"
+- set_fact: etcd_address="{{ ip | default(ansible_default_ipv4['address']) }}"
+- set_fact: etcd_access_address="{{ access_ip | default(etcd_address) }}"
 - set_fact: etcd_peer_url="http://{{ etcd_access_address }}:2380"
 - set_fact: etcd_client_url="http://{{ etcd_access_address }}:2379"
+- set_fact: etcd_authority="{{ access_ip|default('127.0.0.1') }}:2379"
+- set_fact: etcd_endpoint="http://{{ etcd_authority }}"
 - set_fact:
     etcd_access_addresses: |-
       {% for item in groups['etcd'] -%}
         http://{{ hostvars[item].etcd_access_address }}:2379{% if not loop.last %},{% endif %}
       {%- endfor %}
+- set_fact: etcd_access_endpoint="{% if etcd_multiaccess %}{{ etcd_access_addresses }}{% else %}{{ etcd_endpoint }}{% endif %}"
 - set_fact:
     etcd_member_name: |-
       {% for host in groups['etcd'] %}
       {%   if inventory_hostname == host %}{{"etcd"+loop.index|string }}{% endif %}
       {% endfor %}
+- set_fact:
+    etcd_proxy_member_name: |-
+      {% for host in groups['k8s-cluster'] %}
+      {%   if inventory_hostname == host %}{{"etcd-proxy"+loop.index|string }}{% endif %}
+      {% endfor %}
+- set_fact:
+    is_etcd_proxy: "{{ inventory_hostname in groups['k8s-cluster'] }}"
 - set_fact:
     is_etcd_master: "{{ inventory_hostname in groups['etcd'] }}"
 - set_fact:
diff --git a/roles/etcd/templates/deb-etcd-docker.initd.j2 b/roles/etcd/templates/deb-etcd-docker.initd.j2
index 6e5a8cb03505dfaaadc50929016b3065d6e4e547..d2528b034f269b452e00c2a5d871649e707b7f87 100644
--- a/roles/etcd/templates/deb-etcd-docker.initd.j2
+++ b/roles/etcd/templates/deb-etcd-docker.initd.j2
@@ -16,23 +16,16 @@ PATH=/sbin:/usr/sbin:/bin/:/usr/bin
 DESC="etcd k/v store"
 NAME=etcd
 DAEMON={{ docker_bin_dir | default("/usr/bin") }}/docker
-{% if is_etcd_master %}
 DAEMON_ARGS='--restart=always --env-file=/etc/etcd.env \
 --net=host \
 -v /usr/share/ca-certificates/:/etc/ssl/certs:ro \
 -v /var/lib/etcd:/var/lib/etcd:rw \
---name={{ etcd_member_name | default("etcd-proxy") }} \
+--name={{ etcd_member_name | default("etcd") }} \
 {{ etcd_image_repo }}:{{ etcd_image_tag }} \
 {% if etcd_after_v3 %}
-{{ etcd_container_bin_dir }}etcd \
-{% endif %}
-{% if is_etcd_master %}
---proxy off
-{% else %}
---proxy on
+{{ etcd_container_bin_dir }}etcd
 {% endif %}'
 
-
 SCRIPTNAME=/etc/init.d/$NAME
 DAEMON_USER=etcd
 STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/5/KILL/5}"
@@ -41,9 +34,6 @@ PID=/var/run/etcd.pid
 # Exit if the binary is not present
 [ -x "$DAEMON" ] || exit 0
 
-# Read configuration variable file if it is present
-[ -f /etc/etcd.env ] && . /etc/etcd.env
-
 # Define LSB log_* functions.
 # Depend on lsb-base (>= 3.2-14) to ensure that this file is present
 # and status_of_proc is working.
diff --git a/roles/etcd/templates/deb-etcd-host.initd.j2 b/roles/etcd/templates/deb-etcd-host.initd.j2
index ccecdce4e99b7b6273ecfe6b66e326e3732da59c..b27c0f49a63b560c05439bda70562194e71227af 100644
--- a/roles/etcd/templates/deb-etcd-host.initd.j2
+++ b/roles/etcd/templates/deb-etcd-host.initd.j2
@@ -16,11 +16,6 @@ PATH=/sbin:/usr/sbin:/bin:/usr/bin
 DESC="etcd k/v store"
 NAME=etcd
 DAEMON={{ bin_dir }}/etcd
-{% if is_etcd_master %}
-DAEMON_ARGS=""
-{% else %}
-DAEMON_ARGS="--proxy on"
-{% endif %}
 SCRIPTNAME=/etc/init.d/$NAME
 DAEMON_USER=etcd
 STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/5/KILL/5}"
diff --git a/roles/etcd/templates/deb-etcd-proxy-docker.initd.j2 b/roles/etcd/templates/deb-etcd-proxy-docker.initd.j2
new file mode 100644
index 0000000000000000000000000000000000000000..c2fa401061757f8df26b715399d6bc170e64624b
--- /dev/null
+++ b/roles/etcd/templates/deb-etcd-proxy-docker.initd.j2
@@ -0,0 +1,117 @@
+#!/bin/sh
+set -a
+
+### BEGIN INIT INFO
+# Provides:   etcd-proxy
+# Required-Start:    $local_fs $network $syslog
+# Required-Stop:
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: etcd-proxy
+# Description:
+#   etcd-proxy is a proxy for etcd: distributed, consistent key-value store for shared configuration and service discovery
+### END INIT INFO
+
+PATH=/sbin:/usr/sbin:/bin/:/usr/bin
+DESC="etcd-proxy"
+NAME=etcd-proxy
+DAEMON={{ docker_bin_dir | default("/usr/bin") }}/docker
+DAEMON_ARGS='--restart=always --env-file=/etc/etcd-proxy.env \
+--net=host \
+--stop-signal=SIGKILL \
+-v /usr/share/ca-certificates/:/etc/ssl/certs:ro \
+--name={{ etcd_proxy_member_name | default("etcd-proxy") }} \
+{{ etcd_image_repo }}:{{ etcd_image_tag }} \
+{% if etcd_after_v3 %}
+{{ etcd_container_bin_dir }}etcd
+{% endif %}'
+
+
+SCRIPTNAME=/etc/init.d/$NAME
+DAEMON_USER=etcd-proxy
+STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/5/KILL/5}"
+PID=/var/run/etcd-proxy.pid
+
+# Exit if the binary is not present
+[ -x "$DAEMON" ] || exit 0
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
+# and status_of_proc is working.
+. /lib/lsb/init-functions
+
+do_status()
+{
+    status_of_proc -p $PID "$DAEMON" "$NAME" && exit 0 || exit $?
+}
+
+# Function that starts the daemon/service
+#
+do_start()
+{
+    start-stop-daemon --background --start --quiet --make-pidfile --pidfile $PID --user $DAEMON_USER --exec $DAEMON -- \
+        $DAEMON_ARGS \
+        || return 2
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+    start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PID --name $NAME
+    RETVAL="$?"
+
+    sleep 1
+    return "$RETVAL"
+}
+
+
+case "$1" in
+  start)
+        log_daemon_msg "Starting $DESC" "$NAME"
+        do_start
+        case "$?" in
+                0|1) log_end_msg 0 || exit 0 ;;
+                2) log_end_msg 1 || exit 1 ;;
+        esac
+        ;;
+  stop)
+        log_daemon_msg "Stopping $DESC" "$NAME"
+        if do_stop; then
+            log_end_msg 0
+        else
+            log_failure_msg "Can't stop etcd-proxy"
+            log_end_msg 1
+        fi
+        ;;
+  status)
+        if do_status; then
+            log_end_msg 0
+        else
+            log_failure_msg "etcd-proxy is not running"
+            log_end_msg 1
+        fi
+        ;;
+
+  restart|force-reload)
+        log_daemon_msg "Restarting $DESC" "$NAME"
+        if do_stop; then
+            if do_start; then
+                log_end_msg 0
+                exit 0
+            else
+                rc="$?"
+            fi
+        else
+           rc="$?"
+        fi
+        log_failure_msg "Can't restart etcd-proxy"
+        log_end_msg ${rc}
+        ;;
+  *)
+        echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
+        exit 3
+        ;;
+esac
+
diff --git a/roles/etcd/templates/deb-etcd-proxy-host.initd.j2 b/roles/etcd/templates/deb-etcd-proxy-host.initd.j2
new file mode 100644
index 0000000000000000000000000000000000000000..d0858bb2f9513913ce842f7209b30c0383e6f7f0
--- /dev/null
+++ b/roles/etcd/templates/deb-etcd-proxy-host.initd.j2
@@ -0,0 +1,110 @@
+#!/bin/sh
+set -a
+
+### BEGIN INIT INFO
+# Provides:   etcd-proxy
+# Required-Start:    $local_fs $network $syslog
+# Required-Stop:
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: etcd-proxy
+# Description:
+#   etcd-proxy is a proxy for etcd: distributed, consistent key-value store for shared configuration and service discovery
+### END INIT INFO
+
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="etcd-proxy"
+NAME=etcd-proxy
+DAEMON={{ bin_dir }}/etcd
+DAEMON_ARGS=""
+SCRIPTNAME=/etc/init.d/$NAME
+DAEMON_USER=etcd
+STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/5/KILL/5}"
+PID=/var/run/etcd-proxy.pid
+
+# Exit if the binary is not present
+[ -x "$DAEMON" ] || exit 0
+
+# Read configuration variable file if it is present
+[ -f /etc/etcd-proxy.env ] && . /etc/etcd-proxy.env
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
+# and status_of_proc is working.
+. /lib/lsb/init-functions
+
+do_status()
+{
+    status_of_proc -p $PID "$DAEMON" "$NAME" && exit 0 || exit $?
+}
+
+# Function that starts the daemon/service
+#
+do_start()
+{
+    start-stop-daemon --background --start --quiet --make-pidfile --pidfile $PID --user $DAEMON_USER --exec $DAEMON -- \
+        $DAEMON_ARGS \
+        || return 2
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+    start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PID --name $NAME
+    RETVAL="$?"
+
+    sleep 1
+    return "$RETVAL"
+}
+
+
+case "$1" in
+  start)
+        log_daemon_msg "Starting $DESC" "$NAME"
+        do_start
+        case "$?" in
+                0|1) log_end_msg 0 || exit 0 ;;
+                2) log_end_msg 1 || exit 1 ;;
+        esac
+        ;;
+  stop)
+        log_daemon_msg "Stopping $DESC" "$NAME"
+        if do_stop; then
+            log_end_msg 0
+        else
+            log_failure_msg "Can't stop etcd-proxy"
+            log_end_msg 1
+        fi
+        ;;
+  status)
+        if do_status; then
+            log_end_msg 0
+        else
+            log_failure_msg "etcd-proxy is not running"
+            log_end_msg 1
+        fi
+        ;;
+
+  restart|force-reload)
+        log_daemon_msg "Restarting $DESC" "$NAME"
+        if do_stop; then
+            if do_start; then
+                log_end_msg 0
+                exit 0
+            else
+                rc="$?"
+            fi
+        else
+           rc="$?"
+        fi
+        log_failure_msg "Can't restart etcd-proxy"
+        log_end_msg ${rc}
+        ;;
+  *)
+        echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
+        exit 3
+        ;;
+esac
+
diff --git a/roles/etcd/templates/etcd-docker.service.j2 b/roles/etcd/templates/etcd-docker.service.j2
index 623ec70b978f72c9585d9b9028897959e469c021..a02e84c2547edc90df6e2e774ce8d90a5550792b 100644
--- a/roles/etcd/templates/etcd-docker.service.j2
+++ b/roles/etcd/templates/etcd-docker.service.j2
@@ -1,6 +1,6 @@
 [Unit]
 Description=etcd docker wrapper
-Requires=docker.service
+Wants=docker.service
 After=docker.service
 
 [Service]
@@ -13,15 +13,10 @@ ExecStart={{ docker_bin_dir | default("/usr/bin") }}/docker run --restart=always
 --net=host \
 -v /usr/share/ca-certificates/:/etc/ssl/certs:ro \
 -v /var/lib/etcd:/var/lib/etcd:rw \
---name={{ etcd_member_name | default("etcd-proxy") }} \
+--name={{ etcd_member_name | default("etcd") }} \
 {{ etcd_image_repo }}:{{ etcd_image_tag }} \
 {% if etcd_after_v3 %}
-{{ etcd_container_bin_dir }}etcd \
-{% endif %}
-{% if is_etcd_master %}
---proxy off
-{% else %}
---proxy on
+{{ etcd_container_bin_dir }}etcd
 {% endif %}
 ExecStartPre=-{{ docker_bin_dir | default("/usr/bin") }}/docker rm -f {{ etcd_member_name | default("etcd-proxy") }}
 ExecReload={{ docker_bin_dir | default("/usr/bin") }}/docker restart {{ etcd_member_name | default("etcd-proxy") }}
diff --git a/roles/etcd/templates/etcd-host.service.j2 b/roles/etcd/templates/etcd-host.service.j2
index ff39477acd7ec46e2141c0754dfef336ea6bf249..8a91fab920c3732a7cbd8e0ba6caa9f730f47f1f 100644
--- a/roles/etcd/templates/etcd-host.service.j2
+++ b/roles/etcd/templates/etcd-host.service.j2
@@ -6,11 +6,7 @@ After=network.target
 Type=notify
 User=etcd
 EnvironmentFile=/etc/etcd.env
-{% if inventory_hostname in groups['etcd'] %}
 ExecStart={{ bin_dir }}/etcd
-{% else %}
-ExecStart={{ bin_dir }}/etcd -proxy on
-{% endif %}
 NotifyAccess=all
 Restart=always
 RestartSec=10s
diff --git a/roles/etcd/templates/etcd-proxy-docker.service.j2 b/roles/etcd/templates/etcd-proxy-docker.service.j2
new file mode 100644
index 0000000000000000000000000000000000000000..757423bb4fdd9a765f9676fae9693febb26adb4d
--- /dev/null
+++ b/roles/etcd/templates/etcd-proxy-docker.service.j2
@@ -0,0 +1,28 @@
+[Unit]
+Description=etcd-proxy docker wrapper
+Wants=docker.service
+After=docker.service
+
+[Service]
+User=root
+PermissionsStartOnly=true
+ExecStart={{ docker_bin_dir | default("/usr/bin") }}/docker run --restart=always \
+--env-file=/etc/etcd-proxy.env \
+{# TODO(mattymo): Allow docker IP binding and disable in envfile
+   -p 2380:2380 -p 2379:2379 #}
+--net=host \
+--stop-signal=SIGKILL \
+-v /usr/share/ca-certificates/:/etc/ssl/certs:ro \
+--name={{ etcd_proxy_member_name | default("etcd-proxy") }} \
+{{ etcd_image_repo }}:{{ etcd_image_tag }} \
+{% if etcd_after_v3 %}
+{{ etcd_container_bin_dir }}etcd
+{% endif %}
+ExecStartPre=-{{ docker_bin_dir | default("/usr/bin") }}/docker rm -f {{ etcd_proxy_member_name | default("etcd-proxy") }}
+ExecReload={{ docker_bin_dir | default("/usr/bin") }}/docker restart {{ etcd_proxy_member_name | default("etcd-proxy") }}
+ExecStop={{ docker_bin_dir | default("/usr/bin") }}/docker stop {{ etcd_proxy_member_name | default("etcd-proxy") }}
+Restart=always
+RestartSec=10s
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/etcd/templates/etcd-proxy-host.service.j2 b/roles/etcd/templates/etcd-proxy-host.service.j2
new file mode 100644
index 0000000000000000000000000000000000000000..4ea5f7bc9d583bcddb8dfa7775991df3cb343a8b
--- /dev/null
+++ b/roles/etcd/templates/etcd-proxy-host.service.j2
@@ -0,0 +1,19 @@
+[Unit]
+Description=etcd-proxy
+After=network.target
+
+[Service]
+Type=notify
+User=etcd
+PermissionsStartOnly=true
+EnvironmentFile=/etc/etcd-proxy.env
+ExecStart={{ bin_dir }}/etcd
+ExecStartPre=/bin/mkdir -p /var/lib/etcd-proxy
+ExecStartPre=/bin/chown -R etcd: /var/lib/etcd-proxy
+NotifyAccess=all
+Restart=always
+RestartSec=10s
+LimitNOFILE=40000
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/etcd/templates/etcd-proxy.j2 b/roles/etcd/templates/etcd-proxy.j2
new file mode 100644
index 0000000000000000000000000000000000000000..90d6f64701c1bd790f37812b3ae60221555b946f
--- /dev/null
+++ b/roles/etcd/templates/etcd-proxy.j2
@@ -0,0 +1,5 @@
+ETCD_DATA_DIR=/var/lib/etcd-proxy
+ETCD_PROXY=on
+ETCD_LISTEN_CLIENT_URLS={{ etcd_access_endpoint }}
+ETCD_NAME={{ etcd_proxy_member_name | default("etcd-proxy") }}
+ETCD_INITIAL_CLUSTER={% for host in groups['etcd'] %}etcd{{ loop.index|string }}={{ hostvars[host]['etcd_peer_url'] }}{% if not loop.last %},{% endif %}{% endfor %}
diff --git a/roles/etcd/templates/etcd.j2 b/roles/etcd/templates/etcd.j2
index 94d9e8121956600ec76ac1936eff7c7d4fb30cbc..106217ba6eef429243fa027beabfbdf7b9c0de6e 100644
--- a/roles/etcd/templates/etcd.j2
+++ b/roles/etcd/templates/etcd.j2
@@ -1,14 +1,12 @@
 ETCD_DATA_DIR=/var/lib/etcd
-{% if is_etcd_master %}
-ETCD_ADVERTISE_CLIENT_URLS=http://{{ hostvars[inventory_hostname]['access_ip'] | default(hostvars[inventory_hostname]['ip'] | default( hostvars[inventory_hostname]['ansible_default_ipv4']['address'])) }}:2379
-ETCD_INITIAL_ADVERTISE_PEER_URLS=http://{{ hostvars[inventory_hostname]['access_ip'] | default(hostvars[inventory_hostname]['ip'] | default( hostvars[inventory_hostname]['ansible_default_ipv4']['address']))  }}:2380
+ETCD_ADVERTISE_CLIENT_URLS={{ etcd_client_url }}
+ETCD_INITIAL_ADVERTISE_PEER_URLS={{ etcd_peer_url }}
 ETCD_INITIAL_CLUSTER_STATE={% if etcd_cluster_is_healthy.rc != 0 | bool %}new{% else %}existing{% endif %}
 
+ETCD_LISTEN_CLIENT_URLS=http://{{ etcd_address }}:2379
 ETCD_ELECTION_TIMEOUT=10000
 ETCD_INITIAL_CLUSTER_TOKEN=k8s_etcd
-ETCD_LISTEN_PEER_URLS=http://{{ hostvars[inventory_hostname]['ip'] | default( hostvars[inventory_hostname]['ansible_default_ipv4']['address'])  }}:2380
+ETCD_LISTEN_PEER_URLS=http://{{ etcd_address }}:2380
 ETCD_NAME={{ etcd_member_name }}
-{% endif %}
+ETCD_PROXY=off
 ETCD_INITIAL_CLUSTER={% for host in groups['etcd'] %}etcd{{ loop.index|string }}={{ hostvars[host]['etcd_peer_url'] }}{% if not loop.last %},{% endif %}{% endfor %}
-
-ETCD_LISTEN_CLIENT_URLS=http://{{ hostvars[inventory_hostname]['ip'] | default( hostvars[inventory_hostname]['ansible_default_ipv4']['address'])  }}:2379,http://127.0.0.1:2379
diff --git a/roles/kubernetes/master/templates/kube-apiserver.j2 b/roles/kubernetes/master/templates/kube-apiserver.j2
index 07ed59cc61369ac7ecf97c87bf3f134c7174b6e8..0e3a2710da6cdbb3701b50f309f21b4ed787c1e9 100644
--- a/roles/kubernetes/master/templates/kube-apiserver.j2
+++ b/roles/kubernetes/master/templates/kube-apiserver.j2
@@ -27,7 +27,7 @@ KUBE_API_INSECURE_BIND="--insecure-bind-address={{ kube_apiserver_insecure_bind_
 KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range={{ kube_service_addresses }}"
 
 # Location of the etcd cluster
-KUBE_ETCD_SERVERS="--etcd_servers={% for host in groups['etcd'] %}http://{{ hostvars[host]['access_ip'] | default(hostvars[host]['ip'] | default(hostvars[host]['ansible_default_ipv4']['address'])) }}:2379{% if not loop.last %},{% endif %}{% endfor %}"
+KUBE_ETCD_SERVERS="--etcd_servers={{ etcd_access_endpoint }}"
 
 # Bind address for secure endpoint
 KUBE_API_ADDRESS="--bind-address={{ ip | default(ansible_default_ipv4.address) }}"
diff --git a/roles/kubernetes/master/templates/kube-apiserver.service.j2 b/roles/kubernetes/master/templates/kube-apiserver.service.j2
index 699797171da9147882b646fb8688544004a88dd6..785cfd097e3d24292b70dedbd494464fb286a25f 100644
--- a/roles/kubernetes/master/templates/kube-apiserver.service.j2
+++ b/roles/kubernetes/master/templates/kube-apiserver.service.j2
@@ -1,8 +1,8 @@
 [Unit]
 Description=Kubernetes API Server
 Documentation=https://github.com/GoogleCloudPlatform/kubernetes
-Requires=etcd.service
-After=etcd.service
+Wants=etcd-proxy.service
+After=etcd-proxy.service
 
 [Service]
 EnvironmentFile=/etc/kubernetes/kube-apiserver.env
diff --git a/roles/kubernetes/master/templates/manifests/kube-apiserver.manifest.j2 b/roles/kubernetes/master/templates/manifests/kube-apiserver.manifest.j2
index 48b013e6c354d71f26de5afd8853eed973c0d034..853a76cae2ebf7ae0ad55ddb92c759d5f9708aa0 100644
--- a/roles/kubernetes/master/templates/manifests/kube-apiserver.manifest.j2
+++ b/roles/kubernetes/master/templates/manifests/kube-apiserver.manifest.j2
@@ -11,7 +11,7 @@ spec:
     - /hyperkube
     - apiserver
     - --advertise-address={{ ip | default(ansible_default_ipv4.address) }}
-    - --etcd-servers={% for srv in groups['etcd'] %}http://{{ hostvars[srv]['access_ip'] | default(hostvars[srv]['ip']|default(hostvars[srv]['ansible_default_ipv4']['address'])) }}:2379{% if not loop.last %},{% endif %}{% endfor %}
+    - --etcd-servers={{ etcd_access_endpoint }}
     - --admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
     - --service-cluster-ip-range={{ kube_service_addresses }}
     - --client-ca-file={{ kube_cert_dir }}/ca.pem
diff --git a/roles/kubernetes/node/templates/cni-calico.conf.j2 b/roles/kubernetes/node/templates/cni-calico.conf.j2
index c11067965d87929143a8b0d274df1e05c4d742e3..c48b084a53727e44ce8cb37de83dd8de6113bce5 100644
--- a/roles/kubernetes/node/templates/cni-calico.conf.j2
+++ b/roles/kubernetes/node/templates/cni-calico.conf.j2
@@ -1,7 +1,7 @@
 {
   "name": "calico-k8s-network",
   "type": "calico",
-  "etcd_authority": "127.0.0.1:2379",
+  "etcd_authority": "{{ etcd_authority }}",
   "log_level": "info",
   "ipam": {
     "type": "calico-ipam"
diff --git a/roles/network_plugin/calico/tasks/main.yml b/roles/network_plugin/calico/tasks/main.yml
index a8185d4f2a41665443780b22fb77671e00f95250..7e732d632ec8594d4cd3db128a904973053d822a 100644
--- a/roles/network_plugin/calico/tasks/main.yml
+++ b/roles/network_plugin/calico/tasks/main.yml
@@ -44,7 +44,7 @@
 
 - name: Calico | Check if calico network pool has already been configured
   uri:
-    url: "http://127.0.0.1:2379/v2/keys/calico/v1/ipam/v4/pool"
+    url: "{{ etcd_endpoint }}/v2/keys/calico/v1/ipam/v4/pool"
     return_content: yes
     status_code: 200,404
   register: calico_conf
@@ -70,7 +70,7 @@
 
 - name: Calico | Get calico configuration from etcd
   uri:
-    url: "http://127.0.0.1:2379/v2/keys/calico/v1/ipam/v4/pool"
+    url: "{{ etcd_endpoint }}/v2/keys/calico/v1/ipam/v4/pool"
     return_content: yes
   register: calico_pools
   run_once: true
diff --git a/roles/network_plugin/calico/templates/calico-node.service.j2 b/roles/network_plugin/calico/templates/calico-node.service.j2
index 8c8af69711f99949b172fc4b54314619ec28974c..191ae8f40c7a7767c6e620571673cfc8e54947bd 100644
--- a/roles/network_plugin/calico/templates/calico-node.service.j2
+++ b/roles/network_plugin/calico/templates/calico-node.service.j2
@@ -1,8 +1,8 @@
 [Unit]
 Description=Calico per-node agent
 Documentation=https://github.com/projectcalico/calico-docker
-Requires=docker.service
-After=docker.service etcd.service
+Wants=docker.service etcd-proxy.service
+After=docker.service etcd-proxy.service
 
 [Service]
 User=root
diff --git a/roles/network_plugin/calico/templates/calicoctl-container.j2 b/roles/network_plugin/calico/templates/calicoctl-container.j2
index 9436a50e731d6e357ddd1f8f2c8324546d845fb4..a6bf88896d3279f9d16dc6ec3fcf981df96620fe 100644
--- a/roles/network_plugin/calico/templates/calicoctl-container.j2
+++ b/roles/network_plugin/calico/templates/calicoctl-container.j2
@@ -1,6 +1,6 @@
 #!/bin/bash
 /usr/bin/docker run --privileged --rm \
---net=host -e ETCD_AUTHORITY=127.0.0.1:2379 \
+--net=host -e ETCD_AUTHORITY={{ etcd_authority }} \
 -v /usr/bin/docker:/usr/bin/docker \
 -v /var/run/docker.sock:/var/run/docker.sock \
 -v /var/run/calico:/var/run/calico \
diff --git a/roles/network_plugin/calico/templates/network-environment.j2 b/roles/network_plugin/calico/templates/network-environment.j2
index 9a588cfc4ddb7c090c8cf0501d45341ab4380d53..99d592a5ee110714f15ef1253adb9c07163e78ed 100644
--- a/roles/network_plugin/calico/templates/network-environment.j2
+++ b/roles/network_plugin/calico/templates/network-environment.j2
@@ -6,4 +6,4 @@ DEFAULT_IPV4={{ip | default(ansible_default_ipv4.address) }}
 KUBERNETES_MASTER={{ hostvars[groups['kube-master'][0]]['access_ip'] | default(hostvars[groups['kube-master'][0]]['ip'] | default(hostvars[groups['kube-master'][0]]['ansible_default_ipv4']['address'])) }}
 
 # IP and port of etcd instance used by Calico
-ETCD_AUTHORITY=127.0.0.1:2379
+ETCD_AUTHORITY={{ etcd_authority }}
diff --git a/roles/network_plugin/flannel/templates/flannel-pod.yml b/roles/network_plugin/flannel/templates/flannel-pod.yml
index 62f18f5009483d476d71a30467489f3c9684e3ee..b9014f003fd2220cd6ec3be2707bcf9089c51132 100644
--- a/roles/network_plugin/flannel/templates/flannel-pod.yml
+++ b/roles/network_plugin/flannel/templates/flannel-pod.yml
@@ -21,7 +21,7 @@
         args:
           - "--network-config=/etc/flannel-network.json"
           - "--etcd-prefix=/{{ cluster_name }}/network"
-          - "--etcd-server=http://{{ groups['etcd'][0] }}:2379"
+          - "--etcd-server={{ etcd_endpoint }}"
         volumeMounts:
           - name: "networkconfig"
             mountPath: "/etc/flannel-network.json"
@@ -31,7 +31,7 @@
         command:
           - "/bin/sh"
           - "-c"
-          - "/opt/bin/flanneld -etcd-endpoints {% for srv in groups['etcd'] %}http://{{ srv }}:2379{% if not loop.last %},{% endif %}{% endfor %} -etcd-prefix /{{ cluster_name }}/network {% if flannel_interface is defined %}-iface {{ flannel_interface }}{% endif %} {% if flannel_public_ip is defined %}-public-ip {{ flannel_public_ip }}{% endif %}"
+          - "/opt/bin/flanneld -etcd-endpoints {{ etcd_access_endpoint }} -etcd-prefix /{{ cluster_name }}/network {% if flannel_interface is defined %}-iface {{ flannel_interface }}{% endif %} {% if flannel_public_ip is defined %}-public-ip {{ flannel_public_ip }}{% endif %}"
         ports:
           - hostPort: 10253
             containerPort: 10253