diff --git a/tests/assets/ssl/ca_certificate.pem b/tests/assets/ssl/ca_certificate.pem
new file mode 100644
index 00000000..d7c868ce
--- /dev/null
+++ b/tests/assets/ssl/ca_certificate.pem
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFKzCCAxOgAwIBAgIUS8kwSkcTL5/wZ9ga+63M8k1OjwIwDQYJKoZIhvcNAQEL
+BQAwJTEjMCEGA1UEAwwaVGVtcG9yYXJ5LU1haWwtSW4tQS1Cb3gtQ0EwHhcNMjAw
+NjI1MTQyMzAzWhcNMzAwNjIzMTQyMzAzWjAlMSMwIQYDVQQDDBpUZW1wb3Jhcnkt
+TWFpbC1Jbi1BLUJveC1DQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+AOkVAYh97toG9M8XvGIbYsblHoKw55ckeBrEa2sP0ik1lgeGdxasmlJsr9QjCZ8E
+u6Ak1+0LkRWeFerGPcc4M7vUXH/h3hFsYo3Q7mNGGTG+ZOn5ETZWlwFo4szcwqPQ
+TTTmbEQeRq3ZOY1d0/IqzfE0t2m480KgLq3UxrRX1jOe3p4pegJTw4k732nvKoI2
+aHqRiZfdxYpQStmIP/H+n4sqQAsXvK233/5kYCI+RbFcS3J3tKJGlqlK5aR18Z8V
+RwWGeUPTBulq7JuIVX8nReM9kNXkDjkThYDz7H1JPH3QhWGLfJIPJP/YUGa9h7aC
+uJmsVvj9ai6mSaZqLGICfU5n/+q5RRJd3YrSJDoWmLcAURrW+BwqHhMYg0WxRvag
+SKIWXaPczhIlJyoszqXmuSx6TpIhgSSXQs8t+2X/AXkXe40lLjxVSq+aQeJUZpuz
+6/gb2tEDzVZaLezPpnVTPJntSgNF0pR6HirufD2F68lCb7oxzXacv6Oku53/fUu7
+AMOdoQ+oxe11Oin+a3kVN50lqlYOlKo0TTaGID1xU3aHml4M72HgAjW/TcMQ78sy
+gUuz2rECF9nJuynip7Sz6aJnVokDpoLqGPtN0pt5TAelzX+joabgxWEKg9R+0IhN
+XOUxGf/UqVQ5hPwUrQuV59soahO04UREfpDB90MJ9WHvAgMBAAGjUzBRMB0GA1Ud
+DgQWBBSIXhhPVX5XX4DNulLcDVTiZpwmNzAfBgNVHSMEGDAWgBSIXhhPVX5XX4DN
+ulLcDVTiZpwmNzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBz
+JpqfcOmBg28VZpaoqhWRB12C2jfRhz/62sGLDp3lsQq+BdGvzy0hbS3gA1UxUJE+
+kkdk8qD0OV3tyRuzk2IKdqNLy9GtzBUR6GR5O/iFsk2pRrQPRL+obWq+Y2JUgf67
+QRoPuLgkTAuLnUMxJglqy7+MLoatEkQQ/2wKrAOl+swT53juyASuwO2GvG0sZO+T
+4i5u2+NAqlqmGFBOSzLFXuLMqjeksw9e/tkbQ6rm8WEFXvXWnOkYg8GugPrd2IGi
+Kcj0mf6/OsgmQGmenEjHRTCVr0zW+pywVaS4h/+PIdOlrQo60EW2e1lU9p8NB5Rv
+AeNGaViHpJ7jzOp7UnZcUAfupHwCEtUEqaDPGZNFR29dTVMcPkGoRBOVWy+ENgP5
+FPrP2221et4rIEDnL79/WP3OqEPbE/WAkeojKovIevIA62rvUgPVXUIS1YFzLkIA
+AV2dIDnN6ERPTowUgQzehXCnvcgN5tJUYX2NtPyRQJUrQRGvmUEJS7HIpnQMfM15
+MplNom3kvmYc81J52EhdsuX0VrlnQZwbdicZ2ZIzRci452LNY0PKgl/q8b45Yr9F
+ZfSgPKYKQ25E1pIerqWcUrJZemlIhsx4OKGEXiwniJLPlH5uUzwBCyeUQPJRPhEF
+ZkTgGOq1m0vlSfjtCxSdrqFSu0E03yeHhem4uFfYgg==
+-----END CERTIFICATE-----
diff --git a/tests/assets/ssl/ca_private_key.pem b/tests/assets/ssl/ca_private_key.pem
new file mode 100644
index 00000000..59c5509d
--- /dev/null
+++ b/tests/assets/ssl/ca_private_key.pem
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,5C78DCFA3A2168C7E6D9A39C2233B9A2
+
+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
+-----END RSA PRIVATE KEY-----
diff --git a/tests/system-setup/remote-nextcloud-docker.sh b/tests/system-setup/remote-nextcloud-docker.sh
index f9beb5c7..28cf517d 100755
--- a/tests/system-setup/remote-nextcloud-docker.sh
+++ b/tests/system-setup/remote-nextcloud-docker.sh
@@ -142,19 +142,14 @@ install_nextcloud_docker() {
 do_upgrade() {
     local populate_name="$1"
 
-    if [ -e "$LOCAL_MODS_DIR/remote-nextcloud.sh" ]; then
-        # we install w/o remote nextcloud first so we can add
-        # a user w/contacts and ensure the contact exists in the
-        # new system
-        if [ ! -L "$LOCAL_MODS_DIR/remote-nextcloud.sh" ]; then
-            echo "Warning: $LOCAL_MODS_DIR/remote-nextcloud.sh is a regular file - should be a symlink"
-        fi
-        die "Error: $LOCAL_MODS_DIR/remote-nextcloud.sh exists - delete it and try again"
-    fi
-
     # initialize test system
     init
 
+    # we install w/o remote nextcloud first so we can add
+    # a user w/contacts and ensure the contact exists in the
+    # new system
+    disable_miab_mod "remote-nextcloud"
+
     # install w/o remote Nextcloud
     miab_ldap_install
     
diff --git a/tests/system-setup/setup-funcs.sh b/tests/system-setup/setup-funcs.sh
index 12837dda..3e053450 100755
--- a/tests/system-setup/setup-funcs.sh
+++ b/tests/system-setup/setup-funcs.sh
@@ -123,16 +123,31 @@ init_miab_testing() {
     
     # copy in pre-built MiaB-LDAP ssl files
     #   1. avoid the lengthy generation of DH params
-    mkdir -p $STORAGE_ROOT/ssl \
-        || (echo "Unable to create $STORAGE_ROOT/ssl ($?)" && rc=1)
-    cp tests/assets/ssl/dh2048.pem $STORAGE_ROOT/ssl \
-        || (echo "Copy dhparams failed ($?)" && rc=1)
+    if ! mkdir -p $STORAGE_ROOT/ssl; then
+        echo "Unable to create $STORAGE_ROOT/ssl ($?)"
+        rc=1
+    fi
+    echo "Copy dhparams"
+    if ! cp tests/assets/ssl/dh2048.pem $STORAGE_ROOT/ssl; then
+        echo "Copy failed ($?)"
+        rc=1
+    fi
+        
+    if array_contains "--qa-ca" "$@"; then
+        echo "Copy certificate authority"
+        if ! cp tests/assets/ssl/ca_*.pem $STORAGE_ROOT/ssl; then
+            echo "Copy failed ($?)"
+            rc=1
+        fi
+    fi
 
     # create miab_ldap.conf to specify what the Nextcloud LDAP service
     # account password will be to avoid a random one created by start.sh
     if [ ! -z "$LDAP_NEXTCLOUD_PASSWORD" ]; then
-        mkdir -p $STORAGE_ROOT/ldap \
-            || (echo "Could not create $STORAGE_ROOT/ldap" && rc=1)
+        if ! mkdir -p $STORAGE_ROOT/ldap; then
+            echo "Could not create $STORAGE_ROOT/ldap"
+            rc=1
+        fi
         [ -e $STORAGE_ROOT/ldap/miab_ldap.conf ] && \
             echo "Warning: exists: $STORAGE_ROOT/ldap/miab_ldap.conf" 1>&2
         touch $STORAGE_ROOT/ldap/miab_ldap.conf || rc=1
@@ -161,6 +176,12 @@ enable_miab_mod() {
     fi
 }
 
+disable_miab_mod() {
+    local name="${1}.sh"
+    rm -f "$LOCAL_MODS_DIR/$name"
+}
+
+
 tag_from_readme() {
     # extract the recommended TAG from README.md
     # sets a global "TAG"
diff --git a/tests/system-setup/vanilla.sh b/tests/system-setup/vanilla.sh
new file mode 100755
index 00000000..0f5bd3e9
--- /dev/null
+++ b/tests/system-setup/vanilla.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+#
+# setup a "plain vanilla" system from scratch
+#
+
+# ensure working directory
+if [ ! -d "tests/system-setup" ]; then
+    echo "This script must be run from the MiaB root directory"
+    exit 1
+fi
+
+# load helper scripts
+. "tests/lib/all.sh" "tests/lib" || die "Could not load lib scripts"
+. "tests/system-setup/setup-defaults.sh" || die "Could not load setup-defaults"
+. "tests/system-setup/setup-funcs.sh" || die "Could not load setup-funcs"
+
+# ensure running as root
+if [ "$EUID" != "0" ]; then
+    die "This script must be run as root (sudo)"
+fi
+
+
+init() {
+    H1 "INIT"
+    init_test_system
+    init_miab_testing "$@" || die "Initialization failed"
+}
+
+
+# initialize test system
+init "$@"
+
+if array_contains remote-nextcloud "$@"; then
+    H1 "Enable remote-nextcloud mod"
+    enable_miab_mod "remote-nextcloud" \
+        || die "Could not enable remote-nextcloud mod"
+else
+    disable_miab_mod "remote-nextcloud"
+fi
+    
+# run setup to use the remote Nextcloud (doesn't need to be available)
+miab_ldap_install
+