diff --git a/.github/workflows/10-alpine3.10.yml b/.github/workflows/10-alpine3.10.yml
deleted file mode 100644
index 1e27cc11f..000000000
--- a/.github/workflows/10-alpine3.10.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 10 on alpine3.10
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/alpine3.10/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/alpine3.10/Dockerfile
-
-jobs:
-  build:
-    name: 10 on alpine3.10
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 10 alpine3.10
diff --git a/.github/workflows/10-alpine3.11.yml b/.github/workflows/10-alpine3.11.yml
deleted file mode 100644
index e7f459afe..000000000
--- a/.github/workflows/10-alpine3.11.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 10 on alpine3.11
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/alpine3.11/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/alpine3.11/Dockerfile
-
-jobs:
-  build:
-    name: 10 on alpine3.11
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 10 alpine3.11
diff --git a/.github/workflows/10-alpine3.9.yml b/.github/workflows/10-alpine3.9.yml
deleted file mode 100644
index cfc60915a..000000000
--- a/.github/workflows/10-alpine3.9.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 10 on alpine3.9
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/alpine3.9/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/alpine3.9/Dockerfile
-
-jobs:
-  build:
-    name: 10 on alpine3.9
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 10 alpine3.9
diff --git a/.github/workflows/10-buster-slim.yml b/.github/workflows/10-buster-slim.yml
deleted file mode 100644
index 7d1b53e9a..000000000
--- a/.github/workflows/10-buster-slim.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 10 on buster-slim
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/buster-slim/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/buster-slim/Dockerfile
-
-jobs:
-  build:
-    name: 10 on buster-slim
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 10 buster-slim
diff --git a/.github/workflows/10-buster.yml b/.github/workflows/10-buster.yml
deleted file mode 100644
index 648f5f7b9..000000000
--- a/.github/workflows/10-buster.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 10 on buster
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/buster/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/buster/Dockerfile
-
-jobs:
-  build:
-    name: 10 on buster
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 10 buster
diff --git a/.github/workflows/10-stretch-slim.yml b/.github/workflows/10-stretch-slim.yml
deleted file mode 100644
index 846ec74de..000000000
--- a/.github/workflows/10-stretch-slim.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 10 on stretch-slim
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/stretch-slim/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/stretch-slim/Dockerfile
-
-jobs:
-  build:
-    name: 10 on stretch-slim
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 10 stretch-slim
diff --git a/.github/workflows/10-stretch.yml b/.github/workflows/10-stretch.yml
deleted file mode 100644
index 7a9dc1b22..000000000
--- a/.github/workflows/10-stretch.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 10 on stretch
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/stretch/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 10/stretch/Dockerfile
-
-jobs:
-  build:
-    name: 10 on stretch
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 10 stretch
diff --git a/.github/workflows/12-alpine3.10.yml b/.github/workflows/12-alpine3.10.yml
deleted file mode 100644
index e81565fe7..000000000
--- a/.github/workflows/12-alpine3.10.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 12 on alpine3.10
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/alpine3.10/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/alpine3.10/Dockerfile
-
-jobs:
-  build:
-    name: 12 on alpine3.10
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 12 alpine3.10
diff --git a/.github/workflows/12-alpine3.11.yml b/.github/workflows/12-alpine3.11.yml
deleted file mode 100644
index 531a09680..000000000
--- a/.github/workflows/12-alpine3.11.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 12 on alpine3.11
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/alpine3.11/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/alpine3.11/Dockerfile
-
-jobs:
-  build:
-    name: 12 on alpine3.11
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 12 alpine3.11
diff --git a/.github/workflows/12-alpine3.12.yml b/.github/workflows/12-alpine3.12.yml
deleted file mode 100644
index f18fc554e..000000000
--- a/.github/workflows/12-alpine3.12.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 12 on alpine3.12
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/alpine3.12/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/alpine3.12/Dockerfile
-
-jobs:
-  build:
-    name: 12 on alpine3.12
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 12 alpine3.12
diff --git a/.github/workflows/12-alpine3.9.yml b/.github/workflows/12-alpine3.9.yml
deleted file mode 100644
index 385ff52a2..000000000
--- a/.github/workflows/12-alpine3.9.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 12 on alpine3.9
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/alpine3.9/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/alpine3.9/Dockerfile
-
-jobs:
-  build:
-    name: 12 on alpine3.9
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 12 alpine3.9
diff --git a/.github/workflows/12-buster-slim.yml b/.github/workflows/12-buster-slim.yml
deleted file mode 100644
index 04f8e8b11..000000000
--- a/.github/workflows/12-buster-slim.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 12 on buster-slim
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/buster-slim/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/buster-slim/Dockerfile
-
-jobs:
-  build:
-    name: 12 on buster-slim
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 12 buster-slim
diff --git a/.github/workflows/12-buster.yml b/.github/workflows/12-buster.yml
deleted file mode 100644
index 0b16ee6a9..000000000
--- a/.github/workflows/12-buster.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 12 on buster
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/buster/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/buster/Dockerfile
-
-jobs:
-  build:
-    name: 12 on buster
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 12 buster
diff --git a/.github/workflows/12-stretch-slim.yml b/.github/workflows/12-stretch-slim.yml
deleted file mode 100644
index 608160eeb..000000000
--- a/.github/workflows/12-stretch-slim.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 12 on stretch-slim
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/stretch-slim/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/stretch-slim/Dockerfile
-
-jobs:
-  build:
-    name: 12 on stretch-slim
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 12 stretch-slim
diff --git a/.github/workflows/12-stretch.yml b/.github/workflows/12-stretch.yml
deleted file mode 100644
index 8019f3e1f..000000000
--- a/.github/workflows/12-stretch.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 12 on stretch
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/stretch/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 12/stretch/Dockerfile
-
-jobs:
-  build:
-    name: 12 on stretch
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 12 stretch
diff --git a/.github/workflows/14-alpine3.10.yml b/.github/workflows/14-alpine3.10.yml
deleted file mode 100644
index e0d04a271..000000000
--- a/.github/workflows/14-alpine3.10.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 14 on alpine3.10
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/alpine3.10/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/alpine3.10/Dockerfile
-
-jobs:
-  build:
-    name: 14 on alpine3.10
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 14 alpine3.10
diff --git a/.github/workflows/14-alpine3.11.yml b/.github/workflows/14-alpine3.11.yml
deleted file mode 100644
index c401f9389..000000000
--- a/.github/workflows/14-alpine3.11.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 14 on alpine3.11
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/alpine3.11/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/alpine3.11/Dockerfile
-
-jobs:
-  build:
-    name: 14 on alpine3.11
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 14 alpine3.11
diff --git a/.github/workflows/14-alpine3.12.yml b/.github/workflows/14-alpine3.12.yml
deleted file mode 100644
index 3a20d9520..000000000
--- a/.github/workflows/14-alpine3.12.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 14 on alpine3.12
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/alpine3.12/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/alpine3.12/Dockerfile
-
-jobs:
-  build:
-    name: 14 on alpine3.12
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 14 alpine3.12
diff --git a/.github/workflows/14-buster-slim.yml b/.github/workflows/14-buster-slim.yml
deleted file mode 100644
index b7d027f93..000000000
--- a/.github/workflows/14-buster-slim.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 14 on buster-slim
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/buster-slim/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/buster-slim/Dockerfile
-
-jobs:
-  build:
-    name: 14 on buster-slim
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 14 buster-slim
diff --git a/.github/workflows/14-buster.yml b/.github/workflows/14-buster.yml
deleted file mode 100644
index 4481aadc2..000000000
--- a/.github/workflows/14-buster.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 14 on buster
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/buster/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/buster/Dockerfile
-
-jobs:
-  build:
-    name: 14 on buster
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 14 buster
diff --git a/.github/workflows/14-stretch-slim.yml b/.github/workflows/14-stretch-slim.yml
deleted file mode 100644
index d7c68f026..000000000
--- a/.github/workflows/14-stretch-slim.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 14 on stretch-slim
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/stretch-slim/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/stretch-slim/Dockerfile
-
-jobs:
-  build:
-    name: 14 on stretch-slim
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 14 stretch-slim
diff --git a/.github/workflows/14-stretch.yml b/.github/workflows/14-stretch.yml
deleted file mode 100644
index dfbfea496..000000000
--- a/.github/workflows/14-stretch.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: 14 on stretch
-
-on:
-  push:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/stretch/Dockerfile
-  pull_request:
-    paths:
-      - functions.sh
-      - test-build.sh
-      - test-image.bats
-      - 14/stretch/Dockerfile
-
-jobs:
-  build:
-    name: 14 on stretch
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh 14 stretch
diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
new file mode 100644
index 000000000..8cedd1bf3
--- /dev/null
+++ b/.github/workflows/build-test.yml
@@ -0,0 +1,72 @@
+name: test-build
+
+on:
+  push:
+  pull_request:
+
+jobs:
+  gen-matrix:
+    name: generate-matrix
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Calculate file differences
+        uses: lots0logs/gh-action-get-changed-files@2.1.4
+        id: diff
+        with:
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Generate testing matrix
+        uses: actions/github-script@v3
+        id: generator
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          script: |
+            const script = require(`${process.env.GITHUB_WORKSPACE}/genMatrix.js`)
+            return script(
+              ${{ steps.diff.outputs.added }},
+              ${{ steps.diff.outputs.modified }},
+              ${{ steps.diff.outputs.renamed }},
+            );
+
+      - name: Matrix
+        run: echo "${{ toJson(steps.generator.outputs.result) }}"
+
+    outputs:
+      matrix: ${{ steps.generator.outputs.result }}
+
+  build:
+    if: ${{ fromJson(needs.gen-matrix.outputs.matrix) }}
+    needs: gen-matrix
+    name: build
+    runs-on: ubuntu-latest
+    timeout-minutes: 600
+    strategy:
+      fail-fast: false
+      matrix: ${{ fromJson(needs.gen-matrix.outputs.matrix) }}
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Install bats
+        run: sudo apt-get install bats
+
+      - name: Write experimental docker flag
+        run: |
+          echo '{"experimental": true}' | sudo tee /etc/docker/daemon.json
+
+      - name: Restart docker daemon
+        run: sudo systemctl restart docker
+
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v1
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v1
+
+      - name: Build and test
+        run: ./test-build.sh ${{ matrix.version }} ${{ matrix.variant }} ${{ matrix.arch }}
diff --git a/functions.sh b/functions.sh
index be9c57539..25b6b26bc 100755
--- a/functions.sh
+++ b/functions.sh
@@ -359,3 +359,51 @@ function tests_updated() {
   fi
   return 0
 }
+
+function arch_to_buildx_platform() {
+  local arch
+  local platform
+  arch=$1
+  shift
+
+  case $arch in
+    amd64)
+      platform="amd64"
+      ;;
+    arm32v6)
+      platform="arm/v6"
+      ;;
+    arm32v7)
+      platform="arm/v7"
+      ;;
+    arm64v8)
+      platform="arm64"
+      ;;
+    i386)
+      platform="386"
+      ;;
+    ppc64le)
+      platform="ppc64le"
+      ;;
+    s390x)
+      platform="s390x"
+      ;;
+    *)
+      echo "$0 does not support architecture ${arch} ... aborting"
+      exit 1
+      ;;
+  esac
+
+  echo "${platform}"
+}
+
+function json_array() {
+  echo -n '['
+  while [ $# -gt 0 ]; do
+    x=${1//\\/\\\\}
+    echo -n \""${x//\"/\\\"}"\"
+    [ $# -gt 1 ] && echo -n ','
+    shift
+  done
+  echo ']'
+}
diff --git a/genMatrix.js b/genMatrix.js
new file mode 100644
index 000000000..77882b333
--- /dev/null
+++ b/genMatrix.js
@@ -0,0 +1,111 @@
+const path = require('path');
+const fs = require('fs');
+
+const testFiles = [
+  'functions.sh',
+  'test-build.sh',
+  'test-image.bats',
+];
+
+const nodeDirRegex = /^\d+$/;
+
+const areTestFilesChanged = (changedFiles) => changedFiles
+  .filter((file) => testFiles.includes(file)).length;
+
+// Returns a list of the child directories in the given path
+const getChildDirectories = (parent) => fs.readdirSync(parent, { withFileTypes: true })
+    .filter((dirent) => dirent.isDirectory())
+    .map(({ name }) => path.resolve(parent, name));
+
+
+const getNodeVerionDirs = (base) => getChildDirectories(base)
+  .filter((childPath) => nodeDirRegex.test(path.basename(childPath)));
+
+// Returns the paths of Dockerfiles that are at: base/*/Dockerfile
+const getDockerfilesInChildDirs = (base) => getChildDirectories(base)
+    .map((childDir) => path.resolve(childDir, 'Dockerfile'));
+
+const getAllDockerfiles = (base) => getNodeVerionDirs(base).flatMap(getDockerfilesInChildDirs);
+
+// Get the Dockerfiles affected by the architectures file
+const getArchAffectedDockerfiles = (archFile) => getDockerfilesInChildDirs(path.dirname(archFile));
+
+const getAffectedDockerfiles = (filesAdded, filesModified, filesRenamed) => {
+  const files = [
+    ...filesAdded,
+    ...filesModified,
+    ...filesRenamed,
+  ];
+
+  // If the test files were changed, include everything
+  if (areTestFilesChanged(files)) {
+    console.log('Test files changed so scheduling all Dockerfiles');
+    return getAllDockerfiles(__dirname);
+  }
+
+  const dockerfiles = files.filter((file) => file.endsWith('/Dockerfile'));
+
+  // Look Dockerfiles affected by changed architectures files
+  const archAffectedFiles = files.filter((file) => file.endsWith('/architectures'))
+    .flatMap(getArchAffectedDockerfiles);
+
+  return [
+    ...dockerfiles,
+    ...archAffectedFiles,
+  ];
+};
+
+// Parses an arch line like:
+// amd64    stretch,stretch-slim
+// Into ["amd64", ["stretch", "stretch-slim"]]
+const parseArchLine = (line) => {
+  const [arch, rawVariants] = line.split(/\s+/);
+  const variants = rawVariants.split(',');
+
+  return [
+    arch,
+    variants
+  ];
+};
+
+// Parses an architectures file into an object like:
+// {
+//   "amd64": ["stretch", "stretch-slim"],
+//   // ....
+// }
+const parseArchFile = (file) => Object.fromEntries(
+  fs.readFileSync(file, { encoding: 'utf8' })
+    .split('\n')
+    .slice(1)
+    .filter((line) => line)
+    .map(parseArchLine),
+);
+
+// Given a Dockerfile path, this function returns an array of the supported arches
+const getDockerfileArches = (file, variant) => {
+  const archVariants = parseArchFile(path.resolve(path.dirname(file), '../architectures'));
+  return Object.keys(archVariants).filter((arch) => archVariants[arch].includes(variant));
+};
+
+const getDockerfileMatrixEntries = (file) => {
+  const [version, variant] = path.dirname(file).split(path.sep).slice(-2);
+  const supportedArches = getDockerfileArches(file, variant);
+
+  return supportedArches.map((arch) => ({
+    version,
+    variant,
+    arch,
+  }));
+};
+
+const generateBuildMatrix = (filesAdded, filesModified, filesRenamed) => {
+  // The Dockerfile paths contain the version in variant.
+  const dockerfiles = [...new Set(getAffectedDockerfiles(filesAdded, filesModified, filesRenamed))];
+
+  const entries = dockerfiles.flatMap(getDockerfileMatrixEntries);
+  return entries.length
+    ? { include: entries }
+    : null;
+};
+
+module.exports = generateBuildMatrix;
diff --git a/test-build.sh b/test-build.sh
index 6614725d3..682772007 100755
--- a/test-build.sh
+++ b/test-build.sh
@@ -11,6 +11,7 @@ set -euo pipefail
 # "10,12" becomes "10 12" and "slim,alpine" becomes "slim alpine"
 IFS=',' read -ra versions_arg <<< "${1:-}"
 IFS=',' read -ra variant_arg <<< "${2:-}"
+IFS=',' read -ra arches_arg <<< "${3:-}"
 
 default_variant=$(get_config "./" "default_variant")
 
@@ -18,6 +19,7 @@ function build() {
   local version
   local tag
   local variant
+  local platform
   local full_tag
   local path
   version="$1"
@@ -26,13 +28,15 @@ function build() {
   shift
   tag="$1"
   shift
+  platform="$1"
+  shift
 
   full_tag=$(get_full_tag "${variant}" "${tag}")
   path=$(get_path "${version}" "${variant}")
 
-  info "Building ${full_tag}..."
+  info "Building ${full_tag} on ${platform}..."
 
-  if ! docker build --cpuset-cpus="0,1" -t node:"${full_tag}" "${path}"; then
+  if ! docker buildx build --load --platform "${platform}" -t node:"${full_tag}" "${path}"; then
     fatal "Build of ${full_tag} failed!"
   fi
   info "Build of ${full_tag} succeeded."
@@ -41,6 +45,7 @@ function build() {
 function test_image() {
   local full_version
   local variant
+  local platform
   local tag
   local full_tag
   full_version="$1"
@@ -49,25 +54,23 @@ function test_image() {
   shift
   tag="$1"
   shift
+  platform="$1"
+  shift
 
   full_tag=$(get_full_tag "${variant}" "${tag}")
 
-  info "Testing ${full_tag}"
+  info "Testing ${full_tag} on ${platform}"
   (
     export full_version=${full_version}
     export full_tag=${full_tag}
+    export platform=${platform}
     bats test-image.bats
   )
 }
 
 cd "$(cd "${0%/*}" && pwd -P)" || exit
 
-IFS=' ' read -ra versions <<< "$(get_versions . "${versions_arg[@]}")"
-if [ ${#versions[@]} -eq 0 ]; then
-  fatal "No valid versions found!"
-fi
-
-for version in "${versions[@]}"; do
+for version in "${versions_arg[@]}"; do
   # Skip "docs" and other non-docker directories
   [ -f "${version}/Dockerfile" ] || [ -a "${version}/${default_variant}/Dockerfile" ] || continue
 
@@ -82,8 +85,13 @@ for version in "${versions[@]}"; do
     # Skip non-docker directories
     [ -f "${version}/${variant}/Dockerfile" ] || continue
 
-    build "${version}" "${variant}" "${tag}"
-    test_image "${full_version}" "${variant}" "${tag}"
+    for arch in "${arches_arg[@]}"; do
+      buildx_platform=$(arch_to_buildx_platform "${arch}")
+
+      build "${version}" "${variant}" "${tag}" "linux/${buildx_platform}"
+      test_image "${full_version}" "${variant}" "${tag}" "linux/${buildx_platform}"
+    done
+
   done
 
 done
diff --git a/test-image.bats b/test-image.bats
index b424b95a4..5eba95b2a 100755
--- a/test-image.bats
+++ b/test-image.bats
@@ -1,17 +1,17 @@
 #!/usr/bin/env bats
 
 @test "Test for node and version" {
-  run docker run --rm node:"$full_tag" node -e "process.stdout.write(process.versions.node)"
+  run docker run --platform ${platform} --rm node:"$full_tag" node -e "process.stdout.write(process.versions.node)"
   [ "$status" -eq 0 ]
   [ "$output" == "${full_version}" ]
 }
 
 @test "Test for npm" {
-  run docker run --rm node:"$full_tag" npm --version
+  run docker run --platform ${platform} --rm node:"$full_tag" npm --version
   [ "$status" -eq 0 ]
 }
 
 @test "Test for yarn" {
-  run docker run --rm node:"$full_tag" yarn --version
+  run docker run --platform ${platform} --rm node:"$full_tag" yarn --version
   [ "$status" -eq 0 ]
 }
diff --git a/update.sh b/update.sh
index 8b66a6e55..72c539559 100755
--- a/update.sh
+++ b/update.sh
@@ -196,6 +196,9 @@ function add_stage() {
   local variant=${1}
   shift
 
+  IFS=' ' read -ra supportedArches <<< "$(get_supported_arches "${version}" "${variant}")"
+  read -r arch_json <<< "$(json_array "${supportedArches[@]}")"
+
   echo "name: ${version} on ${variant}
 
 on:
@@ -216,10 +219,31 @@ jobs:
   build:
     name: ${version} on ${variant}
     runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix: \${{fromJson('{\"arch\":${arch_json}}')}}
     steps:
-      - uses: actions/checkout@v2
-      - run: sudo apt-get install bats
-      - run: ./test-build.sh ${version} ${variant}" > ".github/workflows/${version}-${variant}.yml"
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Install bats
+        run: sudo apt-get install bats
+
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v1
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v1
+
+      - name: Write experimental docker flag
+        run: |
+          echo '{\"experimental\": true}' | sudo tee /etc/docker/daemon.json
+
+      - name: Restart docker daemon
+        run: sudo systemctl restart docker
+
+      - name: Build and test
+        run: ./test-build.sh ${version} ${variant} \${{ matrix.arch }}" > ".github/workflows/${version}-${variant}.yml"
 }
 
 for version in "${versions[@]}"; do