diff --git a/packages/buendia-backup/data/usr/bin/buendia-backup b/packages/buendia-backup/data/usr/bin/buendia-backup
index cd7597fe..37822862 100755
--- a/packages/buendia-backup/data/usr/bin/buendia-backup
+++ b/packages/buendia-backup/data/usr/bin/buendia-backup
@@ -137,7 +137,9 @@ echo "Saving Buendia configuration..."
 # about any which might be missing.
 config_paths=$(ls -d /usr/share/buendia/{counts,distilled,openmrs,profiles,site} || true)
 tar cfz "$new_dir/buendia.tar.gz" --exclude '*.omod' $config_paths
-ls -l "$new_dir/buendia.tar.gz"
+encrypt_file "$new_dir/buendia.tar.gz"
+rm "$new_dir/buendia.tar.gz"
+ls -l "$new_dir/buendia.tar.gz.enc"
 
 # ---- Back up the MySQL database.
 
@@ -148,7 +150,9 @@ if ! buendia-mysql-dump openmrs "$new_dir/openmrs.zip" >"$tmp/out" 2>&1; then
     echo "buendia-mysql-dump failed!"
     exit 1
 fi
-ls -l "$new_dir/openmrs.zip"
+encrypt_file "$new_dir/openmrs.zip"
+rm "$new_dir/openmrs.zip"
+ls -l "$new_dir/openmrs.zip.enc"
 
 # ---- Back up the package list and non-base packages.
 
@@ -216,6 +220,17 @@ sync
 mv "$new_dir" "$final_dir"
 sync
 
+# If we're backing up to a block device, make sure that the system key is
+# copied there, now that the most recent backup has been committed.
+#
+# This may overwrite the key used to create earlier backups, which is why
+# we waited until this point to copy the key.
+if [ -n "$mnt_dir" ]; then
+    echo "Backing up system key to external storage."
+    cp /usr/share/buendia/system.key "$mnt_dir"
+    sync
+fi
+
 # ---- Sweep all the packages in completed backups into a common directory.
 
 echo
diff --git a/packages/buendia-backup/data/usr/bin/buendia-restore b/packages/buendia-backup/data/usr/bin/buendia-restore
index 400d46a1..46ff52db 100755
--- a/packages/buendia-backup/data/usr/bin/buendia-restore
+++ b/packages/buendia-backup/data/usr/bin/buendia-restore
@@ -137,11 +137,27 @@ if [ $progress_state -le $PROGRESS_SETTINGS_NEXT ]; then
     buendia-pkgserver-index-debs /usr/share/buendia/packages $suite
   fi
 
+  if [ -n "$mnt_dir" ]; then
+    echo
+    if [ ! -r "$mnt_dir/system.key" ]; then
+        echo "ERROR: Cannot find system key on external storage!"
+        exit 1
+    fi
+    echo "Restoring system key from external storage..."
+    cp "$mnt_dir/system.key" /usr/share/buendia/system.key
+    chown root:mysql /usr/share/buendia/system.key
+    chmod 640 /usr/share/buendia/system.key
+  fi
+
   # Restore backed up site settings
   echo
   echo "Restoring settings..."
   mv /usr/share/buendia/site /usr/share/buendia/site.$$
-  if tar -xzf $root/buendia.tar.gz -C / usr/share/buendia/site; then
+  # Copy the file off the backup device to ensure we have enough space to
+  # decrypt it
+  cp "$root/buendia.tar.gz.enc" "$tmp"
+  decrypt_file "$tmp/buendia.tar.gz"
+  if tar -xzf $tmp/buendia.tar.gz -C / usr/share/buendia/site; then
     rm -rf /usr/share/buendia/site.$$
   else
     rm -rf /usr/share/buendia/site
@@ -171,8 +187,12 @@ if [ $progress_state -le $PROGRESS_MYSQL_NEXT ]; then
   # ---- Restore the MySQL database.
   echo
   echo "Restoring MySQL database..."
+  # Copy the file off the backup device to ensure we have enough space to
+  # decrypt it
+  cp "$root/openmrs.zip.enc" "$tmp"
+  decrypt_file "$tmp/openmrs.zip"
   service tomcat7 stop
-  if ! buendia-mysql-load -f openmrs "$root/openmrs.zip" >"$tmp/out" 2>&1; then
+  if ! buendia-mysql-load -f openmrs "$tmp/openmrs.zip" >"$tmp/out" 2>&1; then
     if [ -e $tmp/out ]; then
       cat "$tmp/out"
     fi
diff --git a/packages/buendia-backup/data/usr/share/buendia/tests/40-backup-to-local b/packages/buendia-backup/data/usr/share/buendia/tests/40-backup-to-local
index 920f6368..d00c2c96 100755
--- a/packages/buendia-backup/data/usr/share/buendia/tests/40-backup-to-local
+++ b/packages/buendia-backup/data/usr/share/buendia/tests/40-backup-to-local
@@ -2,20 +2,36 @@ BUENDIA_TEST_BACKUP_TARGET=/var/backups/buendia/backup.$(date +%Y-%m-%d)
 
 # When the backup cron job runs
 test_10_run_backup_cron () {
-    rm -rf $BUENDIA_TEST_BACK_TARGET
+    rm -rf $BUENDIA_TEST_BACKUP_TARGET
     execute_cron_right_now backup
 }
 
-# Then it stores a tarball of site configs locally
-test_20_cron_saved_site_config () {
-    tar tfz $BUENDIA_TEST_BACKUP_TARGET/buendia.tar.gz
+# Then it encrypts the backup
+test_20_no_unencrypted_backup_files () {
+    ! [ -f $BUENDIA_TEST_BACKUP_TARGET/buendia.tar.gz \
+     -o -f $BUENDIA_TEST_BACKUP_TARGET/openmrs.zip ]
+}
+
+# And it stores a tarball of site configs locally
+test_30_cron_saved_site_config () {
+    cp $BUENDIA_TEST_BACKUP_TARGET/buendia.tar.gz.enc .
+    decrypt_file buendia.tar.gz
+    tar tfz buendia.tar.gz
 }
 
 # And it stores a database dump locally
-test_20_cron_saved_database_dump () {
-    unzip -t $BUENDIA_TEST_BACKUP_TARGET/openmrs.zip
+test_30_cron_saved_database_dump () {
+    cp $BUENDIA_TEST_BACKUP_TARGET/openmrs.zip.enc .
+    decrypt_file openmrs.zip
+    unzip -t openmrs.zip
 }
 
-test_20_cron_saved_package_list () {
+# And it stores a list of installed Buendia packages
+test_40_cron_saved_package_list () {
     [ -s $BUENDIA_TEST_BACKUP_TARGET/buendia.list ]
 }
+
+# But it doesn't back up the system key locally
+test_50_no_local_system_key_backup () {
+    [ ! -f $BUENDIA_TEST_BACKUP_TARGET/system.key ]
+}
diff --git a/packages/buendia-backup/data/usr/share/buendia/tests/50-backup-locked-safe b/packages/buendia-backup/data/usr/share/buendia/tests/50-backup-locked-safe
index 50e128af..77759123 100755
--- a/packages/buendia-backup/data/usr/share/buendia/tests/50-backup-locked-safe
+++ b/packages/buendia-backup/data/usr/share/buendia/tests/50-backup-locked-safe
@@ -1,7 +1,11 @@
 test_10_prevent_simultaneous_backup () {
     mount_loopback
-    buendia-backup /dev/loop0 &
+    buendia-backup /dev/loop0 | tee backup.log &
     sleep 0.1
+    if grep skip backup.log; then
+        echo "Backup was skipped; test can't be evaluated"
+        return 0
+    fi
     if buendia-backup /dev/loop0; then
         echo "Simultaneous backup should be prevented"
         return 1
diff --git a/packages/buendia-backup/data/usr/share/buendia/tests/50-backup-to-external-safe b/packages/buendia-backup/data/usr/share/buendia/tests/50-backup-to-external-safe
index 539120a6..4ffb5390 100755
--- a/packages/buendia-backup/data/usr/share/buendia/tests/50-backup-to-external-safe
+++ b/packages/buendia-backup/data/usr/share/buendia/tests/50-backup-to-external-safe
@@ -16,15 +16,24 @@ test_10_run_backup_cron () {
 
 # Then it stores a tarball of site configs on the external device
 test_20_cron_saved_site_config () {
-    tar tfz $BUENDIA_TEST_BACKUP_TARGET/buendia.tar.gz
+    cp $BUENDIA_TEST_BACKUP_TARGET/buendia.tar.gz.enc .
+    decrypt_file buendia.tar.gz
+    tar tfz buendia.tar.gz
 }
 
 # And it stores a database dump on the external device
 test_20_cron_saved_database_dump () {
-    unzip -t $BUENDIA_TEST_BACKUP_TARGET/openmrs.zip
+    cp $BUENDIA_TEST_BACKUP_TARGET/openmrs.zip.enc .
+    decrypt_file openmrs.zip
+    unzip -t openmrs.zip
 }
 
 # And it stores a package listing on the external device
 test_20_cron_saved_package_list () {
     [ -s $BUENDIA_TEST_BACKUP_TARGET/buendia.list ]
 }
+
+# And it stores a copy of the system key
+test_30_cron_saved_system_key () {
+    [ "$(cat /usr/share/buendia/system.key)" = "$(cat loop/system.key)" ]
+}
diff --git a/packages/buendia-backup/data/usr/share/buendia/tests/60-backup-and-restore b/packages/buendia-backup/data/usr/share/buendia/tests/60-backup-and-restore
index 8df9dc05..b0228666 100755
--- a/packages/buendia-backup/data/usr/share/buendia/tests/60-backup-and-restore
+++ b/packages/buendia-backup/data/usr/share/buendia/tests/60-backup-and-restore
@@ -31,6 +31,11 @@ test_50_confirm_changed_patient_list () {
 }
 
 test_60_restore_from_backup () {
+    # Replace the current system key, to ensure that the restore process
+    # correctly pulls in the one we just backed up.
+    openssl rand -hex 128 > /usr/share/buendia/system.key
+
+    # Now try restoring!
     execute_cron_right_now backup
 }
 
diff --git a/packages/buendia-mysql/control/control.template b/packages/buendia-mysql/control/control.template
index ba7b43e7..a1d074a7 100644
--- a/packages/buendia-mysql/control/control.template
+++ b/packages/buendia-mysql/control/control.template
@@ -2,6 +2,6 @@ Package: ${PACKAGE_NAME}
 Version: ${PACKAGE_VERSION}
 Architecture: all
 Pre-Depends: buendia-utils
-Depends: buendia-monitoring, cron-daemon, mysql-server, sysvinit-utils, unzip, zip
+Depends: buendia-monitoring, cron-daemon, mariadb-server-10.1 (>= 10.1.4), sysvinit-utils, unzip, zip
 Description: MySQL server configured for Buendia
 Maintainer: projectbuendia.org
diff --git a/packages/buendia-mysql/control/postinst b/packages/buendia-mysql/control/postinst
index c58b4b58..da6348e9 100755
--- a/packages/buendia-mysql/control/postinst
+++ b/packages/buendia-mysql/control/postinst
@@ -14,6 +14,28 @@ set -e; . /usr/share/buendia/utils.sh
 
 case $1 in
     configure)
+        if [ ! -r /etc/mysql/keyfile.enc ]; then
+            # Key #1 is required by MariaDB for encrypting system data. It will be used
+            # for other purposes as well, if no other keys are defined.
+            # https://mariadb.com/kb/en/encryption-key-management/#using-multiple-encryption-keys
+            ( echo -n '1;'; openssl rand -hex 32 ) > /etc/mysql/keyfile
+
+            # Encrypt the keyfile using the Buendia system key and remove the plaintext version
+            openssl enc -aes-256-cbc -md sha1 -in /etc/mysql/keyfile -out /etc/mysql/keyfile.enc \
+                    -pass file:/usr/share/buendia/system.key
+            rm /etc/mysql/keyfile
+        fi
+
+        # Ensure that MariaDB can read the encrypted key.
+        chown root:mysql /etc/mysql/keyfile.enc
+        chmod 640 /etc/mysql/keyfile.enc
+
+        # Ensure that MariaDB can read the system key.
+        chown root:mysql /usr/share/buendia/system.key
+        chmod 640 /usr/share/buendia/system.key
+
+        service mysql restart
+
         buendia-reconfigure mysql
         service cron start
         ;;
diff --git a/packages/buendia-mysql/control/preinst b/packages/buendia-mysql/control/preinst
index 9d9e8bd5..d3ab65d6 100755
--- a/packages/buendia-mysql/control/preinst
+++ b/packages/buendia-mysql/control/preinst
@@ -13,6 +13,9 @@
 set -e; . /usr/share/buendia/utils.sh
 
 case $1 in
-    install|upgrade) service_if_exists cron stop ;;
+    install|upgrade) 
+        service_if_exists cron stop
+        service_if_exists mysql stop
+        ;;
     *) exit 1
 esac
diff --git a/packages/buendia-mysql/data/etc/mysql/conf.d/buendia-mysql b/packages/buendia-mysql/data/etc/mysql/conf.d/buendia-mysql.cnf
similarity index 100%
rename from packages/buendia-mysql/data/etc/mysql/conf.d/buendia-mysql
rename to packages/buendia-mysql/data/etc/mysql/conf.d/buendia-mysql.cnf
diff --git a/packages/buendia-mysql/data/etc/mysql/mariadb.conf.d/60-encryption.cnf b/packages/buendia-mysql/data/etc/mysql/mariadb.conf.d/60-encryption.cnf
new file mode 100644
index 00000000..652a4e52
--- /dev/null
+++ b/packages/buendia-mysql/data/etc/mysql/mariadb.conf.d/60-encryption.cnf
@@ -0,0 +1,19 @@
+[mariadb]
+plugin_load_add = file_key_management
+
+# The table encryption secret gets *itself* encrypted at rest.
+# See https://mariadb.com/kb/en/file-key-management-encryption-plugin/#creating-the-key-file
+file_key_management_filename = /etc/mysql/keyfile.enc
+file_key_management_filekey = FILE:/usr/share/buendia/system.key
+
+# AES_CBC is used by default. AES_CTR is preferred, but not supported in older MariaDB builds.
+# https://mariadb.com/kb/en/file-key-management-encryption-plugin/#choosing-an-encryption-algorithm
+# file_key_management_encryption_algorithm = AES_CTR
+
+# InnoDB/XtraDB Encryption
+# https://mariadb.com/kb/en/innodb-encryption-overview/#basic-configuration
+# The 'FORCE' option requires all InnoDB tables to be encrypted.
+innodb_encrypt_tables = FORCE
+innodb_encrypt_log = ON
+innodb_encryption_threads = 4
+innodb_encryption_rotate_key_age = 1
diff --git a/packages/buendia-mysql/data/usr/share/buendia/tests/30-mysql-encryption-safe b/packages/buendia-mysql/data/usr/share/buendia/tests/30-mysql-encryption-safe
new file mode 100755
index 00000000..f39f76bf
--- /dev/null
+++ b/packages/buendia-mysql/data/usr/share/buendia/tests/30-mysql-encryption-safe
@@ -0,0 +1,14 @@
+test_10_openmrs_tables_are_encrypted () {
+    # MariaDB starts encrypting tables on a background thread when encryption
+    # is enabled. As a result, there's no obvious way to know when the job is
+    # done. This test makes the assumption that if at least one of the OpenMRS
+    # tables is encrypted, then the server background thread is doing its work
+    # and all OpenMRS tables will be encrypted eventually.
+
+    sudo mysql -s openmrs >table_count <<End
+        SELECT COUNT(*) FROM information_schema.INNODB_TABLESPACES_ENCRYPTION
+            WHERE name LIKE "openmrs/%"
+            AND encryption_scheme = 1;
+End
+    [ "$(cat table_count)" -gt 0 ]
+}
diff --git a/packages/buendia-utils/control/control.template b/packages/buendia-utils/control/control.template
index 9c249c5c..9658fef3 100644
--- a/packages/buendia-utils/control/control.template
+++ b/packages/buendia-utils/control/control.template
@@ -1,6 +1,6 @@
 Package: ${PACKAGE_NAME}
 Version: ${PACKAGE_VERSION}
 Architecture: all
-Depends: coreutils, cron-daemon, curl, perl
+Depends: coreutils, cron-daemon, curl, perl, openssl
 Description: Utility scripts for Buendia
 Maintainer: projectbuendia.org
diff --git a/packages/buendia-utils/control/postinst b/packages/buendia-utils/control/postinst
index 502f5600..019d0ed3 100755
--- a/packages/buendia-utils/control/postinst
+++ b/packages/buendia-utils/control/postinst
@@ -14,6 +14,11 @@ set -e; . /usr/share/buendia/utils.sh
 
 case $1 in
     configure)
+        if [ ! -f /usr/share/buendia/system.key ]; then
+            openssl rand -hex 128 > /usr/share/buendia/system.key
+            chmod 600 /usr/share/buendia/system.key
+        fi
+
         if [ -d /usr/share/buendia/systemd ]; then
             cp /usr/share/buendia/systemd/* /lib/systemd/system
             systemctl enable reboot-check.timer
diff --git a/packages/buendia-utils/data/usr/share/buendia/utils.sh b/packages/buendia-utils/data/usr/share/buendia/utils.sh
index b9f70a97..197a058c 100644
--- a/packages/buendia-utils/data/usr/share/buendia/utils.sh
+++ b/packages/buendia-utils/data/usr/share/buendia/utils.sh
@@ -52,5 +52,18 @@ function external_file_systems() {
     done
 }
 
+# Encrypt a file using the system key, given the input file name.
+function encrypt_file() {
+    openssl enc -aes-256-cbc -md sha256 -salt -in $1 -out $1.enc \
+        -pass file:/usr/share/buendia/system.key
+}
+
+# Decrypt a file using the system key, given the expected output filename. The
+# input filename is assumed to be the output filename with '.enc' appended.
+function decrypt_file() {
+    openssl enc -d -aes-256-cbc -md sha256 -in $1.enc -out $1 \
+        -pass file:/usr/share/buendia/system.key
+}
+
 # A handy shortcut, just for typing convenience.
 usb=usr/share/buendia
diff --git a/packages/tests/check-filenames b/packages/tests/check-filenames
index e172ed77..71da5694 100755
--- a/packages/tests/check-filenames
+++ b/packages/tests/check-filenames
@@ -20,6 +20,8 @@ for dir in $(find data -name '*.d'); do
     case $dir:$PACKAGE_NAME in
         data/etc/apt/sources.list.d:*) name=buendia*.list ;;
         data/etc/udev/rules.d:*) continue ;;
+        data/etc/mysql/mariadb.conf.d:*) continue ;;
+        data/etc/mysql/conf.d:*) name=${PACKAGE_NAME}.cnf ;;
         data/usr/share/buendia/*:buendia-site-*) name=site ;;
         data/usr/share/buendia/config.d:*) name="??-${PACKAGE_NAME#buendia-}" ;;
         data/usr/share/buendia/*:*) name=${PACKAGE_NAME#buendia-} ;;