diff --git a/.github/actions/e2e-prepare-containers/action.yml b/.github/actions/e2e-prepare-containers/action.yml
new file mode 100644
index 0000000000000000000000000000000000000000..97234ab5adc4965584243222b2a461a6ec5a0ed5
--- /dev/null
+++ b/.github/actions/e2e-prepare-containers/action.yml
@@ -0,0 +1,84 @@
+name: Prepare Docker containers
+description: Prepare Docker containers for E2E tests.
+inputs:
+  username:
+    description: DockerHub username
+    required: true
+  password:
+    description: DockerHub password/token
+    required: true
+  maildev:
+    description: Maildev
+    required: true
+    default: 'false'
+  openldap:
+    description: Maildev
+    required: true
+    default: 'false'
+  postgres:
+    description: Maildev
+    required: true
+    default: 'false'
+  mysql:
+    description: Maildev
+    required: true
+    default: 'false'
+  mongo:
+    description: Maildev
+    required: true
+    default: 'false'
+
+
+runs:
+  using: "composite"
+  steps:
+    - name: Authenticate to prevent API rate limit
+      uses: docker/login-action@v3
+      with:
+        username: ${{ inputs.username }}
+        password: ${{ inputs.password }}
+
+    - name: Start Containers
+      run: |
+        if ${{ inputs.maildev }}; then
+          echo "Starting maildev container..." &&
+          docker run -d -p 1080:1080 -p 1025:1025 maildev/maildev:2.0.5 &&
+          while ! nc -z localhost 1080; do sleep 1; done &&
+          while ! nc -z localhost 1025; do sleep 1; done &&
+          echo "Maildev is up and running!"
+        fi
+
+        if ${{ inputs.openldap }}; then
+          echo "Starting openldap container..." &&
+          docker run -d -p 389:389 \
+            --env LDAP_ADMIN_PASSWORD=adminpass \
+            --env LDAP_USERS=user01@example.org,user02@example.org \
+            --env LDAP_PASSWORDS=123456,123465 \
+            --env LDAP_ROOT=dc=example,dc=org \
+            --env LDAP_PORT_NUMBER=389 \
+            bitnami/openldap:2.6.4 &&
+          while ! nc -z localhost 389; do sleep 1; done &&
+          echo "Openldap is up and running!"
+        fi
+
+        if ${{ inputs.postgres }}; then
+          echo "Starting postgres container..." &&
+          docker run -d -p 5404:5432 metabase/qa-databases:postgres-sample-12 &&
+          while ! nc -z localhost 5404; do sleep 1; done &&
+          echo "Postgres is up and running!"
+        fi
+
+        if ${{ inputs.mysql }}; then
+          echo "Starting mysql container..." &&
+          docker run -d -p 3304:3306 metabase/qa-databases:mysql-sample-8 &&
+          while ! nc -z localhost 3304; do sleep 1; done &&
+          echo "MySQL is up and running!"
+        fi
+
+        if ${{ inputs.mongo }}; then
+          echo "Starting mongo container..." &&
+          docker run -d -p 27004:27017 metabase/qa-databases:mongo-sample-4.4 &&
+          while ! nc -z localhost 27004; do sleep 1; done &&
+          echo "Mongo is up and running!"
+        fi
+      shell: bash
diff --git a/.github/workflows/e2e-stress-test-flake-fix.yml b/.github/workflows/e2e-stress-test-flake-fix.yml
index a4a42e2709dc289aeb171ac632abca853ab66b47..8352bac01aac3e888bd4fbe9ea2ad94692ceb4f3 100644
--- a/.github/workflows/e2e-stress-test-flake-fix.yml
+++ b/.github/workflows/e2e-stress-test-flake-fix.yml
@@ -29,51 +29,20 @@ jobs:
       MB_SNOWPLOW_AVAILABLE: true
       MB_SNOWPLOW_URL: "http://localhost:9090" # Snowplow micro
       TZ: US/Pacific # to make node match the instance tz
-    services:
-      maildev:
-        image: maildev/maildev:2.0.5
-        ports:
-          - "1080:1080"
-          - "1025:1025"
-        credentials:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
-      openldap:
-        image: bitnami/openldap:2.6.4
-        ports:
-          - 389:389
-        env:
-          LDAP_ADMIN_PASSWORD: adminpass
-          LDAP_USERS: user01@example.org,user02@example.org
-          LDAP_PASSWORDS: 123456,123465
-          LDAP_ROOT: dc=example,dc=org
-          LDAP_PORT_NUMBER: 389
-        credentials:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
-      postgres-sample:
-        image: metabase/qa-databases:postgres-sample-12
-        ports:
-          - "5404:5432"
-        credentials:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
-      mongo-sample:
-        image: metabase/qa-databases:mongo-sample-4.4
-        ports:
-          - 27004:27017
-        credentials:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
-      mysql-sample:
-        image: metabase/qa-databases:mysql-sample-8
-        ports:
-          - 3304:3306
-        credentials:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
     steps:
       - uses: actions/checkout@v3
+
+      - name: Prepare Docker containers
+        uses: ./.github/actions/e2e-prepare-containers
+        with:
+          username: ${{ secrets.DOCKERHUB_USERNAME }}
+          password: ${{ secrets.DOCKERHUB_TOKEN }}
+          maildev: true
+          openldap: true
+          postgres: true
+          mysql: true
+          mongo: true
+
       - name: Download Metabase uberjar from a previously stored artifact
         run: |
           curl -L \
diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml
index 2431a6974edd83932ed42a0721d6cc92c954b64c..f844fdf8653eb453d28f732375b879547b639ea1 100644
--- a/.github/workflows/e2e-tests.yml
+++ b/.github/workflows/e2e-tests.yml
@@ -174,44 +174,21 @@ jobs:
     strategy:
       fail-fast: false
       matrix: ${{ fromJSON(needs.e2e-matrix-builder.outputs.matrix) }}
-    services:
-      maildev:
-        image: maildev/maildev:2.0.5
-        ports:
-          - "1080:1080"
-          - "1025:1025"
-        credentials:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
-      openldap:
-        image: bitnami/openldap:2.6.4
-        ports:
-          - 389:389
-        env:
-          LDAP_ADMIN_PASSWORD: adminpass
-          LDAP_USERS: user01@example.org,user02@example.org
-          LDAP_PASSWORDS: 123456,123465
-          LDAP_ROOT: dc=example,dc=org
-          LDAP_PORT_NUMBER: 389
-        credentials:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
-      postgres-sample:
-        image: metabase/qa-databases:postgres-sample-12
-        ports:
-          - "5404:5432"
-        credentials:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
-      mysql-sample:
-        image: metabase/qa-databases:mysql-sample-8
-        ports:
-          - 3304:3306
-        credentials:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
+
     steps:
       - uses: actions/checkout@v3
+
+      - name: Prepare Docker containers
+        uses: ./.github/actions/e2e-prepare-containers
+        with:
+          username: ${{ secrets.DOCKERHUB_USERNAME }}
+          password: ${{ secrets.DOCKERHUB_TOKEN }}
+          maildev: true
+          openldap: ${{ startsWith(matrix.name, 'admin') }}
+          postgres: ${{ matrix.name != 'mongo'}}
+          mysql: ${{ matrix.name != 'mongo'}}
+          mongo: ${{ matrix.name == 'mongo'}}
+
       - name: Download Metabase uberjar from a previously stored artifact
         if: needs.download_uberjar.result == 'success'
         run: |
@@ -245,7 +222,6 @@ jobs:
           jar xf target/uberjar/metabase.jar version.properties
           mv version.properties resources/
 
-
       - name: Prepare front-end environment
         uses: ./.github/actions/prepare-frontend
       - name: Prepare JDK ${{ matrix.java-version }}
@@ -291,14 +267,6 @@ jobs:
         env:
           TERM: xterm
 
-      # Mongo
-      - name: Start Mongo server
-        if: matrix.name == 'mongo'
-        run: docker run -d -p 27004:27017 --name e2e-mongo metabase/qa-databases:mongo-sample-4.4
-      - name: Wait until the port 27004 is ready
-        if: matrix.name == 'mongo'
-        run: while ! nc -z localhost 27004; do sleep 1; done
-        timeout-minutes: 3
       - name: Run E2E tests that depend on Mongo
         if: matrix.name == 'mongo'
         run: |
@@ -385,18 +353,16 @@ jobs:
     name: percy-visual-regression-tests
     env:
       MB_EDITION: ${{ matrix.edition }}
-    services:
-      maildev:
-        image: maildev/maildev:2.0.5
-        ports:
-          - "1080:1080"
-          - "1025:1025"
-        credentials:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
     steps:
       - uses: actions/checkout@v3
 
+      - name: Prepare Docker containers
+        uses: ./.github/actions/e2e-prepare-containers
+        with:
+          username: ${{ secrets.DOCKERHUB_USERNAME }}
+          password: ${{ secrets.DOCKERHUB_TOKEN }}
+          maildev: true
+
       - name: Download Metabase uberjar from a previously stored artifact
         if: needs.download_uberjar.result == 'success'
         run: |
diff --git a/e2e/snapshot-creators/qa-db.cy.snap.js b/e2e/snapshot-creators/qa-db.cy.snap.js
index 5c8a2ca85ad8de2e083c5cbef864cea2f96a2963..bdb6c03a8b7ac69d197800c0904b2b925a478837 100644
--- a/e2e/snapshot-creators/qa-db.cy.snap.js
+++ b/e2e/snapshot-creators/qa-db.cy.snap.js
@@ -13,36 +13,36 @@ describe("qa databases snapshots", { tags: "@external" }, () => {
   });
 
   it("creates snapshots for supported qa databases", () => {
-    addPostgresDatabase();
-    snapshot("postgres-12");
-    deleteDatabase("postgresID");
-
-    restoreAndAuthenticate();
+    if (Cypress.env("QA_DB_MONGO") === true) {
+      addMongoDatabase();
+      snapshot("mongo-4");
+      deleteDatabase("mongoID");
 
-    setupWritableDB("postgres");
-    addPostgresDatabase("Writable Postgres12", true);
-    snapshot("postgres-writable");
-    deleteDatabase("postgresID");
+      restoreAndAuthenticate();
+    } else {
+      addPostgresDatabase();
+      snapshot("postgres-12");
+      deleteDatabase("postgresID");
 
-    restoreAndAuthenticate();
+      restoreAndAuthenticate();
 
-    addMySQLDatabase();
-    snapshot("mysql-8");
-    deleteDatabase("mysqlID");
+      setupWritableDB("postgres");
+      addPostgresDatabase("Writable Postgres12", true);
+      snapshot("postgres-writable");
+      deleteDatabase("postgresID");
 
-    restoreAndAuthenticate();
+      restoreAndAuthenticate();
 
-    setupWritableDB("mysql");
-    addMySQLDatabase("Writable MySQL8", true);
-    snapshot("mysql-writable");
-    deleteDatabase("mysqlID");
+      addMySQLDatabase();
+      snapshot("mysql-8");
+      deleteDatabase("mysqlID");
 
-    restoreAndAuthenticate();
+      restoreAndAuthenticate();
 
-    if (Cypress.env("QA_DB_MONGO") === true) {
-      addMongoDatabase();
-      snapshot("mongo-4");
-      deleteDatabase("mongoID");
+      setupWritableDB("mysql");
+      addMySQLDatabase("Writable MySQL8", true);
+      snapshot("mysql-writable");
+      deleteDatabase("mysqlID");
 
       restoreAndAuthenticate();
     }