diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 9d8ef1e..5b2f1c6 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,5 +1,5 @@
 {
-  "name": "error-report-web (Django + MySQL)",
+  "name": "error-report-web (Django + MariaDB)",
   "dockerComposeFile": [
     "../docker-compose.yaml",
     "docker-compose.dev.yml"
@@ -41,7 +41,7 @@
             "previewLimit": 50,
             "server": "db",
             "port": 3306,
-            "driver": "MySQL",
+            "driver": "MariaDB",
             "name": "errorreport",
             "database": "errorreport",
             "username": "errorreport",
diff --git a/.devcontainer/docker-compose.dev.yml b/.devcontainer/docker-compose.dev.yml
index 32aa685..6b73bd3 100644
--- a/.devcontainer/docker-compose.dev.yml
+++ b/.devcontainer/docker-compose.dev.yml
@@ -1,5 +1,5 @@
 # Devcontainer override for docker-compose.yaml: runserver on the mounted
-# workspace, MySQL port unpublished (no clash with other dev DBs), no nginx.
+# workspace, MariaDB port unpublished (no clash with other dev DBs), no nginx.
 services:
   backend:
     command: ["python3", "manage.py", "runserver", "0.0.0.0:8000"]
diff --git a/README b/README
index 7539329..63f3776 100644
--- a/README
+++ b/README
@@ -44,7 +44,7 @@ For more information on error reporting and log collection see: http://www.yocto
 Docker
 ------
 
-docker-compose.yaml runs a development setup: MySQL, the application in
+docker-compose.yaml runs a development setup: MariaDB, the application in
 gunicorn, and nginx serving it on http://localhost:8080. Its settings are in
 .env.dev.app and .env.dev.db, which hold development-only values.
 
diff --git a/docker-compose.prod.yaml b/docker-compose.prod.yaml
index 582f76c..576590d 100644
--- a/docker-compose.prod.yaml
+++ b/docker-compose.prod.yaml
@@ -1,14 +1,14 @@
 # Prod: nginx with Let's Encrypt TLS in front of gunicorn. See README.
 services:
   db:
-    image: mysql:8.4
+    image: mariadb:lts
     restart: unless-stopped
     volumes:
       - data:/var/lib/mysql
     env_file:
       - .env.prod.db
     healthcheck:
-      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "--silent"]
+      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
       timeout: 20s
       retries: 10
 
diff --git a/docker-compose.staging.yaml b/docker-compose.staging.yaml
index 908bbb7..704fcf0 100644
--- a/docker-compose.staging.yaml
+++ b/docker-compose.staging.yaml
@@ -1,14 +1,14 @@
 # Staging: nginx with Let's Encrypt TLS in front of gunicorn. See README.
 services:
   db:
-    image: mysql:8.4
+    image: mariadb:lts
     restart: unless-stopped
     volumes:
       - data:/var/lib/mysql
     env_file:
       - .env.staging.db
     healthcheck:
-      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "--silent"]
+      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
       timeout: 20s
       retries: 10
 
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 9f0a735..d5339ab 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -1,6 +1,6 @@
 services:
   db:
-    image: mysql:8.4
+    image: mariadb:lts
     container_name: error_report_web_db
     restart: always
     volumes:
@@ -10,8 +10,7 @@ services:
     ports:
       - "3306:3306"
     healthcheck:
-      # TCP: MySQL's init-time server only listens on the socket.
-      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "--silent"]
+      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
       timeout: 20s
       retries: 10
   
diff --git a/docker/run-tests.sh b/docker/run-tests.sh
index be1faeb..4e7913b 100755
--- a/docker/run-tests.sh
+++ b/docker/run-tests.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
-# Run the tests in the app image against a throwaway MySQL. Used by CI.
+# Run the tests in the app image against a throwaway MariaDB. Used by CI.
 #
 # SPDX-License-Identifier: MIT
 
@@ -17,8 +17,8 @@ trap 'docker rm -f $DB >/dev/null; docker network rm $NET >/dev/null' EXIT
 # Root so Django can create the test database; testingpw isn't a secret.
 docker run -d --name $DB --network $NET --network-alias db \
     -e MYSQL_ROOT_PASSWORD=testingpw -e MYSQL_DATABASE=errorreport \
-    mysql:8.4 >/dev/null
-until docker exec $DB mysql -h127.0.0.1 -uroot -ptestingpw -e 'SELECT 1' >/dev/null 2>&1; do
+    mariadb:lts >/dev/null
+until docker exec $DB healthcheck.sh --connect --innodb_initialized >/dev/null 2>&1; do
     if [ "$(docker inspect -f '{{.State.Running}}' $DB)" != true ]; then
         docker logs $DB | tail -5; exit 1
     fi
