diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6a1eef6abac75c3509f11465a54b40c8b89f2ab1..e03e640178bcdcd02f2430ebbe15b5ffa21c779a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -308,6 +308,10 @@ before_script:
 # stage: deploy-special
   MOVED_TO_GROUP_VARS: "true"
 
+.opensuse_canal_variables: &opensuse_canal_variables
+# stage: deploy-part2
+  MOVED_TO_GROUP_VARS: "true"
+
 
 # Builds for PRs only (premoderated by unit-tests step) and triggers (auto)
 ### PR JOBS PART1
@@ -589,6 +593,17 @@ gce_centos7-calico-ha-triggers:
   when: on_success
   only: ['triggers']
 
+gce_opensuse-canal:
+  stage: deploy-part2
+  <<: *job
+  <<: *gce
+  variables:
+    <<: *gce_variables
+    <<: *opensuse_canal_variables
+  when: manual
+  except: ['triggers']
+  only: ['master', /^pr-.*$/]
+
 # no triggers yet https://github.com/kubernetes-incubator/kargo/issues/613
 gce_coreos-alpha-weave-ha:
   stage: deploy-special
diff --git a/README.md b/README.md
index 081c28fe6b247a3992013bc88b643f2284470825..45a3515bfd4715886ca6a58c7ceda072f83ba566 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,7 @@ Documents
 -   [Vagrant install](docs/vagrant.md)
 -   [CoreOS bootstrap](docs/coreos.md)
 -   [Debian Jessie setup](docs/debian.md)
+-   [openSUSE setup](docs/opensuse.md)
 -   [Downloaded artifacts](docs/downloads.md)
 -   [Cloud providers](docs/cloud.md)
 -   [OpenStack](docs/openstack.md)
@@ -70,6 +71,7 @@ Supported Linux Distributions
 -   **Ubuntu** 16.04
 -   **CentOS/RHEL** 7
 -   **Fedora/CentOS** Atomic
+-   **openSUSE** Leap 42.3/Tumbleweed
 
 Note: Upstart/SysV init based OS types are not supported.
 
diff --git a/Vagrantfile b/Vagrantfile
index 720e2419f8475c913357ac185b40a7b5ced046be..d0b6b73d13c3c78af15f7d30d8ed88dd1e1a294f 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -18,6 +18,8 @@ SUPPORTED_OS = {
   "coreos-beta"   => {box: "coreos-beta",        bootstrap_os: "coreos", user: "core", box_url: COREOS_URL_TEMPLATE % ["beta"]},
   "ubuntu"        => {box: "bento/ubuntu-16.04", bootstrap_os: "ubuntu", user: "vagrant"},
   "centos"        => {box: "centos/7",           bootstrap_os: "centos", user: "vagrant"},
+  "opensuse"      => {box: "opensuse/openSUSE-42.3-x86_64", bootstrap_os: "opensuse", use: "vagrant"},
+  "opensuse-tumbleweed" => {box: "opensuse/openSUSE-Tumbleweed-x86_64", bootstrap_os: "opensuse", use: "vagrant"},
 }
 
 # Defaults for config options defined in CONFIG
@@ -84,7 +86,6 @@ Vagrant.configure("2") do |config|
   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
@@ -110,8 +111,10 @@ Vagrant.configure("2") do |config|
         end
       end
 
+      config.vm.synced_folder ".", "/vagrant", type: "rsync", rsync__args: ['--verbose', '--archive', '--delete', '-z']
+
       $shared_folders.each do |src, dst|
-        config.vm.synced_folder src, dst
+        config.vm.synced_folder src, dst, type: "rsync", rsync__args: ['--verbose', '--archive', '--delete', '-z']
       end
 
       config.vm.provider :virtualbox do |vb|
diff --git a/docs/opensuse.md b/docs/opensuse.md
new file mode 100644
index 0000000000000000000000000000000000000000..88fac3790e36a3a0865344e34ab5dfcf29875081
--- /dev/null
+++ b/docs/opensuse.md
@@ -0,0 +1,19 @@
+openSUSE Leap 42.3 and Tumbleweed
+===============
+
+openSUSE Leap installation Notes:
+
+- Install Ansible
+
+  ```
+  sudo zypper ref
+  sudo zypper -n install ansible
+
+  ```
+
+- Install Jinja2 and Python-Netaddr
+
+  ```sudo zypper -n install python-Jinja2 python-netaddr```
+
+
+Now you can continue with [Preparing your deployment](getting-started.md#starting-custom-deployment)
diff --git a/roles/bootstrap-os/tasks/bootstrap-opensuse.yml b/roles/bootstrap-os/tasks/bootstrap-opensuse.yml
new file mode 100644
index 0000000000000000000000000000000000000000..abedd2195f84e9dd192e5c3adc113c4af282c324
--- /dev/null
+++ b/roles/bootstrap-os/tasks/bootstrap-opensuse.yml
@@ -0,0 +1,7 @@
+---
+- name: Install required packages (SUSE)
+  package:
+    name: "{{ item }}"
+    state: present
+  with_items:
+    - python-cryptography
diff --git a/roles/bootstrap-os/tasks/main.yml b/roles/bootstrap-os/tasks/main.yml
index 01031deeb807fae8c70146e00c4060720ad4c1ec..c921b643e0e43c259e073bcc337c25ab6ab83b60 100644
--- a/roles/bootstrap-os/tasks/main.yml
+++ b/roles/bootstrap-os/tasks/main.yml
@@ -11,6 +11,9 @@
 - import_tasks: bootstrap-centos.yml
   when: bootstrap_os == "centos"
 
+- import_tasks: bootstrap-opensuse.yml
+  when: bootstrap_os == "opensuse"
+
 - import_tasks: setup-pipelining.yml
 
 - name: check if atomic host
@@ -26,18 +29,25 @@
     gather_subset: '!all'
     filter: ansible_*
 
-- name: Assign inventory name to unconfigured hostnames (non-CoreOS)
+- name: Assign inventory name to unconfigured hostnames (non-CoreOS and Tumbleweed)
   hostname:
     name: "{{inventory_hostname}}"
-  when: ansible_os_family not in ['CoreOS', 'Container Linux by CoreOS'] and override_system_hostname
+  when:
+    - override_system_hostname
+    - ansible_distribution not in ['openSUSE Tumbleweed']
+    - ansible_os_family not in ['CoreOS', 'Container Linux by CoreOS']
 
-- name: Assign inventory name to unconfigured hostnames (CoreOS only)
+- name: Assign inventory name to unconfigured hostnames (CoreOS and Tumbleweed only)
   command: "hostnamectl set-hostname  {{inventory_hostname}}"
   register: hostname_changed
-  when: ansible_hostname == 'localhost' and ansible_os_family in ['CoreOS', 'Container Linux by CoreOS'] and override_system_hostname
+  when:
+    - ansible_hostname == 'localhost'
+    - ansible_distribution in ['openSUSE Tumbleweed'] or ansible_os_family in ['CoreOS', 'Container Linux by CoreOS']
+    - override_system_hostname
 
-- name: Update hostname fact (CoreOS only)
+- name: Update hostname fact (CoreOS and Tumbleweed only)
   setup:
     gather_subset: '!all'
     filter: ansible_hostname
-  when: ansible_os_family in ['CoreOS', 'Container Linux by CoreOS'] and hostname_changed.changed
+  when:
+    - hostname_changed.changed
diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml
index 729397b449635a607c6faf318722c5f987da4fd1..3668f61b8dfb636755d8b86a5bbcea7875d0f861 100644
--- a/roles/docker/tasks/main.yml
+++ b/roles/docker/tasks/main.yml
@@ -15,6 +15,14 @@
   tags:
     - facts
 
+# https://yum.dockerproject.org/repo/main/opensuse/ contains packages for an EOL
+# openSUSE version so we can't use it. The only alternative is to use the docker
+# packages from the distribution repositories.
+- name: Warn about Docker version on SUSE
+  debug:
+    msg: "SUSE distributions always install Docker from the distro repos"
+  when: ansible_pkg_mgr == 'zypper'
+
 - include_tasks: set_facts_dns.yml
   when: dns_mode != 'none' and resolvconf_mode == 'docker_dns'
   tags:
@@ -43,7 +51,7 @@
   retries: 4
   delay: "{{ retry_stagger | random + 3 }}"
   with_items: "{{ docker_repo_key_info.repo_keys }}"
-  when: not (ansible_os_family in ["CoreOS", "Container Linux by CoreOS", "RedHat"] or is_atomic)
+  when: not (ansible_os_family in ["CoreOS", "Container Linux by CoreOS", "RedHat", "Suse"] or is_atomic)
 
 - name: ensure docker-ce repository is enabled
   action: "{{ docker_repo_info.pkg_repo }}"
@@ -51,7 +59,7 @@
     repo: "{{item}}"
     state: present
   with_items: "{{ docker_repo_info.repos }}"
-  when: not (ansible_os_family in ["CoreOS", "Container Linux by CoreOS", "RedHat"] or is_atomic) and (docker_repo_info.repos|length > 0)
+  when: not (ansible_os_family in ["CoreOS", "Container Linux by CoreOS", "RedHat", "Suse"] or is_atomic) and (docker_repo_info.repos|length > 0)
 
 - name: ensure docker-engine repository public key is installed
   action: "{{ dockerproject_repo_key_info.pkg_key }}"
@@ -64,7 +72,7 @@
   retries: 4
   delay: "{{ retry_stagger | random + 3 }}"
   with_items: "{{ dockerproject_repo_key_info.repo_keys }}"
-  when: not (ansible_os_family in ["CoreOS", "Container Linux by CoreOS", "RedHat"] or is_atomic)
+  when: not (ansible_os_family in ["CoreOS", "Container Linux by CoreOS", "RedHat", "Suse"] or is_atomic)
 
 - name: ensure docker-engine repository is enabled
   action: "{{ dockerproject_repo_info.pkg_repo }}"
@@ -72,7 +80,7 @@
     repo: "{{item}}"
     state: present
   with_items: "{{ dockerproject_repo_info.repos }}"
-  when: not (ansible_os_family in ["CoreOS", "Container Linux by CoreOS", "RedHat"] or is_atomic) and (dockerproject_repo_info.repos|length > 0)
+  when: not (ansible_os_family in ["CoreOS", "Container Linux by CoreOS", "RedHat", "Suse"] or is_atomic) and (dockerproject_repo_info.repos|length > 0)
 
 - name: Configure docker repository on RedHat/CentOS
   template:
@@ -110,6 +118,12 @@
   notify: restart docker
   when: not (ansible_os_family in ["CoreOS", "Container Linux by CoreOS"] or is_atomic) and (docker_package_info.pkgs|length > 0)
 
+- name: ensure service is started if docker packages are already present
+  service:
+    name: docker
+    state: started
+  when: docker_task_result is not changed
+
 - name: flush handlers so we can wait for docker to come up
   meta: flush_handlers
 
diff --git a/roles/docker/templates/docker.service.j2 b/roles/docker/templates/docker.service.j2
index d8efe202546dac187a44d4d7ba575fef27d66a98..8dc82bbb205acfc3a80bbd132f71290dccb7dbf6 100644
--- a/roles/docker/templates/docker.service.j2
+++ b/roles/docker/templates/docker.service.j2
@@ -7,6 +7,9 @@ Wants=docker-storage-setup.service
 {% elif ansible_os_family == "Debian" %}
 After=network.target docker.socket
 Wants=docker.socket
+{% elif ansible_os_family == "Suse" %}
+After=network.target containerd.socket containerd.service
+Requires=containerd.socket containerd.service
 {% endif %}
 
 [Service]
@@ -19,6 +22,9 @@ ExecReload=/bin/kill -s HUP $MAINPID
 Delegate=yes
 KillMode=process
 ExecStart={{ docker_bin_dir }}/docker{% if installed_docker_version.stdout|version_compare('17.03', '<') %} daemon{% else %}d{% endif %} \
+{% if ansible_os_family == "Suse" %}
+          --containerd /run/containerd/containerd.sock --add-runtime oci=/usr/bin/docker-runc \
+{% endif %}
           $DOCKER_OPTS \
           $DOCKER_STORAGE_OPTIONS \
           $DOCKER_NETWORK_OPTIONS \
diff --git a/roles/docker/vars/suse.yml b/roles/docker/vars/suse.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d89a50a7f4bdfc98c7223a7b843fa6b61ef23cda
--- /dev/null
+++ b/roles/docker/vars/suse.yml
@@ -0,0 +1,15 @@
+---
+docker_kernel_min_version: '0'
+
+docker_package_info:
+  pkg_mgr: zypper
+  pkgs:
+    - name: docker
+
+docker_repo_key_info:
+  pkg_key: ''
+  repo_keys: []
+
+docker_repo_info:
+  pkg_repo: ''
+  repos: []
diff --git a/roles/etcd/tasks/upd_ca_trust.yml b/roles/etcd/tasks/upd_ca_trust.yml
index dd36554fb95a3fcc643cf51823f96919b2373a2e..0ff3638601b73e6fb56b07c5985ff4a85efb0dc6 100644
--- a/roles/etcd/tasks/upd_ca_trust.yml
+++ b/roles/etcd/tasks/upd_ca_trust.yml
@@ -8,6 +8,8 @@
       /etc/pki/ca-trust/source/anchors/etcd-ca.crt
       {%- elif ansible_os_family in ["CoreOS", "Container Linux by CoreOS"] -%}
       /etc/ssl/certs/etcd-ca.pem
+      {%- elif ansible_os_family == "Suse" -%}
+      /etc/pki/trust/anchors/etcd-ca.pem
       {%- endif %}
   tags:
     - facts
@@ -19,9 +21,9 @@
     remote_src: true
   register: etcd_ca_cert
 
-- name: Gen_certs | update ca-certificates (Debian/Ubuntu/Container Linux by CoreOS)
+- name: Gen_certs | update ca-certificates (Debian/Ubuntu/SUSE/Container Linux by CoreOS)
   command: update-ca-certificates
-  when: etcd_ca_cert.changed and ansible_os_family in ["Debian", "CoreOS", "Container Linux by CoreOS"]
+  when: etcd_ca_cert.changed and ansible_os_family in ["Debian", "CoreOS", "Container Linux by CoreOS", "Suse"]
 
 - name: Gen_certs | update ca-certificates (RedHat)
   command: update-ca-trust extract
diff --git a/roles/kubernetes/preinstall/defaults/main.yml b/roles/kubernetes/preinstall/defaults/main.yml
index 149cbb42a1d9c6636f492572ce8543ca766ab25a..3bf847fb9f5e2c36c65731eacf1ac8c428f0464e 100644
--- a/roles/kubernetes/preinstall/defaults/main.yml
+++ b/roles/kubernetes/preinstall/defaults/main.yml
@@ -8,7 +8,7 @@ epel_enabled: false
 
 common_required_pkgs:
   - python-httplib2
-  - openssl
+  - "{{ (ansible_distribution == 'openSUSE Tumbleweed') | ternary('openssl-1_1_0', 'openssl') }}"
   - curl
   - rsync
   - bash-completion
diff --git a/roles/kubernetes/preinstall/tasks/main.yml b/roles/kubernetes/preinstall/tasks/main.yml
index 8b9cbbe1a038bd3743abae1077f8ea3279a101c4..8df0ff9ee4b2737db706dd675e56973b7c6825dc 100644
--- a/roles/kubernetes/preinstall/tasks/main.yml
+++ b/roles/kubernetes/preinstall/tasks/main.yml
@@ -158,6 +158,15 @@
     - not is_atomic
   tags: bootstrap-os
 
+- name: Update package management cache (zypper) - SUSE
+  shell: zypper -n --gpg-auto-import-keys ref
+  register: make_cache_output
+  until: make_cache_output|succeeded
+  retries: 4
+  delay: "{{ retry_stagger | random + 3 }}"
+  when:
+    - ansible_pkg_mgr == 'zypper'
+  tags: bootstrap-os
 
 - name: Update package management cache (APT)
   apt:
diff --git a/roles/kubernetes/preinstall/tasks/verify-settings.yml b/roles/kubernetes/preinstall/tasks/verify-settings.yml
index 8f0a2e854735fac7b8220e552e1f6532a74c4fc7..5f647101d8b70a15379c6b6008c5fe6615df14a3 100644
--- a/roles/kubernetes/preinstall/tasks/verify-settings.yml
+++ b/roles/kubernetes/preinstall/tasks/verify-settings.yml
@@ -12,7 +12,7 @@
 
 - name: Stop if unknown OS
   assert:
-    that: ansible_distribution in ['RedHat', 'CentOS', 'Fedora', 'Ubuntu', 'Debian', 'CoreOS', 'Container Linux by CoreOS']
+    that: ansible_distribution in ['RedHat', 'CentOS', 'Fedora', 'Ubuntu', 'Debian', 'CoreOS', 'Container Linux by CoreOS', 'openSUSE Leap', 'openSUSE Tumbleweed']
   ignore_errors: "{{ ignore_assert_errors }}"
 
 - name: Stop if unknown network plugin
@@ -94,4 +94,4 @@
   assert:
     that: ansible_kernel.split('-')[0]|version_compare('4.8', '>=')
   when: kube_network_plugin == 'cilium'
-  ignore_errors: "{{ ignore_assert_errors }}"
\ No newline at end of file
+  ignore_errors: "{{ ignore_assert_errors }}"
diff --git a/roles/kubernetes/preinstall/vars/suse.yml b/roles/kubernetes/preinstall/vars/suse.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3f4f9aee9a1c5c55c3c372724785ea757844452d
--- /dev/null
+++ b/roles/kubernetes/preinstall/vars/suse.yml
@@ -0,0 +1,4 @@
+---
+required_pkgs:
+  - device-mapper
+  - ebtables
diff --git a/roles/kubernetes/secrets/tasks/upd_ca_trust.yml b/roles/kubernetes/secrets/tasks/upd_ca_trust.yml
index eec44987f5109ce6d7de88deb7f0748fe4298ffa..cdd5f48fa0785ad4c3142b85ee48e6a110bb6ca7 100644
--- a/roles/kubernetes/secrets/tasks/upd_ca_trust.yml
+++ b/roles/kubernetes/secrets/tasks/upd_ca_trust.yml
@@ -8,6 +8,8 @@
       /etc/pki/ca-trust/source/anchors/kube-ca.crt
       {%- elif ansible_os_family in ["CoreOS", "Container Linux by CoreOS"] -%}
       /etc/ssl/certs/kube-ca.pem
+      {%- elif ansible_os_family == "Suse" -%}
+      /etc/pki/trust/anchors/kube-ca.pem
       {%- endif %}
   tags:
     - facts
@@ -19,9 +21,9 @@
     remote_src: true
   register: kube_ca_cert
 
-- name: Gen_certs | update ca-certificates (Debian/Ubuntu/Container Linux by CoreOS)
+- name: Gen_certs | update ca-certificates (Debian/Ubuntu/SUSE/Container Linux by CoreOS)
   command: update-ca-certificates
-  when: kube_ca_cert.changed and ansible_os_family in ["Debian", "CoreOS", "Container Linux by CoreOS"]
+  when: kube_ca_cert.changed and ansible_os_family in ["Debian", "CoreOS", "Container Linux by CoreOS", "Suse"]
 
 - name: Gen_certs | update ca-certificates (RedHat)
   command: update-ca-trust extract
diff --git a/roles/rkt/tasks/install.yml b/roles/rkt/tasks/install.yml
index 599f9e50e2b1d5e3c3cd22c7b73b29560a2c924b..f881a81fe40c03e34a911c5b22e9cf9c5db419fa 100644
--- a/roles/rkt/tasks/install.yml
+++ b/roles/rkt/tasks/install.yml
@@ -34,3 +34,13 @@
   retries: 4
   delay: "{{ retry_stagger | random + 3 }}"
   when: ansible_os_family == "RedHat"
+
+- name: install rkt pkg on openSUSE
+  zypper:
+    name: "{{ rkt_download_url }}/{{ rkt_pkg_name }}"
+    state: present
+  register: rkt_task_result
+  until: rkt_task_result|succeeded
+  retries: 4
+  delay: "{{ retry_stagger | random + 3 }}"
+  when: ansible_os_family == "Suse"
diff --git a/roles/rkt/vars/suse.yml b/roles/rkt/vars/suse.yml
new file mode 100644
index 0000000000000000000000000000000000000000..13149e8fbfeac5d9e4f793588d0fcb0f56b7d72a
--- /dev/null
+++ b/roles/rkt/vars/suse.yml
@@ -0,0 +1,2 @@
+---
+rkt_pkg_name: "rkt-{{ rkt_pkg_version }}.x86_64.rpm"
diff --git a/tests/files/gce_opensuse-canal.yml b/tests/files/gce_opensuse-canal.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9eae57e2e463a028a692875ca0531ece7d6e6275
--- /dev/null
+++ b/tests/files/gce_opensuse-canal.yml
@@ -0,0 +1,12 @@
+# Instance settings
+cloud_image_family: opensuse-leap
+cloud_region: us-central1-c
+mode: default
+
+# Deployment settings
+bootstrap_os: opensuse
+kube_network_plugin: canal
+kubeadm_enabled: true
+deploy_netchecker: true
+kubedns_min_replicas: 1
+cloud_provider: gce