diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7cb1d798cb95706f386d11e3c5f0c3b6517ff298..7925b875e5481e62c9f88e817691118697851b8e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -132,7 +132,7 @@ setup-openappstack:
     - cp clusters/${CI_COMMIT_REF_SLUG}/inventory.yml ansible/
     - cp clusters/${CI_COMMIT_REF_SLUG}/group_vars/all/settings.yml ansible/group_vars/all/
     # Set up cluster
-    - python3 -m openappstack $HOSTNAME install
+    - python3 -m openappstack $HOSTNAME install --install-kubernetes
     # Show versions of installed apps/binaries
     - cd ansible
     - ansible master -m shell -a 'oas-version-info.sh 2>&1'
diff --git a/ansible/install-kubernetes.yml b/ansible/install-kubernetes.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ade2008ed505c8255ec436ccc99f77ba6931b4a0
--- /dev/null
+++ b/ansible/install-kubernetes.yml
@@ -0,0 +1,36 @@
+---
+
+- name: Run compatibility checks
+  hosts: all
+  gather_facts: false
+  pre_tasks:
+    - import_role:
+        name: compatibility-checks
+
+- name: Pre-configure hosts
+  hosts: all
+  # We use `mitogen_linear` as default strategy. However,
+  # mitogen fails installing the `openshift` python module as requirement
+  # for the `k8s` ansible resource, and using `k8s` in the same context.
+  # That's why we use the standard `linear` ansible strategy for this role.
+  # See https://open.greenhost.net/openappstack/openappstack/issues/102
+  strategy: linear
+  roles:
+    - role: pre-configure
+      tags: ['pre-configure']
+
+- name: Install Kubernetes
+  hosts: master
+  tasks:
+    - name: Configure VPS to run Kubernetes securely and install it
+      block:
+        - import_role:
+            name: configure
+          tags: ['configure']
+        - import_role:
+            name: setup-kubernetes
+          tags: ['setup-kubernetes']
+      always:
+        - import_role:
+            name: finalize
+          tags: ['finalize']
diff --git a/ansible/install-openappstack.yml b/ansible/install-openappstack.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fb97648a9ab71aa70e8c3bfca9f02a8107998521
--- /dev/null
+++ b/ansible/install-openappstack.yml
@@ -0,0 +1,25 @@
+---
+
+- name: Run compatibility checks
+  hosts: all
+  gather_facts: false
+  pre_tasks:
+    - import_role:
+        name: compatibility-checks
+
+- name: Install OpenAppStack
+  hosts: master
+  tasks:
+    - name: Everything for after Kubernetes is installed
+      block:
+        - import_role:
+            name: apps
+          tags: ['apps']
+        - import_role:
+            name: local-flux
+          tags: ['flux']
+          when: flux.local_flux
+      always:
+        - import_role:
+            name: finalize
+          tags: ['finalize']
diff --git a/ansible/roles/compatibility-checks/tasks/main.yml b/ansible/roles/compatibility-checks/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..480d99345668a840f467f9b922bae5b7a83c6d2e
--- /dev/null
+++ b/ansible/roles/compatibility-checks/tasks/main.yml
@@ -0,0 +1,36 @@
+---
+- name: Require minimal ansible version
+  assert:
+    that:
+      - "ansible_version.full is version_compare('2.7', '>=')"
+    msg: >
+      "Please use Ansible 2.7 or higher to bootstrap your OAS cluster."
+
+- name: Release name must start with lower case
+  assert:
+    that: "'{{ release_name }}' is match('^[a-z]')"
+    msg: >
+      "Release name ({{ release_name }}) should start with lower case."
+
+- name: cluster_dir variable is needed
+  assert:
+    that: "cluster_dir is defined"
+    msg: >
+      "Please define the variable `cluster_dir`."
+
+- name: Check if all variables from settings.yml.example are set in your settings.yml file
+  assert:
+    that: "{{ item }} is defined"
+    msg: >
+      "Please define the variable `{{ item }}`."
+  with_items:
+    - ip_address
+    - domain
+    - admin_email
+    - release_name
+    - acme_staging
+    - prometheus_enable_ingress
+    - backup
+    - flux
+    - helm_operator
+    - enabled_applications
diff --git a/docs/installation_instructions.rst b/docs/installation_instructions.rst
index 0176a4f9f31d6de2ab2028699d29b86256e7f274..0429b06569773bc090a2cdece1f9fd3c1372e0e6 100644
--- a/docs/installation_instructions.rst
+++ b/docs/installation_instructions.rst
@@ -412,7 +412,7 @@ To start the installation process, run:
 
 ::
 
-    $ python -m openappstack my-cluster install
+    $ python -m openappstack my-cluster install --install-kubernetes
 
 This will take between 5 and 20 minutes. It generates secrets that will
 be added to the ``clusters/my-cluster/secrets`` directory. If you ever
@@ -437,6 +437,11 @@ to settle, and then continue to the next step (validating your setup).
    have the ``clusters/my-cluster`` directory and it contains the same
    ``secrets`` directory before you run the installation command.
 
+.. Note::
+   The ``--install-kubernetes`` argument installs or updates your VPS's K3s
+   installation. If you only want to re-run the part that installs OpenAppStack
+   on your Kubernetes clusters, leave it out.
+
 Step 5: Validate setup
 ~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/openappstack/__main__.py b/openappstack/__main__.py
index d3b99a28df578b2c02c261437be970c5b509d809..22cbc5a604419502c6ff4bcf594bb8e012fdf61a 100755
--- a/openappstack/__main__.py
+++ b/openappstack/__main__.py
@@ -172,7 +172,14 @@ def main():  # pylint: disable=too-many-statements,too-many-branches,too-many-lo
         metavar=['PARAM[=VALUE]'],
         action='append',
         nargs=1,
-        help='forward ansible parameters to the ansible-playbook call')
+        help='forward ansible parameters to the ansible-playbook call. If '
+          '--install-kubernetes is supplied, parameters are passed to both '
+          'ansible playbooks')
+
+    install_parser.add_argument(
+        '--install-kubernetes',
+        action='store_true',
+        help="Installs k3s on your VPS before installing OpenAppStack")
 
     test_parser = subparsers.add_parser(
         'test',
@@ -341,9 +348,16 @@ def install(clus, args):
     :param argparse.Namespace args: Command line arguments
     """
     clus.load_data()
+
+    if args.install_kubernetes:
+        ansible.run_ansible(
+            clus,
+            os.path.join(ansible.ANSIBLE_PATH, 'install-kubernetes.yml'),
+            args.ansible_param)
+
     ansible.run_ansible(
         clus,
-        os.path.join(ansible.ANSIBLE_PATH, 'bootstrap.yml'),
+        os.path.join(ansible.ANSIBLE_PATH, 'install-openappstack.yml'),
         args.ansible_param)