diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3a2b566285667768b7a368ad78df0bb6c1e8a724..ae5a65bec07dac635fd623c058b8b656db458b55 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -32,12 +32,13 @@ variables:
   RECOVER_CONTROL_PLANE_TEST: "false"
   RECOVER_CONTROL_PLANE_TEST_GROUPS: "etcd[2:],kube_control_plane[1:]"
   TERRAFORM_VERSION: 1.0.8
+  ANSIBLE_MAJOR_VERSION: "2.10"
 
 before_script:
   - ./tests/scripts/rebase.sh
   - update-alternatives --install /usr/bin/python python /usr/bin/python3 1
-  - python -m pip uninstall -y ansible
-  - python -m pip install -r tests/requirements.txt
+  - python -m pip uninstall -y ansible ansible-base ansible-core
+  - python -m pip install -r tests/requirements-${ANSIBLE_MAJOR_VERSION}.txt
   - mkdir -p /.ssh
 
 .job: &job
diff --git a/.gitlab-ci/packet.yml b/.gitlab-ci/packet.yml
index 6e72a4cd811351af523edcfcbbff9acd4e7820a1..90a3ad3978f9eadd11734d373f13ada0a0b02492 100644
--- a/.gitlab-ci/packet.yml
+++ b/.gitlab-ci/packet.yml
@@ -23,15 +23,34 @@
   extends: .packet
 
 packet_ubuntu18-calico-aio:
-  stage: deploy-part1
+  stage: deploy-part2
   extends: .packet_pr
   when: on_success
 
-# Future AIO job
+# The ubuntu20-calico-aio jobs are meant as early stages to prevent running the full CI if something is horribly broken
 packet_ubuntu20-calico-aio:
   stage: deploy-part1
   extends: .packet_pr
   when: on_success
+  variables:
+    RESET_CHECK: "true"
+
+# Exericse ansible variants
+packet_ubuntu20-calico-aio-ansible-2_9:
+  stage: deploy-part1
+  extends: .packet_pr
+  when: on_success
+  variables:
+    ANSIBLE_MAJOR_VERSION: "2.9"
+    RESET_CHECK: "true"
+
+packet_ubuntu20-calico-aio-ansible-2_11:
+  stage: deploy-part1
+  extends: .packet_pr
+  when: on_success
+  variables:
+    ANSIBLE_MAJOR_VERSION: "2.11"
+    RESET_CHECK: "true"
 
 # ### PR JOBS PART2
 
diff --git a/ansible_version.yml b/ansible_version.yml
index b7fff003984aa884fda704ba278a594d4b957262..5226fd90fee79329d4bc7ee46b555c3952940be1 100644
--- a/ansible_version.yml
+++ b/ansible_version.yml
@@ -5,7 +5,7 @@
   vars:
     minimal_ansible_version: 2.9.0
     minimal_ansible_version_2_10: 2.10.11
-    maximal_ansible_version: 2.11.0
+    maximal_ansible_version: 2.12.0
     ansible_connection: local
   tags: always
   tasks:
diff --git a/requirements-2.10.txt b/requirements-2.10.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5fcbf80485805e68e64e15b58dd3913f4b0261a6
--- /dev/null
+++ b/requirements-2.10.txt
@@ -0,0 +1,10 @@
+ansible==3.4.0
+ansible-base==2.10.15
+cryptography==2.8
+jinja2==2.11.3
+netaddr==0.7.19
+pbr==5.4.4
+jmespath==0.9.5
+ruamel.yaml==0.16.10
+ruamel.yaml.clib==0.2.4
+MarkupSafe==1.1.1
diff --git a/requirements-2.11.txt b/requirements-2.11.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5d535be6555b946ef7335a3f26668aad677a24a5
--- /dev/null
+++ b/requirements-2.11.txt
@@ -0,0 +1,10 @@
+ansible==4.8.0
+ansible-core==2.11.6
+cryptography==2.8
+jinja2==2.11.3
+netaddr==0.7.19
+pbr==5.4.4
+jmespath==0.9.5
+ruamel.yaml==0.16.10
+ruamel.yaml.clib==0.2.4
+MarkupSafe==1.1.1
diff --git a/requirements-2.9.txt b/requirements-2.9.txt
new file mode 100644
index 0000000000000000000000000000000000000000..220780881c58a94271836715712902ce1d53f383
--- /dev/null
+++ b/requirements-2.9.txt
@@ -0,0 +1,8 @@
+ansible==2.9.27
+jinja2==2.11.3
+netaddr==0.7.19
+pbr==5.4.4
+jmespath==0.9.5
+ruamel.yaml==0.16.10
+ruamel.yaml.clib==0.2.4
+MarkupSafe==1.1.1
diff --git a/requirements-2.9.yml b/requirements-2.9.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e8034543d7a20fcbe10c8eb2743fc403e10595ca
--- /dev/null
+++ b/requirements-2.9.yml
@@ -0,0 +1,4 @@
+---
+collections:
+- name: community.general
+  version: '<3.0'
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index ab7f293f1d66fa8ce36724c9c6a7d97dedcf9990..0000000000000000000000000000000000000000
--- a/requirements.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-ansible==3.4.0
-ansible-base==2.10.11
-cryptography==2.8
-jinja2==2.11.3
-netaddr==0.7.19
-pbr==5.4.4
-jmespath==0.9.5
-ruamel.yaml==0.16.10
-ruamel.yaml.clib==0.2.4
-MarkupSafe==1.1.1
diff --git a/requirements.txt b/requirements.txt
new file mode 120000
index 0000000000000000000000000000000000000000..5202ea4fbeabf890928d24c00085afb2fa537343
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+requirements-2.10.txt
\ No newline at end of file
diff --git a/tests/files/packet_ubuntu20-calico-aio-ansible-2_11.yml b/tests/files/packet_ubuntu20-calico-aio-ansible-2_11.yml
new file mode 120000
index 0000000000000000000000000000000000000000..10064637f2578eefc7124bf4e74ac56e96e69bf3
--- /dev/null
+++ b/tests/files/packet_ubuntu20-calico-aio-ansible-2_11.yml
@@ -0,0 +1 @@
+packet_ubuntu20-calico-aio.yml
\ No newline at end of file
diff --git a/tests/files/packet_ubuntu20-calico-aio-ansible-2_9.yml b/tests/files/packet_ubuntu20-calico-aio-ansible-2_9.yml
new file mode 120000
index 0000000000000000000000000000000000000000..10064637f2578eefc7124bf4e74ac56e96e69bf3
--- /dev/null
+++ b/tests/files/packet_ubuntu20-calico-aio-ansible-2_9.yml
@@ -0,0 +1 @@
+packet_ubuntu20-calico-aio.yml
\ No newline at end of file
diff --git a/tests/requirements-2.10.txt b/tests/requirements-2.10.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a80c3a868388a939741d3284ba4ecdda259f1415
--- /dev/null
+++ b/tests/requirements-2.10.txt
@@ -0,0 +1,12 @@
+-r ../requirements-2.10.txt
+yamllint==1.19.0
+apache-libcloud==2.2.1
+tox==3.11.1
+dopy==0.3.7
+cryptography==2.8
+ansible-lint==5.0.11
+openshift==0.8.8
+molecule==3.0.6
+molecule-vagrant==0.3
+testinfra==5.2.2
+python-vagrant==0.5.15
diff --git a/tests/requirements-2.11.txt b/tests/requirements-2.11.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8ad98b8b448a7acc9dc742fab0e4b3bc7d9714fd
--- /dev/null
+++ b/tests/requirements-2.11.txt
@@ -0,0 +1,12 @@
+-r ../requirements-2.11.txt
+yamllint==1.19.0
+apache-libcloud==2.2.1
+tox==3.11.1
+dopy==0.3.7
+cryptography==2.8
+ansible-lint==5.0.11
+openshift==0.8.8
+molecule==3.0.6
+molecule-vagrant==0.3
+testinfra==5.2.2
+python-vagrant==0.5.15
diff --git a/tests/requirements-2.9.txt b/tests/requirements-2.9.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2913072490d25195ecc8dc125cfda1c23760c46a
--- /dev/null
+++ b/tests/requirements-2.9.txt
@@ -0,0 +1,12 @@
+-r ../requirements-2.9.txt
+yamllint==1.19.0
+apache-libcloud==2.2.1
+tox==3.11.1
+dopy==0.3.7
+cryptography==2.8
+ansible-lint==5.0.11
+openshift==0.8.8
+molecule==3.0.6
+molecule-vagrant==0.3
+testinfra==5.2.2
+python-vagrant==0.5.15
diff --git a/tests/requirements.txt b/tests/requirements.txt
deleted file mode 100644
index 2524ef93ce6925b2eae31b602918da8cbccf23c4..0000000000000000000000000000000000000000
--- a/tests/requirements.txt
+++ /dev/null
@@ -1,12 +0,0 @@
--r ../requirements.txt
-yamllint==1.19.0
-apache-libcloud==2.2.1
-tox==3.11.1
-dopy==0.3.7
-cryptography==2.8
-ansible-lint==5.0.11
-openshift==0.8.8
-molecule==3.0.6
-molecule-vagrant==0.3
-testinfra==5.2.2
-python-vagrant==0.5.15
diff --git a/tests/requirements.txt b/tests/requirements.txt
new file mode 120000
index 0000000000000000000000000000000000000000..5202ea4fbeabf890928d24c00085afb2fa537343
--- /dev/null
+++ b/tests/requirements.txt
@@ -0,0 +1 @@
+requirements-2.10.txt
\ No newline at end of file
diff --git a/tests/scripts/testcases_prepare.sh b/tests/scripts/testcases_prepare.sh
index d70086a2b1de6630457d931b0528fde355e60b64..bfaf65f186909e15a5bde052e85a4bcb375641d2 100755
--- a/tests/scripts/testcases_prepare.sh
+++ b/tests/scripts/testcases_prepare.sh
@@ -1,9 +1,18 @@
 #!/bin/bash
 set -euxo pipefail
 
-/usr/bin/python -m pip uninstall -y ansible
-/usr/bin/python -m pip install -r tests/requirements.txt
+: ${ANSIBLE_MAJOR_VERSION:=2.10}
+
+/usr/bin/python -m pip uninstall -y ansible ansible-base ansible-core
+/usr/bin/python -m pip install -r tests/requirements-${ANSIBLE_MAJOR_VERSION}.txt
 mkdir -p /.ssh
 mkdir -p cluster-dump
 mkdir -p $HOME/.ssh
 ansible-playbook --version
+
+# in some cases we may need to bring in collections or roles from ansible-galaxy
+# to compensate for missing functionality in older ansible versions
+if [ -f requirements-${ANSIBLE_MAJOR_VERSION}.yml ] ; then
+  ansible-galaxy role install -r requirements-${ANSIBLE_MAJOR_VERSION}.yml
+  ansible-galaxy collection install -r requirements-${ANSIBLE_MAJOR_VERSION}.yml
+fi
diff --git a/tests/scripts/testcases_run.sh b/tests/scripts/testcases_run.sh
index 2461d29c6c6351dc5bd80e5325110b5cc944fff9..a6b595edb2df58fbec5da17ae0e100b197d7c5ad 100755
--- a/tests/scripts/testcases_run.sh
+++ b/tests/scripts/testcases_run.sh
@@ -86,27 +86,26 @@ ansible-playbook --limit "all:!fake_hosts" -e @${CI_TEST_VARS} ${CI_TEST_ADDITIO
 ## Kubernetes conformance tests
 ansible-playbook -i ${ANSIBLE_INVENTORY} -e @${CI_TEST_VARS} ${CI_TEST_ADDITIONAL_VARS} --limit "all:!fake_hosts" tests/testcases/100_check-k8s-conformance.yml $ANSIBLE_LOG_LEVEL
 
-## Idempotency checks 1/5 (repeat deployment)
 if [ "${IDEMPOT_CHECK}" = "true" ]; then
+  ## Idempotency checks 1/5 (repeat deployment)
   ansible-playbook ${ANSIBLE_LOG_LEVEL} -e @${CI_TEST_REGISTRY_MIRROR} ${CI_TEST_ADDITIONAL_VARS} -e @${CI_TEST_VARS} -e local_release_dir=${PWD}/downloads --limit "all:!fake_hosts" cluster.yml
-fi
 
-## Idempotency checks 2/5 (Advanced DNS checks)
-if [ "${IDEMPOT_CHECK}" = "true" ]; then
+  ## Idempotency checks 2/5 (Advanced DNS checks)
   ansible-playbook ${ANSIBLE_LOG_LEVEL} -e @${CI_TEST_VARS} ${CI_TEST_ADDITIONAL_VARS} --limit "all:!fake_hosts" tests/testcases/040_check-network-adv.yml
-fi
 
-## Idempotency checks 3/5 (reset deployment)
-if [ "${IDEMPOT_CHECK}" = "true" -a "${RESET_CHECK}" = "true" ]; then
-  ansible-playbook ${ANSIBLE_LOG_LEVEL} -e @${CI_TEST_REGISTRY_MIRROR}  -e @${CI_TEST_VARS} ${CI_TEST_ADDITIONAL_VARS} -e reset_confirmation=yes --limit "all:!fake_hosts" reset.yml
-fi
+  if [ "${RESET_CHECK}" = "true" ]; then
+    ## Idempotency checks 3/5 (reset deployment)
+    ansible-playbook ${ANSIBLE_LOG_LEVEL} -e @${CI_TEST_REGISTRY_MIRROR}  -e @${CI_TEST_VARS} ${CI_TEST_ADDITIONAL_VARS} -e reset_confirmation=yes --limit "all:!fake_hosts" reset.yml
+
+    ## Idempotency checks 4/5 (redeploy after reset)
+    ansible-playbook ${ANSIBLE_LOG_LEVEL} -e @${CI_TEST_REGISTRY_MIRROR} -e @${CI_TEST_VARS} ${CI_TEST_ADDITIONAL_VARS} -e local_release_dir=${PWD}/downloads --limit "all:!fake_hosts" cluster.yml
 
-## Idempotency checks 4/5 (redeploy after reset)
-if [ "${IDEMPOT_CHECK}" = "true" -a "${RESET_CHECK}" = "true" ]; then
-  ansible-playbook ${ANSIBLE_LOG_LEVEL} -e @${CI_TEST_REGISTRY_MIRROR} -e @${CI_TEST_VARS} ${CI_TEST_ADDITIONAL_VARS} -e local_release_dir=${PWD}/downloads --limit "all:!fake_hosts" cluster.yml
+    ## Idempotency checks 5/5 (Advanced DNS checks)
+    ansible-playbook ${ANSIBLE_LOG_LEVEL} -e @${CI_TEST_REGISTRY_MIRROR} -e @${CI_TEST_VARS} ${CI_TEST_ADDITIONAL_VARS} --limit "all:!fake_hosts" tests/testcases/040_check-network-adv.yml
+  fi
 fi
 
-## Idempotency checks 5/5 (Advanced DNS checks)
-if [ "${IDEMPOT_CHECK}" = "true" -a "${RESET_CHECK}" = "true" ]; then
-  ansible-playbook ${ANSIBLE_LOG_LEVEL} -e @${CI_TEST_REGISTRY_MIRROR} -e @${CI_TEST_VARS} ${CI_TEST_ADDITIONAL_VARS} --limit "all:!fake_hosts" tests/testcases/040_check-network-adv.yml
+# Clean up at the end, this is to allow stage1 tests to include cleanup test
+if [ "${RESET_CHECK}" = "true" ]; then
+  ansible-playbook ${ANSIBLE_LOG_LEVEL} -e @${CI_TEST_REGISTRY_MIRROR}  -e @${CI_TEST_VARS} ${CI_TEST_ADDITIONAL_VARS} -e reset_confirmation=yes --limit "all:!fake_hosts" reset.yml
 fi