diff --git a/services/contacts/.nvmrc b/services/contacts/.nvmrc
index 5ab50cd8e9..d9f880069d 100644
--- a/services/contacts/.nvmrc
+++ b/services/contacts/.nvmrc
@@ -1 +1 @@
-14.18.3
+16.14.2
diff --git a/services/contacts/Dockerfile b/services/contacts/Dockerfile
index 92da7718ec..1bf436b430 100644
--- a/services/contacts/Dockerfile
+++ b/services/contacts/Dockerfile
@@ -2,7 +2,7 @@
 # Instead run bin/update_build_scripts from
 # https://github.com/sharelatex/sharelatex-dev-environment
 
-FROM gcr.io/overleaf-ops/node:14.18.3 as base
+FROM node:16.14.2 as base
 
 WORKDIR /overleaf/services/contacts
 
diff --git a/services/contacts/Makefile b/services/contacts/Makefile
index 9f36b9555c..4f89afb502 100644
--- a/services/contacts/Makefile
+++ b/services/contacts/Makefile
@@ -30,7 +30,7 @@ HERE=$(shell pwd)
 MONOREPO=$(shell cd ../../ && pwd)
 # Run the linting commands in the scope of the monorepo.
 # Eslint and prettier (plus some configs) are on the root.
-RUN_LINTING = docker run --rm -v $(MONOREPO):$(MONOREPO) -w $(HERE) gcr.io/overleaf-ops/node:14.18.3 npm run --silent
+RUN_LINTING = docker run --rm -v $(MONOREPO):$(MONOREPO) -w $(HERE) node:16.14.2 npm run --silent
 
 format:
 	$(RUN_LINTING) format
diff --git a/services/contacts/buildscript.txt b/services/contacts/buildscript.txt
index c94c634ab5..e0f883f9b3 100644
--- a/services/contacts/buildscript.txt
+++ b/services/contacts/buildscript.txt
@@ -3,7 +3,6 @@ contacts
 --docker-repos=gcr.io/overleaf-ops
 --env-add=
 --env-pass-through=
---node-image=gcr.io/overleaf-ops/node
---node-version=14.18.3
+--node-version=16.14.2
 --public-repo=False
 --script-version=4.1.0
diff --git a/services/contacts/docker-compose.yml b/services/contacts/docker-compose.yml
index adb4968ba5..7ced69a758 100644
--- a/services/contacts/docker-compose.yml
+++ b/services/contacts/docker-compose.yml
@@ -6,7 +6,7 @@ version: "2.3"
 
 services:
   test_unit:
-    image: gcr.io/overleaf-ops/node:14.18.3
+    image: node:16.14.2
     volumes:
       - .:/overleaf/services/contacts
       - ../../node_modules:/overleaf/node_modules
@@ -20,7 +20,7 @@ services:
     user: node
 
   test_acceptance:
-    image: gcr.io/overleaf-ops/node:14.18.3
+    image: node:16.14.2
     volumes:
       - .:/overleaf/services/contacts
       - ../../node_modules:/overleaf/node_modules
diff --git a/services/notifications/.nvmrc b/services/notifications/.nvmrc
index 5ab50cd8e9..d9f880069d 100644
--- a/services/notifications/.nvmrc
+++ b/services/notifications/.nvmrc
@@ -1 +1 @@
-14.18.3
+16.14.2
diff --git a/services/notifications/Dockerfile b/services/notifications/Dockerfile
index 7fe1184fd6..b7c0ead6ea 100644
--- a/services/notifications/Dockerfile
+++ b/services/notifications/Dockerfile
@@ -2,7 +2,7 @@
 # Instead run bin/update_build_scripts from
 # https://github.com/sharelatex/sharelatex-dev-environment
 
-FROM gcr.io/overleaf-ops/node:14.18.3 as base
+FROM node:16.14.2 as base
 
 WORKDIR /overleaf/services/notifications
 
diff --git a/services/notifications/Makefile b/services/notifications/Makefile
index 91e65d84cf..4a8a625d1f 100644
--- a/services/notifications/Makefile
+++ b/services/notifications/Makefile
@@ -30,7 +30,7 @@ HERE=$(shell pwd)
 MONOREPO=$(shell cd ../../ && pwd)
 # Run the linting commands in the scope of the monorepo.
 # Eslint and prettier (plus some configs) are on the root.
-RUN_LINTING = docker run --rm -v $(MONOREPO):$(MONOREPO) -w $(HERE) gcr.io/overleaf-ops/node:14.18.3 npm run --silent
+RUN_LINTING = docker run --rm -v $(MONOREPO):$(MONOREPO) -w $(HERE) node:16.14.2 npm run --silent
 
 format:
 	$(RUN_LINTING) format
diff --git a/services/notifications/buildscript.txt b/services/notifications/buildscript.txt
index f3db5bf0c9..45a91611bd 100644
--- a/services/notifications/buildscript.txt
+++ b/services/notifications/buildscript.txt
@@ -3,7 +3,6 @@ notifications
 --docker-repos=gcr.io/overleaf-ops
 --env-add=
 --env-pass-through=
---node-image=gcr.io/overleaf-ops/node
---node-version=14.18.3
+--node-version=16.14.2
 --public-repo=True
 --script-version=4.1.0
diff --git a/services/notifications/docker-compose.yml b/services/notifications/docker-compose.yml
index d815de7211..2fb3afab9d 100644
--- a/services/notifications/docker-compose.yml
+++ b/services/notifications/docker-compose.yml
@@ -6,7 +6,7 @@ version: "2.3"
 
 services:
   test_unit:
-    image: gcr.io/overleaf-ops/node:14.18.3
+    image: node:16.14.2
     volumes:
       - .:/overleaf/services/notifications
       - ../../node_modules:/overleaf/node_modules
@@ -20,7 +20,7 @@ services:
     user: node
 
   test_acceptance:
-    image: gcr.io/overleaf-ops/node:14.18.3
+    image: node:16.14.2
     volumes:
       - .:/overleaf/services/notifications
       - ../../node_modules:/overleaf/node_modules
diff --git a/services/real-time/.nvmrc b/services/real-time/.nvmrc
index 5ab50cd8e9..d9f880069d 100644
--- a/services/real-time/.nvmrc
+++ b/services/real-time/.nvmrc
@@ -1 +1 @@
-14.18.3
+16.14.2
diff --git a/services/real-time/Dockerfile b/services/real-time/Dockerfile
index ed883fb584..94c589dea2 100644
--- a/services/real-time/Dockerfile
+++ b/services/real-time/Dockerfile
@@ -2,7 +2,7 @@
 # Instead run bin/update_build_scripts from
 # https://github.com/sharelatex/sharelatex-dev-environment
 
-FROM gcr.io/overleaf-ops/node:14.18.3 as base
+FROM node:16.14.2 as base
 
 WORKDIR /overleaf/services/real-time
 
diff --git a/services/real-time/Makefile b/services/real-time/Makefile
index 5991bddeee..05333b3b4b 100644
--- a/services/real-time/Makefile
+++ b/services/real-time/Makefile
@@ -30,7 +30,7 @@ HERE=$(shell pwd)
 MONOREPO=$(shell cd ../../ && pwd)
 # Run the linting commands in the scope of the monorepo.
 # Eslint and prettier (plus some configs) are on the root.
-RUN_LINTING = docker run --rm -v $(MONOREPO):$(MONOREPO) -w $(HERE) gcr.io/overleaf-ops/node:14.18.3 npm run --silent
+RUN_LINTING = docker run --rm -v $(MONOREPO):$(MONOREPO) -w $(HERE) node:16.14.2 npm run --silent
 
 format:
 	$(RUN_LINTING) format
diff --git a/services/real-time/buildscript.txt b/services/real-time/buildscript.txt
index 7894471894..3ad7113b41 100644
--- a/services/real-time/buildscript.txt
+++ b/services/real-time/buildscript.txt
@@ -3,7 +3,6 @@ real-time
 --docker-repos=gcr.io/overleaf-ops
 --env-add=
 --env-pass-through=
---node-image=gcr.io/overleaf-ops/node
---node-version=14.18.3
+--node-version=16.14.2
 --public-repo=True
 --script-version=4.1.0
diff --git a/services/real-time/docker-compose.yml b/services/real-time/docker-compose.yml
index 1e3f7c7e0d..979d9df408 100644
--- a/services/real-time/docker-compose.yml
+++ b/services/real-time/docker-compose.yml
@@ -6,7 +6,7 @@ version: "2.3"
 
 services:
   test_unit:
-    image: gcr.io/overleaf-ops/node:14.18.3
+    image: node:16.14.2
     volumes:
       - .:/overleaf/services/real-time
       - ../../node_modules:/overleaf/node_modules
@@ -20,7 +20,7 @@ services:
     user: node
 
   test_acceptance:
-    image: gcr.io/overleaf-ops/node:14.18.3
+    image: node:16.14.2
     volumes:
       - .:/overleaf/services/real-time
       - ../../node_modules:/overleaf/node_modules
diff --git a/services/spelling/.nvmrc b/services/spelling/.nvmrc
index 5ab50cd8e9..d9f880069d 100644
--- a/services/spelling/.nvmrc
+++ b/services/spelling/.nvmrc
@@ -1 +1 @@
-14.18.3
+16.14.2
diff --git a/services/spelling/Dockerfile b/services/spelling/Dockerfile
index 0a946d9ace..3a2268800d 100644
--- a/services/spelling/Dockerfile
+++ b/services/spelling/Dockerfile
@@ -2,7 +2,7 @@
 # Instead run bin/update_build_scripts from
 # https://github.com/sharelatex/sharelatex-dev-environment
 
-FROM gcr.io/overleaf-ops/node:14.18.3 as base
+FROM node:16.14.2 as base
 
 WORKDIR /overleaf/services/spelling
 COPY services/spelling/install_deps.sh /overleaf/services/spelling/
diff --git a/services/spelling/Makefile b/services/spelling/Makefile
index 9ddeeedd76..db6411fa4c 100644
--- a/services/spelling/Makefile
+++ b/services/spelling/Makefile
@@ -30,7 +30,7 @@ HERE=$(shell pwd)
 MONOREPO=$(shell cd ../../ && pwd)
 # Run the linting commands in the scope of the monorepo.
 # Eslint and prettier (plus some configs) are on the root.
-RUN_LINTING = docker run --rm -v $(MONOREPO):$(MONOREPO) -w $(HERE) gcr.io/overleaf-ops/node:14.18.3 npm run --silent
+RUN_LINTING = docker run --rm -v $(MONOREPO):$(MONOREPO) -w $(HERE) node:16.14.2 npm run --silent
 
 format:
 	$(RUN_LINTING) format
diff --git a/services/spelling/buildscript.txt b/services/spelling/buildscript.txt
index 47ddd8a0cb..7043696cda 100644
--- a/services/spelling/buildscript.txt
+++ b/services/spelling/buildscript.txt
@@ -4,7 +4,6 @@ spelling
 --docker-repos=gcr.io/overleaf-ops
 --env-add=
 --env-pass-through=
---node-image=gcr.io/overleaf-ops/node
---node-version=14.18.3
+--node-version=16.14.2
 --public-repo=False
 --script-version=4.1.0
diff --git a/services/spelling/install_deps.sh b/services/spelling/install_deps.sh
index 77dc28693d..c88135e9ba 100644
--- a/services/spelling/install_deps.sh
+++ b/services/spelling/install_deps.sh
@@ -2,19 +2,11 @@
 
 set -ex
 
-echo 'APT::Default-Release "stretch";' >/etc/apt/apt.conf.d/default-release
-
 # The following aspell packages exist in Ubuntu but not Debian:
 # aspell-af, aspell-id, aspell-nr, aspell-ns, aspell-st, aspell-tn, aspell-ts, aspell-xhu
 echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal main universe" > /etc/apt/sources.list.d/focal-amd.list
 echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal main universe" > /etc/apt/sources.list.d/focal-ports-arm.list
 apt-key adv --no-tty --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
-# Need to install aspell-no from testing (buster) as broken in stable (stretch).
-echo "deb http://deb.debian.org/debian/ unstable main" > /etc/apt/sources.list.d/unstable.list
 
 apt-get update
-apt-get install -y aspell aspell-en aspell-af aspell-ar aspell-ar-large aspell-bg aspell-br aspell-ca aspell-cs aspell-cy aspell-da aspell-de aspell-de-alt aspell-el aspell-eo aspell-es aspell-et aspell-eu-es aspell-fa aspell-fo aspell-fr aspell-ga aspell-gl-minimos aspell-hr aspell-hsb aspell-id aspell-it aspell-kk aspell-ku aspell-lt aspell-lv aspell-nl aspell-nr aspell-ns  aspell-pa aspell-pl aspell-pt aspell-pt-br aspell-ro aspell-ru aspell-sk aspell-sl aspell-st aspell-sv aspell-tl aspell-tn aspell-ts aspell-xh
-
-printf 'Package: aspell-*\nPin: release a=unstable\nPin-Priority: 1337\n' \
-  > /etc/apt/preferences.d/aspell-from-unstable
-apt-get install aspell-no
+apt-get install -y aspell aspell-en aspell-af aspell-ar aspell-ar-large aspell-bg aspell-br aspell-ca aspell-cs aspell-cy aspell-da aspell-de aspell-de-1901 aspell-el aspell-eo aspell-es aspell-et aspell-eu-es aspell-fa aspell-fo aspell-fr aspell-ga aspell-gl-minimos aspell-hr aspell-hsb aspell-id aspell-it aspell-kk aspell-ku aspell-lt aspell-lv aspell-nl aspell-no aspell-nr aspell-ns  aspell-pa aspell-pl aspell-pt aspell-pt-br aspell-ro aspell-ru aspell-sk aspell-sl aspell-st aspell-sv aspell-tl aspell-tn aspell-ts aspell-xh
diff --git a/services/spelling/test/acceptance/js/CheckTest.js b/services/spelling/test/acceptance/js/CheckTest.js
index 76cb4f1774..1204f831b4 100644
--- a/services/spelling/test/acceptance/js/CheckTest.js
+++ b/services/spelling/test/acceptance/js/CheckTest.js
@@ -27,7 +27,26 @@ describe('checking words', function () {
     it('should return the list of misspellings', async function () {
       const body = JSON.parse(response.body)
       expect(body).to.deep.equal({
-        misspellings: [{ index: 0, suggestions: ['anther', 'another'] }],
+        misspellings: [
+          {
+            index: 0,
+            suggestions: [
+              'anther',
+              'another',
+              'anthers',
+              'panther',
+              'anathema',
+              'anthem',
+              'nether',
+              "anther's",
+              'ante',
+              'neither',
+              'norther',
+              'ether',
+              'other',
+            ],
+          },
+        ],
       })
     })
   })
diff --git a/services/track-changes/.nvmrc b/services/track-changes/.nvmrc
index 5ab50cd8e9..d9f880069d 100644
--- a/services/track-changes/.nvmrc
+++ b/services/track-changes/.nvmrc
@@ -1 +1 @@
-14.18.3
+16.14.2
diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile
index 656e4dc047..e63a6eab66 100644
--- a/services/track-changes/Dockerfile
+++ b/services/track-changes/Dockerfile
@@ -2,7 +2,7 @@
 # Instead run bin/update_build_scripts from
 # https://github.com/sharelatex/sharelatex-dev-environment
 
-FROM gcr.io/overleaf-ops/node:14.18.3 as base
+FROM node:16.14.2 as base
 
 WORKDIR /overleaf/services/track-changes
 
diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile
index 732caa178b..3bb89dba6c 100644
--- a/services/track-changes/Makefile
+++ b/services/track-changes/Makefile
@@ -30,7 +30,7 @@ HERE=$(shell pwd)
 MONOREPO=$(shell cd ../../ && pwd)
 # Run the linting commands in the scope of the monorepo.
 # Eslint and prettier (plus some configs) are on the root.
-RUN_LINTING = docker run --rm -v $(MONOREPO):$(MONOREPO) -w $(HERE) gcr.io/overleaf-ops/node:14.18.3 npm run --silent
+RUN_LINTING = docker run --rm -v $(MONOREPO):$(MONOREPO) -w $(HERE) node:16.14.2 npm run --silent
 
 format:
 	$(RUN_LINTING) format
diff --git a/services/track-changes/app/js/HttpController.js b/services/track-changes/app/js/HttpController.js
index 3fd093ee47..5194d7bcd7 100644
--- a/services/track-changes/app/js/HttpController.js
+++ b/services/track-changes/app/js/HttpController.js
@@ -238,7 +238,7 @@ module.exports = HttpController = {
     UpdatesManager.exportProject(
       project_id,
       function (err, { updates, userIds }, confirmWrite) {
-        const abortStreaming = req.aborted || res.finished || res.destroyed
+        const abortStreaming = req.destroyed || res.finished || res.destroyed
         if (abortStreaming) {
           // Tell the producer to stop emitting data
           if (confirmWrite) confirmWrite(new Error('stop'))
diff --git a/services/track-changes/app/js/ZipManager.js b/services/track-changes/app/js/ZipManager.js
index 3310e9c28d..b302cede97 100644
--- a/services/track-changes/app/js/ZipManager.js
+++ b/services/track-changes/app/js/ZipManager.js
@@ -162,7 +162,7 @@ async function makeTempDirectory() {
  * Clean up a temporary directory made with makeTempDirectory()
  */
 function cleanupTempDirectory(tmpdir) {
-  fs.promises.rmdir(tmpdir, { recursive: true }).catch(err => {
+  fs.promises.rm(tmpdir, { recursive: true, force: true }).catch(err => {
     if (err) {
       logger.warn({ err, tmpdir }, 'Failed to clean up temp directory')
     }
diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt
index f3509b7b92..29a7d32576 100644
--- a/services/track-changes/buildscript.txt
+++ b/services/track-changes/buildscript.txt
@@ -3,7 +3,6 @@ track-changes
 --docker-repos=gcr.io/overleaf-ops
 --env-add=AWS_BUCKET=bucket
 --env-pass-through=
---node-image=gcr.io/overleaf-ops/node
---node-version=14.18.3
+--node-version=16.14.2
 --public-repo=True
 --script-version=4.1.0
diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml
index 90cd52ac6e..3c19f637a5 100644
--- a/services/track-changes/docker-compose.yml
+++ b/services/track-changes/docker-compose.yml
@@ -6,7 +6,7 @@ version: "2.3"
 
 services:
   test_unit:
-    image: gcr.io/overleaf-ops/node:14.18.3
+    image: node:16.14.2
     volumes:
       - .:/overleaf/services/track-changes
       - ../../node_modules:/overleaf/node_modules
@@ -20,7 +20,7 @@ services:
     user: node
 
   test_acceptance:
-    image: gcr.io/overleaf-ops/node:14.18.3
+    image: node:16.14.2
     volumes:
       - .:/overleaf/services/track-changes
       - ../../node_modules:/overleaf/node_modules