diff --git a/docs/large-deploymets.md b/docs/large-deploymets.md
new file mode 100644
index 0000000000000000000000000000000000000000..2a36c3ebc98422a94e64711fcc3ff00fd0d322b3
--- /dev/null
+++ b/docs/large-deploymets.md
@@ -0,0 +1,19 @@
+Large deployments of K8s
+========================
+
+For a large scaled deployments, consider the following configuration changes:
+
+* Tune [ansible settings](http://docs.ansible.com/ansible/intro_configuration.html)
+  for `forks` and `timeout` vars to fit large numbers of nodes being deployed.
+
+* Override containers' `foo_image_repo` vars to point to intranet registry.
+
+* Override the ``download_run_once: true`` to download binaries and container
+  images only once then push to nodes in batches.
+
+* Adjust the `retry_stagger` global var as appropriate. It should provide sane
+  load on a delegate (the first K8s master node) then retrying failed
+  push or download operations.
+
+For example, when deploying 200 nodes, you may want to run ansible with
+``--forks=50``, ``--timeout=600`` and define the ``retry_stagger: 60``.
diff --git a/inventory/group_vars/all.yml b/inventory/group_vars/all.yml
index 91fab4c06d3fc66de1ff617e8111a05427b1c67a..2de01828c45f8a634d780693bd8e08fccc627e15 100644
--- a/inventory/group_vars/all.yml
+++ b/inventory/group_vars/all.yml
@@ -7,6 +7,8 @@ bin_dir: /usr/local/bin
 # Where the binaries will be downloaded.
 # Note: ensure that you've enough disk space (about 1G)
 local_release_dir: "/tmp/releases"
+# Random shifts for retrying failed ops like pushing/downloading
+retry_stagger: 5
 
 # Uncomment this line for CoreOS only.
 # Directory where python binary is installed
diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml
index 826e16978697da3325846574e82cc3e61d53c911..1d237f5e9ac9ad81c3000899f28a7aebd361855a 100644
--- a/roles/docker/tasks/main.yml
+++ b/roles/docker/tasks/main.yml
@@ -30,7 +30,7 @@
   register: keyserver_task_result
   until: keyserver_task_result|success
   retries: 4
-  delay: "{{ 20 | random + 3 }}"
+  delay: "{{ retry_stagger | random + 3 }}"
   with_items: "{{ docker_repo_key_info.repo_keys }}"
   when: ansible_os_family != "CoreOS"
 
@@ -58,7 +58,7 @@
   register: docker_task_result
   until: docker_task_result|success
   retries: 4
-  delay: "{{ 20 | random + 3 }}"
+  delay: "{{ retry_stagger | random + 3 }}"
   with_items: "{{ docker_package_info.pkgs }}"
   when: (ansible_os_family != "CoreOS") and (docker_package_info.pkgs|length > 0)
 
diff --git a/roles/download/defaults/main.yml b/roles/download/defaults/main.yml
index 610fb5b7a2e1564af405e602a791426e144a8f5c..51f0b02fd3b34395f6696e9687d59b36ea8105cc 100644
--- a/roles/download/defaults/main.yml
+++ b/roles/download/defaults/main.yml
@@ -13,6 +13,8 @@ etcd_version: v3.0.6
 calico_version: v0.20.0
 calico_cni_version: v1.3.1
 weave_version: v1.6.1
+flannel_version: 0.5.5
+flannel_server_helper_version: 0.1
 
 # Download URL's
 etcd_download_url: "https://storage.googleapis.com/kargo/{{etcd_version}}_etcd"
@@ -26,6 +28,22 @@ calico_cni_ipam_checksum: "3df6951a30749c279229e7e318e74ac4e41263996125be65257db
 weave_checksum: "9bf9d6e5a839e7bcbb28cc00c7acae9d09284faa3e7a3720ca9c2b9e93c68580"
 etcd_checksum: "385afd518f93e3005510b7aaa04d38ee4a39f06f5152cd33bb86d4f0c94c7485"
 
+# Containers
+# Possible values: host, docker
+etcd_deployment_type: "docker"
+etcd_image_repo: "quay.io/coreos/etcd"
+etcd_image_tag: "{{ etcd_version }}"
+flannel_server_helper_image_repo: "gcr.io/google_containers/flannel-server-helper"
+flannel_server_helper_image_tag: "{{ flannel_server_helper_version }}"
+flannel_image_repo: "quay.io/coreos/flannel"
+flannel_image_tag: "{{ flannel_version }}"
+calicoctl_image_repo: "calico/ctl"
+calicoctl_image_tag: "{{ calico_version }}"
+calico_node_image_repo: "calico/node"
+calico_node_image_tag: "{{ calico_version }}"
+hyperkube_image_repo: "quay.io/coreos/hyperkube"
+hyperkube_image_tag: "{{ kube_version }}_coreos.0"
+
 downloads:
   calico_cni_plugin:
     dest: calico/bin/calico
@@ -35,6 +53,7 @@ downloads:
     url: "{{ calico_cni_download_url }}"
     owner: "root"
     mode: "0755"
+    enabled: "{{ kube_network_plugin == 'calico' }}"
   calico_cni_plugin_ipam:
     dest: calico/bin/calico-ipam
     version: "{{calico_cni_version}}"
@@ -43,6 +62,7 @@ downloads:
     url: "{{ calico_cni_ipam_download_url }}"
     owner: "root"
     mode: "0755"
+    enabled: "{{ kube_network_plugin == 'calico' }}"
   weave:
     dest: weave/bin/weave
     version: "{{weave_version}}"
@@ -51,6 +71,7 @@ downloads:
     sha256: "{{ weave_checksum }}"
     owner: "root"
     mode: "0755"
+    enabled: "{{ kube_network_plugin == 'weave' }}"
   etcd:
     version: "{{etcd_version}}"
     dest: "etcd/etcd-{{ etcd_version }}-linux-amd64.tar.gz"
@@ -60,10 +81,38 @@ downloads:
     unarchive: true
     owner: "etcd"
     mode: "0755"
-  nothing:
-    enabled: false
+    container: "{{ etcd_deployment_type == 'docker' }}"
+    repo: "{{ etcd_image_repo }}"
+    tag: "{{ etcd_image_tag }}"
+  hyperkube:
+    container: true
+    repo: "{{ hyperkube_image_repo }}"
+    tag: "{{ hyperkube_image_tag }}"
+  flannel:
+    container: true
+    repo: "{{ flannel_image_repo }}"
+    tag: "{{ flannel_image_tag }}"
+    enabled: "{{ kube_network_plugin == 'flannel' }}"
+  flannel_server_helper:
+    container: true
+    repo: "{{ flannel_server_helper_image_repo }}"
+    tag: "{{ flannel_server_helper_image_tag }}"
+    enabled: "{{ kube_network_plugin == 'flannel' }}"
+  calicoctl:
+    container: true
+    repo: "{{ calicoctl_image_repo }}"
+    tag: "{{ calicoctl_image_tag }}"
+    enabled: "{{ kube_network_plugin == 'calico' }}"
+  calico_node:
+    container: true
+    repo: "{{ calico_node_image_repo }}"
+    tag: "{{ calico_node_image_tag }}"
+    enabled: "{{ kube_network_plugin == 'calico' }}"
 
 download:
+  container: "{{ file.container|default('false') }}"
+  repo: "{{ file.repo|default(None) }}"
+  tag: "{{ file.tag|default(None) }}"
   enabled: "{{ file.enabled|default('true') }}"
   dest: "{{ file.dest|default(None) }}"
   version: "{{ file.version|default(None) }}"
diff --git a/roles/download/tasks/main.yml b/roles/download/tasks/main.yml
index 40c52f5ea471ef5258184d179a7c6a9e4fc2edbf..6329a11084cb8eef48cbd7c2029e95ae75074f1d 100644
--- a/roles/download/tasks/main.yml
+++ b/roles/download/tasks/main.yml
@@ -4,11 +4,12 @@
 - name: downloading...
   debug:
     msg: "{{ download.url }}"
-  when: "{{ download.enabled|bool }}"
+  when: "{{ download.enabled|bool and not download.container|bool }}"
 
 - name: Create dest directories
   file: path={{local_release_dir}}/{{download.dest|dirname}} state=directory recurse=yes
-  when: "{{ download.enabled|bool }}"
+  when: "{{ download.enabled|bool and not download.container|bool }}"
+  delegate_to: "{{ groups['kube-master'][0] if download_run_once|bool else omit }}"
   run_once: "{{ download_run_once|bool }}"
 
 - name: Download items
@@ -18,7 +19,12 @@
     sha256sum: "{{download.sha256 | default(omit)}}"
     owner: "{{ download.owner|default(omit) }}"
     mode: "{{ download.mode|default(omit) }}"
-  when: "{{ download.enabled|bool }}"
+  register: get_url_result
+  until: "'OK' in get_url_result.msg or 'file already exists' in get_url_result.msg"
+  retries: 4
+  delay: "{{ retry_stagger | random + 3 }}"
+  when: "{{ download.enabled|bool and not download.container|bool }}"
+  delegate_to: "{{ groups['kube-master'][0] if download_run_once|bool else omit }}"
   run_once: "{{ download_run_once|bool }}"
 
 - name: Extract archives
@@ -28,7 +34,8 @@
     owner: "{{ download.owner|default(omit) }}"
     mode: "{{ download.mode|default(omit) }}"
     copy: no
-  when: "{{ download.enabled|bool }} and ({{download.unarchive is defined and download.unarchive == True}})"
+  when: "{{ download.enabled|bool and not download.container|bool and download.unarchive is defined and download.unarchive == True }}"
+  delegate_to: "{{ groups['kube-master'][0] if download_run_once|bool else omit }}"
   run_once: "{{ download_run_once|bool }}"
 
 - name: Fix permissions
@@ -37,5 +44,50 @@
     path: "{{local_release_dir}}/{{download.dest}}"
     owner: "{{ download.owner|default(omit) }}"
     mode: "{{ download.mode|default(omit) }}"
-  when: "{{ download.enabled|bool }} and ({{download.unarchive is not defined or download.unarchive == False}})"
+  when: "{{ download.enabled|bool and not download.container|bool and (download.unarchive is not defined or download.unarchive == False) }}"
+  delegate_to: "{{ groups['kube-master'][0] if download_run_once|bool else omit }}"
   run_once: "{{ download_run_once|bool }}"
+
+- name: pulling...
+  debug:
+    msg: "{{ download.repo }}:{{ download.tag }}"
+  when: "{{ download.enabled|bool and download.container|bool }}"
+
+- name: Create dest directory for saved/loaded container images
+  file: path="{{local_release_dir}}/containers" state=directory recurse=yes
+  when: "{{ download.enabled|bool and download.container|bool }}"
+
+#NOTE(bogdando) this brings no docker-py deps for nodes
+- name: Download containers
+  command: "/usr/bin/docker pull {{ download.repo }}:{{ download.tag }}"
+  register: pull_task_result
+  until: pull_task_result.rc == 0
+  retries: 4
+  delay: "{{ retry_stagger | random + 3 }}"
+  when: "{{ download.enabled|bool and download.container|bool }}"
+  delegate_to: "{{ groups['kube-master'][0] if download_run_once|bool else omit }}"
+  run_once: "{{ download_run_once|bool }}"
+
+- set_fact:
+    fname: "{{local_release_dir}}/containers/{{download.repo|regex_replace('/|\0|:', '_')}}:{{download.tag|regex_replace('/|\0|:', '_')}}.tar"
+
+- name: Download | save container images
+  shell: docker save "{{ download.repo }}:{{ download.tag }}" > "{{ fname }}"
+  delegate_to: "{{groups['kube-master'][0]}}"
+  run_once: true
+  when: ansible_os_family != "CoreOS" and download_run_once|bool
+
+- name: Download | get container images
+  synchronize:
+    src: "{{ fname }}"
+    dest: "{{local_release_dir}}/containers"
+    mode: push
+  register: get_task
+  until: get_task|success
+  retries: 4
+  delay: "{{ retry_stagger | random + 3 }}"
+  when: ansible_os_family != "CoreOS" and inventory_hostname != groups['kube-master'][0] and download_run_once|bool
+
+- name: Download | load container images
+  shell: docker load < "{{ fname }}"
+  when: ansible_os_family != "CoreOS" and inventory_hostname != groups['kube-master'][0] and download_run_once|bool
diff --git a/roles/etcd/defaults/main.yml b/roles/etcd/defaults/main.yml
index 585f75a40501a44d492265e969f6ab04ddf0a2bb..02234a2fe773c09bb957ef7d066bd3399fa59867 100644
--- a/roles/etcd/defaults/main.yml
+++ b/roles/etcd/defaults/main.yml
@@ -1,10 +1,2 @@
 ---
-etcd_version: v3.0.6
 etcd_bin_dir: "{{ local_release_dir }}/etcd/etcd-{{ etcd_version }}-linux-amd64/"
-
-# Possible values: host, docker
-etcd_deployment_type: "docker"
-
-
-etcd_image_repo: "quay.io/coreos/etcd"
-etcd_image_tag: "{{ etcd_version }}"
diff --git a/roles/etcd/meta/main.yml b/roles/etcd/meta/main.yml
index 8e4cb5846de2f8840ad0b67f58d957542e086f37..b55966a99114442247f044535cb13f36b85b50e1 100644
--- a/roles/etcd/meta/main.yml
+++ b/roles/etcd/meta/main.yml
@@ -3,8 +3,7 @@ dependencies:
   - role: adduser
     user: "{{ addusers.etcd }}"
     when: ansible_os_family != 'CoreOS'
-  - role: download
-    file: "{{ downloads.etcd }}"
-    when: etcd_deployment_type == "host"
   - role: docker
     when: (ansible_os_family != "CoreOS" and etcd_deployment_type == "docker" or inventory_hostname in groups['k8s-cluster'])
+  - role: download
+    file: "{{ downloads.etcd }}"
diff --git a/roles/etcd/tasks/install.yml b/roles/etcd/tasks/install.yml
index 959133c29fd61f2d38da28f9bec45261a0c6b499..aa7f32ca38753a9e49722540b04c22262b37b040 100644
--- a/roles/etcd/tasks/install.yml
+++ b/roles/etcd/tasks/install.yml
@@ -20,7 +20,7 @@
   register: etcd_task_result
   until: etcd_task_result.rc == 0
   retries: 4
-  delay: "{{ 20 | random + 3 }}"
+  delay: "{{ retry_stagger | random + 3 }}"
   changed_when: false
 
 #Plan B: looks nicer, but requires docker-py on all hosts:
diff --git a/roles/kubernetes/master/defaults/main.yml b/roles/kubernetes/master/defaults/main.yml
index ac23c0ada8a4c86a352d484a177aa57af5231f2f..d0be14d647210ab45b5fc9085bb247c5f7907ab4 100644
--- a/roles/kubernetes/master/defaults/main.yml
+++ b/roles/kubernetes/master/defaults/main.yml
@@ -10,6 +10,3 @@ kube_users_dir: "{{ kube_config_dir }}/users"
 # An experimental dev/test only dynamic volumes provisioner,
 # for PetSets. Works for kube>=v1.3 only.
 kube_hostpath_dynamic_provisioner: "false"
-
-hyperkube_image_repo: "quay.io/coreos/hyperkube"
-hyperkube_image_tag: "{{ kube_version }}_coreos.0"
diff --git a/roles/kubernetes/master/meta/main.yml b/roles/kubernetes/master/meta/main.yml
index 021c01de4b5d6f3ffd229b1d39d9b3db0f1a67a7..f4da42e39eafe305d675ab26e3237be86b4ebea6 100644
--- a/roles/kubernetes/master/meta/main.yml
+++ b/roles/kubernetes/master/meta/main.yml
@@ -1,4 +1,4 @@
 ---
 dependencies:
-  - role: download # For kube_version variable
-    file: "{{ downloads.nothing }}"
+  - role: download
+    file: "{{ downloads.hyperkube }}"
diff --git a/roles/kubernetes/master/tasks/main.yml b/roles/kubernetes/master/tasks/main.yml
index e8dfe08fcd12b4992c8ad1cc7cf3bc9b6beb55e5..ff6abcb13156770698884b50a0cf6e4eb57763de 100644
--- a/roles/kubernetes/master/tasks/main.yml
+++ b/roles/kubernetes/master/tasks/main.yml
@@ -12,7 +12,7 @@
   register: kube_task_result
   until: kube_task_result.rc == 0
   retries: 4
-  delay: "{{ 20 | random + 3 }}"
+  delay: "{{ retry_stagger | random + 3 }}"
   changed_when: false
 
 - name: Write kube-apiserver manifest
diff --git a/roles/kubernetes/node/defaults/main.yml b/roles/kubernetes/node/defaults/main.yml
index 96b941a5efec0a02cc78eeb52ec2cd3720db5d0b..7dc7d1183d2827cb487c9803a56f90a804666055 100644
--- a/roles/kubernetes/node/defaults/main.yml
+++ b/roles/kubernetes/node/defaults/main.yml
@@ -8,9 +8,6 @@ kube_resolv_conf: "/etc/resolv.conf"
 
 kube_proxy_mode: iptables
 
-hyperkube_image_repo: "quay.io/coreos/hyperkube"
-hyperkube_image_tag: "{{ kube_version }}_coreos.0"
-
 # IP address of the DNS server.
 # Kubernetes will create a pod with several containers, serving as the DNS
 # server and expose it under this IP address. The IP address must be from
diff --git a/roles/kubernetes/node/meta/main.yml b/roles/kubernetes/node/meta/main.yml
index c65c683933f857db60111fb7602eaded495aa9f7..b9cbbd9ff59b47645ae69dc46e0db6013de82f84 100644
--- a/roles/kubernetes/node/meta/main.yml
+++ b/roles/kubernetes/node/meta/main.yml
@@ -1,5 +1,5 @@
 ---
 dependencies:
-  - role: download #For kube_version
-    file: "{{ downloads.nothing }}"
+  - role: download
+    file: "{{ downloads.hyperkube }}"
   - role: kubernetes/secrets
diff --git a/roles/kubernetes/preinstall/tasks/main.yml b/roles/kubernetes/preinstall/tasks/main.yml
index 8c22b73bfe1a8290d33aea9470957fcec98b5af3..8c2aecec5e49f0d49bedafa09f7a3d558e003b2a 100644
--- a/roles/kubernetes/preinstall/tasks/main.yml
+++ b/roles/kubernetes/preinstall/tasks/main.yml
@@ -104,7 +104,7 @@
   register: pkgs_task_result
   until: pkgs_task_result|success
   retries: 4
-  delay: "{{ 20 | random + 3 }}"
+  delay: "{{ retry_stagger | random + 3 }}"
   with_items: "{{required_pkgs | default([]) | union(common_required_pkgs|default([]))}}"
   when: ansible_os_family != "CoreOS"
 
diff --git a/roles/network_plugin/calico/defaults/main.yml b/roles/network_plugin/calico/defaults/main.yml
index 3cdf5b4920e04525ba02120ffcde759a4393f95a..b5b275e04167cd96a88bc0524ab25d45b98c1a40 100644
--- a/roles/network_plugin/calico/defaults/main.yml
+++ b/roles/network_plugin/calico/defaults/main.yml
@@ -7,9 +7,3 @@ ipip: false
 
 # cloud_provider can only be set to 'gce' or 'aws'
 # cloud_provider:
-
-calicoctl_image_repo: calico/ctl
-calicoctl_image_tag: "{{ calico_version }}"
-
-calico_node_image_repo: calico/node
-calico_node_image_tag: "{{ calico_version }}"
diff --git a/roles/network_plugin/calico/meta/main.yml b/roles/network_plugin/calico/meta/main.yml
index 92ab5391b62b9732f94c769bd61e517f25a9b33e..c13e976d3821e4325420577048b5f30ccc750392 100644
--- a/roles/network_plugin/calico/meta/main.yml
+++ b/roles/network_plugin/calico/meta/main.yml
@@ -4,3 +4,9 @@ dependencies:
     file: "{{ downloads.calico_cni_plugin }}"
   - role: download
     file: "{{ downloads.calico_cni_plugin_ipam }}"
+  - role: download
+    file: "{{ downloads.calico_node }}"
+  - role: download
+    file: "{{ downloads.calicoctl }}"
+  - role: download
+    file: "{{ downloads.hyperkube }}"
diff --git a/roles/network_plugin/calico/tasks/main.yml b/roles/network_plugin/calico/tasks/main.yml
index 1ce6c79d3e33cd517b02afa6f2b326f5641a6679..ff7bc32ae7d88278d968cfded28677f03e5b8652 100644
--- a/roles/network_plugin/calico/tasks/main.yml
+++ b/roles/network_plugin/calico/tasks/main.yml
@@ -48,7 +48,7 @@
   register: cni_task_result
   until: cni_task_result.rc == 0
   retries: 4
-  delay: "{{ 20 | random + 3 }}"
+  delay: "{{ retry_stagger | random + 3 }}"
   changed_when: false
   when: use_hyperkube_cni
 
diff --git a/roles/network_plugin/flannel/defaults/main.yml b/roles/network_plugin/flannel/defaults/main.yml
index 8ff48cdb0f02617b6b3a08e54e89069264b4d21d..ce00090eceb545075406bebd3e5da0c0cf5519bc 100644
--- a/roles/network_plugin/flannel/defaults/main.yml
+++ b/roles/network_plugin/flannel/defaults/main.yml
@@ -10,10 +10,3 @@ flannel_public_ip: "{{ access_ip|default(ip|default(ansible_default_ipv4.address
 # You can choose what type of flannel backend to use
 # please refer to flannel's docs : https://github.com/coreos/flannel/blob/master/README.md
 flannel_backend_type: "vxlan"
-
-
-flannel_server_helper_image_repo: "gcr.io/google_containers/flannel-server-helper"
-flannel_server_helper_image_tag: "0.1"
-
-flannel_image_repo: "quay.io/coreos/flannel"
-flannel_image_tag: "0.5.5"
diff --git a/roles/network_plugin/flannel/meta/main.yml b/roles/network_plugin/flannel/meta/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..935d9c3bbe8b5d3396c18506c83d5777112b8fa2
--- /dev/null
+++ b/roles/network_plugin/flannel/meta/main.yml
@@ -0,0 +1,6 @@
+---
+dependencies:
+  - role: download
+    file: "{{ downloads.flannel_server_helper }}"
+  - role: download
+    file: "{{ downloads.flannel }}"