diff --git a/float/.gitrepo b/float/.gitrepo
index a9d16002fa477c515c5cc70d4f4d53bdeed610e5..1ccfe2b6526fd53d15e7a43e19bbd211ad7bbe61 100644
--- a/float/.gitrepo
+++ b/float/.gitrepo
@@ -6,7 +6,7 @@
 [subrepo]
 	remote = https://git.autistici.org/ai3/float.git
 	branch = master
-	commit = c2c4ad89ae7ad3f1f722bf4528e1057c377e2886
+	commit = 0735dbc8a83476504fdd6eabb8225663d953a781
 	parent = d9b2f97887292b92babad1990bd760c23e952416
-	cmdver = 0.4.3
+	cmdver = 0.4.5
 	method = merge
diff --git a/float/playbooks/init-credentials.yml b/float/playbooks/init-credentials.yml
index 555b0ba4c0967a9e6645ab0732821dc7a9ac075c..6c02e0ebaa9267eff1a2cd58bf2be118976ae113 100644
--- a/float/playbooks/init-credentials.yml
+++ b/float/playbooks/init-credentials.yml
@@ -28,7 +28,6 @@
         - dnssec
         - ssh
         - sso
-        - x509
 
     # First of all, generate secrets from the passwords.yml file.
     - name: Initialize secrets
@@ -50,8 +49,17 @@
     - name: Generate SSO credentials
       local_action: ed25519 privkey="{{ credentials_dir }}/sso/secret.key" pubkey="{{ credentials_dir }}/sso/public.key"
 
-    - name: Generate global DH params
-      local_action: command openssl dhparam -out "{{ credentials_dir }}/x509/dhparam" "{{ dhparam_bits | default('2048') }}" creates="{{ credentials_dir }}/x509/dhparam"
+    - set_fact:
+        default_x509_ca_list:
+          - {tag: x509}
+
+    - name: Create X509 CA directory
+      local_action: file path="{{ credentials_dir }}/{{ item.tag }}" state=directory
+      loop: "{{ x509_ca_list | default(default_x509_ca_list) }}"
 
     - name: Generate the X509 CA certificate
-      local_action: x509_ca ca_subject="{{ x509_ca_subject | default('CN=Service CA') }}" ca_cert_path="{{ credentials_dir }}/x509/ca.pem" ca_key_path="{{ credentials_dir }}/x509/ca_private_key.pem"
+      local_action: x509_ca ca_subject="{{ item.subject | default('CN=Service CA') }}" ca_cert_path="{{ credentials_dir }}/{{ item.tag }}/ca.pem" ca_key_path="{{ credentials_dir }}/{{ item.tag }}/ca_private_key.pem"
+      loop: "{{ x509_ca_list | default(default_x509_ca_list) }}"
+
+    - name: Generate global DH params
+      local_action: command openssl dhparam -out "{{ credentials_dir }}/x509/dhparam" "{{ dhparam_bits | default('2048') }}" creates="{{ credentials_dir }}/x509/dhparam"
diff --git a/float/roles/float-base-backup-metadata/handlers/main.yml b/float/roles/float-base-backup-metadata/handlers/main.yml
index 34aab05fd0469a26607b3b8d41b4faf2aceec8ba..68eff7aa14144e41a39ef8113500d2a4874da523 100644
--- a/float/roles/float-base-backup-metadata/handlers/main.yml
+++ b/float/roles/float-base-backup-metadata/handlers/main.yml
@@ -4,3 +4,6 @@
   systemd:
     name: tabacco-metadb.service
     state: restarted
+  # Allow failure when testing backups, the unit can't start until later.
+  ignore_errors: "{{ testing | default(True) }}"
+
diff --git a/float/roles/float-base-datasets/tasks/dataset_litestream.yml b/float/roles/float-base-datasets/tasks/dataset_litestream.yml
index 7e312408100365843fdbe187df6e2621b1e85c09..dd888126370b76fd4c38599db6825dc7391119e0 100644
--- a/float/roles/float-base-datasets/tasks/dataset_litestream.yml
+++ b/float/roles/float-base-datasets/tasks/dataset_litestream.yml
@@ -2,7 +2,6 @@
 
 - set_fact:
     dataset_filename: "{{ dataset.filename }}"
-    dataset_replica_url: "{{ backup_litestream_url | default('') }}/{{ dataset_tag }}"
     dataset_replication_unit: "replicate-{{ dataset_tag }}.service"
     # Just don't backup at all if litestream is not configured.
     dataset_should_backup: "{{ dataset_should_backup and (backup_litestream_config is defined) }}"
diff --git a/float/roles/float-base-datasets/templates/litestream-restore-script.j2 b/float/roles/float-base-datasets/templates/litestream-restore-script.j2
index 4d0d28a91aa0f8eada5134e61cb1fcf1d14e365b..f37e36dc622ebe65cf35be79a1e8f22cc1bb5bff 100644
--- a/float/roles/float-base-datasets/templates/litestream-restore-script.j2
+++ b/float/roles/float-base-datasets/templates/litestream-restore-script.j2
@@ -1,8 +1,11 @@
 #!/bin/sh
 
-{% if backup_litestream_url is defined %}
+{% if backup_litestream_config is defined %}
 # Restore the dataset {{ dataset_name }} using litestream.
 
+set -a
+. /etc/litestream/{{ dataset_tag }}.env
+
 /usr/bin/litestream restore --config=/etc/litestream/{{ dataset_tag }}.yml --if-replica-exists -v "{{ dataset_path }}/{{ dataset_filename }}"
 
 if [ $? -gt 0 ]; then
diff --git a/float/roles/float-base-datasets/templates/restore-service.j2 b/float/roles/float-base-datasets/templates/restore-service.j2
index 07801edba25c17acef7ca94a6de409ee0cf96da5..620f14017756e761cfaf0c0b78ae21e557488b8f 100644
--- a/float/roles/float-base-datasets/templates/restore-service.j2
+++ b/float/roles/float-base-datasets/templates/restore-service.j2
@@ -9,5 +9,8 @@ Type=oneshot
 RemainAfterExit=true
 ExecStart=/usr/lib/float/float-dataset-restore {{ dataset_tag }}
 
+Restart=on-failure
+RestartSec=10s
+
 [Install]
 RequiredBy={{ required_by | join(' ') }}
diff --git a/float/roles/float-base-docker/templates/run.sh.j2 b/float/roles/float-base-docker/templates/run.sh.j2
index 4e08780895806cf82445648b1549f021170ec20f..94be2ee771b22302b8b09e04d999ac93d0e77f56 100644
--- a/float/roles/float-base-docker/templates/run.sh.j2
+++ b/float/roles/float-base-docker/templates/run.sh.j2
@@ -79,7 +79,8 @@
 
 {# Mount the service credentials inside the container. #}
 {% for creds in services[item.service].get('service_credentials', []) %}
-  {{ opt('mount', 'type=bind,source=/etc/credentials/x509/' + creds.name + ',destination=/etc/credentials/x509/' + creds.name) }}
+  {%- set credentials_path='/etc/credentials/' + (creds.ca_tag | default('x509')) + '/' + creds.name %}
+  {{ opt('mount', 'type=bind,source=' + credentials_path + ',destination=' + credentials_path) }}
 {% endfor %}
 
 {# Security options (unless root=True) #}
diff --git a/float/roles/float-base-service-credentials/meta/main.yml b/float/roles/float-base-service-credentials/meta/main.yml
index a60e6df6f945b1626bb4718b6db4e1eb5627e2c4..8da7fb7bba169ce4e09aba7a217220dd2d972383 100644
--- a/float/roles/float-base-service-credentials/meta/main.yml
+++ b/float/roles/float-base-service-credentials/meta/main.yml
@@ -4,4 +4,6 @@ dependencies:
   - role: float-util-credentials
     vars:
       credentials: "{{ float_host_service_credentials }}"
+      ca_tag: "x509"
+
 
diff --git a/float/roles/float-base/tasks/main.yml b/float/roles/float-base/tasks/main.yml
index af7e3324bde7d6a0a4537c4c13cdfab83175ac45..4567974337170270f6e3089a69283edd387917cf 100644
--- a/float/roles/float-base/tasks/main.yml
+++ b/float/roles/float-base/tasks/main.yml
@@ -40,6 +40,14 @@
 - include_tasks: rollback_protection.yml
   when: "git_revision != 'none' and not testing|default(True)"
 
+# Detect virtual machines / physical hardware.
+- name: Detect virtual machine
+  slurp:
+    src: "/sys/class/dmi/id/sys_vendor"
+  register: slurp_sysfs_dmi_vendor
+- set_fact:
+    float_is_vm: "{{ slurp_sysfs_dmi_vendor['content'] | b64decode == 'QEMU' }}"
+
 # Create the /usr/lib/float and /var/lib/float directories for
 # internal scripts.
 - file:
diff --git a/float/roles/float-base/templates/ssh/sshd_config.j2 b/float/roles/float-base/templates/ssh/sshd_config.j2
index 73a5610ac463e8a890d886440986ca367307a310..faa706f5f73bbe45bda6dc4190093f283b80cfa2 100644
--- a/float/roles/float-base/templates/ssh/sshd_config.j2
+++ b/float/roles/float-base/templates/ssh/sshd_config.j2
@@ -13,6 +13,7 @@ HostCertificate /etc/ssh/ssh_host_{{ key_type }}_key-cert.pub
 {% endfor %}
 
 # Ciphers and MACs
+# (cipher order differs from the default)
 KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
 Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
 MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com
@@ -35,7 +36,6 @@ HostbasedAuthentication no
 ChallengeResponseAuthentication no
 PasswordAuthentication no
 UseDNS no
-#IgnoreUserKnownHosts yes
 
 # Makes ansible faster?
 PrintMotd no
@@ -49,7 +49,13 @@ ClientAliveInterval 120
 #AcceptEnv LANG LC_*
 
 # Be restrictive on forwarding / proxying.
+
+# Disable agent forwarding for the clients' safety.
 AllowAgentForwarding no
-AllowTcpForwarding no
-X11Forwarding no
-#PermitTunnel no
+
+# Reverse forwarding can lead to security issues due to manipulating
+# the network perimeter.
+AllowTcpForwarding local
+AllowStreamLocalForwarding local
+PermitListen none
+
diff --git a/float/roles/float-infra-log-collector/tasks/main.yml b/float/roles/float-infra-log-collector/tasks/main.yml
index c86da8dd5d85e208e80ce61462e42f9b27bc8d3e..e9bb796c721cdcfaa9bd9a8278b899a0f1dc49b5 100644
--- a/float/roles/float-infra-log-collector/tasks/main.yml
+++ b/float/roles/float-infra-log-collector/tasks/main.yml
@@ -49,5 +49,14 @@
   template:
     src: "rsyslog-collector.conf.j2"
     dest: "/etc/rsyslog-collector.conf"
+  vars:
+    rsyslog_port: 6514
+    rsyslog_exporter_port: 9105
+    rsyslog_elasticsearch_host: "127.0.0.1"
+    rsyslog_elasticsearch_port: 9200
+    rsyslog_tls_ca: "/etc/credentials/x509/log-collector/ca.pem"
+    rsyslog_tls_cert: "/etc/credentials/x509/log-collector/server/cert.pem"
+    rsyslog_tls_key: "/etc/credentials/x509/log-collector/server/private_key.pem"
+    rsyslog_tls_permittedpeer: "*.{{ domain }}"
   notify: "restart rsyslog-collector"
 
diff --git a/float/roles/float-infra-log-collector/templates/rsyslog-collector.conf.j2 b/float/roles/float-infra-log-collector/templates/rsyslog-collector.conf.j2
index 9adc7e16d69f1b381fd795f7a2d1e9725acef16e..16bd8fc3d7db581ed100e11432fdd9563fcf07c7 100644
--- a/float/roles/float-infra-log-collector/templates/rsyslog-collector.conf.j2
+++ b/float/roles/float-infra-log-collector/templates/rsyslog-collector.conf.j2
@@ -1,6 +1,7 @@
 
 global(
   maxMessageSize="64k"
+  workDirectory="/var/spool/rsyslog"
 )
 
 main_queue(
@@ -24,7 +25,7 @@ ruleset(name="process_stats") {
   action(
     type="omprog"
     name="to_exporter"
-    binary="/usr/bin/rsyslog_exporter -web.listen-address=:9105"
+    binary="/usr/bin/rsyslog_exporter -web.listen-address=:{{ rsyslog_exporter_port }}"
     queue.type="linkedlist"
     queue.workerThreads="1"
   )
@@ -165,8 +166,8 @@ ruleset(name="incoming"){
     if ($syslogfacility-text == "auth" and $programname == "audit") then {
       # Structured audit logs go to a dedicated Elasticsearch index.
       action(type="omelasticsearch"
-             server="127.0.0.1"
-             serverport="9200"
+             server="{{ rsyslog_elasticsearch_host }}"
+             serverport="{{ rsyslog_elasticsearch_port }}"
              template="esTemplateAudit"
              searchIndex="esIndexAudit"
              searchType="_doc"
@@ -179,7 +180,6 @@ ruleset(name="incoming"){
              queue.mindequeuebatchsize="100"
              queue.mindequeuebatchsize.timeout="3000"
              queue.filename="es-audit"
-             queue.spoolDirectory="/var/spool/rsyslog"
              action.resumeretrycount="-1")
     } else {
       # Extension point for rules applying to structured logs.
@@ -191,8 +191,8 @@ ruleset(name="incoming"){
       # Normal structured log present in the default syslog flow. Send
       # straight to Elasticsearch, skipping the log normalization step.
       action(type="omelasticsearch"
-             server="127.0.0.1"
-             serverport="9200"
+             server="{{ rsyslog_elasticsearch_host }}"
+             serverport="{{ rsyslog_elasticsearch_port }}"
              template="esTemplateJSON"
              searchIndex="esIndex"
              searchType="_doc"
@@ -205,7 +205,6 @@ ruleset(name="incoming"){
              queue.mindequeuebatchsize="100"
              queue.mindequeuebatchsize.timeout="3000"
              queue.filename="es-structured"
-             queue.spoolDirectory="/var/spool/rsyslog"
              action.resumeretrycount="-1")
     }
   } else if ($syslogfacility-text == "local3") then {
@@ -219,8 +218,8 @@ ruleset(name="incoming"){
       set $!request = "/sso_login?";
     }
     action(type="omelasticsearch"
-           server="127.0.0.1"
-           serverport="9200"
+           server="{{ rsyslog_elasticsearch_host }}"
+           serverport="{{ rsyslog_elasticsearch_port }}"
            template="esTemplateHTTP"
            searchIndex="esIndexHTTP"
            searchType="_doc"
@@ -233,7 +232,6 @@ ruleset(name="incoming"){
            queue.mindequeuebatchsize="100"
            queue.mindequeuebatchsize.timeout="3000"
            queue.filename="es-http"
-           queue.spoolDirectory="/var/spool/rsyslog"
            action.resumeretrycount="-1")
   } else {
     # Traditional syslog message. Run it through mmnormalize to
@@ -268,8 +266,8 @@ ruleset(name="incoming"){
     # valid and ES will refuse it.
     set $!ignore = "1";
     action(type="omelasticsearch"
-           server="127.0.0.1"
-           serverport="9200"
+           server="{{ rsyslog_elasticsearch_host }}"
+           serverport="{{ rsyslog_elasticsearch_port }}"
            template="esTemplate"
            searchIndex="esIndex"
            searchType="_doc"
@@ -282,7 +280,6 @@ ruleset(name="incoming"){
            queue.mindequeuebatchsize="100"
            queue.mindequeuebatchsize.timeout="3000"
            queue.filename="es-default"
-           queue.spoolDirectory="/var/spool/rsyslog"
            action.resumeretrycount="-1")
   }
 {% endif %}
@@ -295,14 +292,14 @@ module(
 
 input(
   type="imrelp"
-  port="6514"
+  port="{{ rsyslog_port }}"
   maxDataSize="64k"
   ruleset="incoming"
   tls="on"
   tls.compression="on"
-  tls.cacert="/etc/credentials/x509/log-collector/ca.pem"
-  tls.mycert="/etc/credentials/x509/log-collector/server/cert.pem"
-  tls.myprivkey="/etc/credentials/x509/log-collector/server/private_key.pem"
-  tls.permittedpeer="*.{{ domain }}" 
+  tls.cacert="{{ rsyslog_tls_ca }}"
+  tls.mycert="{{ rsyslog_tls_cert }}"
+  tls.myprivkey="{{ rsyslog_tls_key }}"
+  tls.permittedpeer="{{ rsyslog_tls_permittedpeer }}"
   tls.authmode="certvalid"
 )
diff --git a/float/roles/float-infra-sso-server/defaults/main.yml b/float/roles/float-infra-sso-server/defaults/main.yml
index c0f325cce85fd10d95ce0376c6472787c396a6f2..28c5317931e0b0c874e7140bc9a304326c02acb7 100644
--- a/float/roles/float-infra-sso-server/defaults/main.yml
+++ b/float/roles/float-infra-sso-server/defaults/main.yml
@@ -41,3 +41,7 @@ sso_service_ttls:
 # List of allowed Origins for CORS (URLs without path component).
 # These are not regular expressions, but you can use wildcards (*).
 sso_allowed_cors_origins: []
+
+# When are users asked to authenticate again? (seconds)
+sso_auth_session_lifetime: 43200
+
diff --git a/float/roles/float-infra-sso-server/handlers/main.yml b/float/roles/float-infra-sso-server/handlers/main.yml
index 07ab7643f3393959d68fa0a6ba652945c84d0162..8a883a84fa574111fa55d6a773a4419df4d52ccc 100644
--- a/float/roles/float-infra-sso-server/handlers/main.yml
+++ b/float/roles/float-infra-sso-server/handlers/main.yml
@@ -5,6 +5,8 @@
 
 - name: restart user-meta-server
   systemd: name=user-meta-server.service state=restarted
+  # Allow failure when testing backups, the unit can't start until later.
+  ignore_errors: "{{ testing | default(True) }}"
 
 - name: restart auth-server
   systemd: name=auth-server.service state=restarted
diff --git a/float/roles/float-infra-sso-server/templates/server.yml.j2 b/float/roles/float-infra-sso-server/templates/server.yml.j2
index d6360c42474a3bdc77c47a389cf6d068c06e92eb..827187a15aa880410374e676636d66f834c25941 100644
--- a/float/roles/float-infra-sso-server/templates/server.yml.j2
+++ b/float/roles/float-infra-sso-server/templates/server.yml.j2
@@ -28,7 +28,7 @@ allowed_services:
 allowed_cors_origins: {{ sso_allowed_cors_origins | to_json }}
 allowed_exchanges: {{ sso_allowed_exchanges | to_json }}
 service_ttls: {{ sso_service_ttls | to_json }}
-auth_session_lifetime: 43200
+auth_session_lifetime: {{ sso_auth_session_lifetime }}
 session_auth_key: "{{ sso_session_auth_secret }}"
 session_enc_key: "{{ sso_session_enc_secret }}"
 csrf_secret: "{{ sso_csrf_secret }}"
diff --git a/float/roles/float-util-credentials/README.md b/float/roles/float-util-credentials/README.md
index de7a8aa25a884c06ffb8fd242fbd6a107484fb64..88f16957806144e8de11a667f6daa6d705125e22 100644
--- a/float/roles/float-util-credentials/README.md
+++ b/float/roles/float-util-credentials/README.md
@@ -12,7 +12,7 @@ on the Ansible host.
 X509 credentials are stored in /etc/credentials/x509 under directories
 named after the services. Every service directory contains a copy of
 the public CA certificate, so it can be bind-mounted in a container
-easily.
+easily. There will be separate client and server certificates.
 
 Private keys have mode 440, are owned by root and by a dedicated group
 named *service*-credentials. When the service is actually installed,
@@ -24,3 +24,25 @@ list of entries specifying the desired credentials. This is already
 done once system-wide by the *float-credentials* role with the
 credentials automagically derived from the service definitions by
 *float*.
+
+## Multiple PKIs
+
+The role supports credentials from different PKI CAs, each identified
+by a separate *tag*, with *x509* being the tag of the default internal
+float CA.
+
+Additional PKIs are expected to have their CA credentials in the
+*credentials_dir*/*tag* local directory, and will have their
+certificates installed below /etc/credentials/*tag*.
+
+There are two ways, when invoking this role, to specify that a
+different CA from the default should be used:
+
+* By setting the *ca_tag* attribute in the *credentials* map of any of
+  the values passed in the *credentials* variable (yes that's
+  credentials nested twice). This is how float passes the
+  *service_credentials* metadata, so you can just set *ca_tag* there.
+* By setting the *ca_tag* variable in Ansible when including this
+  role, if you are creating certificates manually rather than relying
+  on *service_credentials*.
+  
diff --git a/float/roles/float-util-credentials/tasks/main.yml b/float/roles/float-util-credentials/tasks/main.yml
index 907f0fff481982de77e40d6d7d794528fc92e9c9..b7cf1fe8446feba87c3ae0db84af9dc65386f09c 100644
--- a/float/roles/float-util-credentials/tasks/main.yml
+++ b/float/roles/float-util-credentials/tasks/main.yml
@@ -16,8 +16,10 @@
   changed_when: false
   register: all_systemd_units
 
-# Get the credential names from the list of certs.
 - set_fact:
+    # Default CA name.
+    default_ca_tag: "{{ ca_tag | default('x509') }}"
+    # Get the credential names from the list of certs.
     credentials_names: "{{ credentials | map(attribute='credentials') | map(attribute='name') | unique | list }}"
 
 - name: "Create service credentials group"
@@ -28,18 +30,18 @@
 
 - name: "Create service credentials dirs"
   file:
-    path: "/etc/credentials/x509/{{ item }}"
+    path: "/etc/credentials/{{ item.credentials.ca_tag | default(default_ca_tag) }}/{{ item.credentials.name }}"
     state: directory
-  loop: "{{ credentials_names }}"
+  loop: "{{ credentials }}"
 
 - name: Copy CA
   copy:
-    src: "{{ credentials_dir }}/x509/ca.pem"
-    dest: "/etc/credentials/x509/{{ item }}/ca.pem"
+    src: "{{ local_ca_path | default(credentials_dir + '/' + (item.credentials.ca_tag | default(default_ca_tag))) }}/ca.pem"
+    dest: "/etc/credentials/{{ item.credentials.ca_tag | default(default_ca_tag) }}/{{ item.credentials.name }}/ca.pem"
     owner: root
     group: root
     mode: 0644
-  loop: "{{ credentials_names }}"
+  loop: "{{ credentials }}"
 
 # Create and sign all certificates in a series of loops (with some
 # unfortunately complex change-detection logic).
@@ -47,7 +49,7 @@
   block:
 
     - file:
-        path: "/etc/credentials/x509/{{ item.credentials.name }}/{{ item.mode }}"
+        path: "/etc/credentials/{{ item.credentials.ca_tag | default(default_ca_tag) }}/{{ item.credentials.name }}/{{ item.mode }}"
         state: directory
       loop: "{{ credentials }}"
 
@@ -57,9 +59,9 @@
         domain: "{{ domain }}"
         mode: "{{ item.mode }}"
         params: "{{ item.x509_params|default({}) }}"
-        private_key_path: "/etc/credentials/x509/{{ item.credentials.name }}/{{ item.mode }}/private_key.pem"
-        cert_path: "/etc/credentials/x509/{{ item.credentials.name }}/{{ item.mode }}/cert.pem"
-        ca_cert_path: "/etc/credentials/x509/{{ item.credentials.name }}/ca.pem"
+        private_key_path: "/etc/credentials/{{ item.credentials.ca_tag | default(default_ca_tag) }}/{{ item.credentials.name }}/{{ item.mode }}/private_key.pem"
+        cert_path: "/etc/credentials/{{ item.credentials.ca_tag | default(default_ca_tag) }}/{{ item.credentials.name }}/{{ item.mode }}/cert.pem"
+        ca_cert_path: "/etc/credentials/{{ item.credentials.ca_tag | default(default_ca_tag) }}/{{ item.credentials.name }}/ca.pem"
         check: true
       loop: "{{ credentials }}"
       check_mode: no
@@ -72,7 +74,7 @@
         domain: "{{ domain }}"
         mode: "{{ item.0.mode }}"
         params: "{{ item.0.x509_params|default({}) }}"
-        private_key_path: "/etc/credentials/x509/{{ item.0.credentials.name }}/{{ item.0.mode }}/private_key.pem"
+        private_key_path: "/etc/credentials/{{ item.0.credentials.ca_tag | default(default_ca_tag) }}/{{ item.0.credentials.name }}/{{ item.0.mode }}/private_key.pem"
         check: false
       when: "item.1.changed"
       loop: "{{ credentials | zip(x509_should_update.results) | list }}"
@@ -82,15 +84,15 @@
       x509_sign:
         csr: "{{ item.1.csr }}"
         mode: "{{ item.0.mode }}"
-        ca_cert_path: "{{ credentials_dir }}/x509/ca.pem"
-        ca_key_path: "{{ credentials_dir }}/x509/ca_private_key.pem"
+        ca_cert_path: "{{ local_ca_path | default(credentials_dir + '/' + (item.0.credentials.ca_tag | default(default_ca_tag))) }}/ca.pem"
+        ca_key_path: "{{ local_ca_path | default(credentials_dir + '/' + (item.0.credentials.ca_tag | default(default_ca_tag))) }}/ca_private_key.pem"
       when: "item.1.changed"
       loop: "{{ credentials | zip(x509_csr.results) | list }}"
       register: x509_sign
 
     - name: "Install the signed internal PKI certificates"
       copy:
-        dest: "/etc/credentials/x509/{{ item.0.credentials.name }}/{{ item.0.mode }}/cert.pem"
+        dest: "/etc/credentials/{{ item.0.credentials.ca_tag | default(default_ca_tag) }}/{{ item.0.credentials.name }}/{{ item.0.mode }}/cert.pem"
         content: "{{ item.1.cert }}"
         mode: 0644
       when: "item.1.changed"
@@ -98,7 +100,7 @@
 
     - name: "Set permissions on the private keys"
       file:
-        path: "/etc/credentials/x509/{{ item.credentials.name }}/{{ item.mode }}/private_key.pem"
+        path: "/etc/credentials/{{ item.credentials.ca_tag | default(default_ca_tag) }}/{{ item.credentials.name }}/{{ item.mode }}/private_key.pem"
         group: "{{ item.credentials.name }}-credentials"
         mode: 0640
       loop: "{{ credentials }}"
@@ -112,4 +114,3 @@
   rescue:
     - debug:
         msg: "Failed to set up one or more credentials"
-
diff --git a/float/test/backup.ref/site.yml b/float/test/backup.ref/site.yml
index 601f945b9ad12cbbbed11a7f58c93575500d8572..7d5a19411e68523a7156aa5b267443e40c2b91c2 100644
--- a/float/test/backup.ref/site.yml
+++ b/float/test/backup.ref/site.yml
@@ -7,4 +7,11 @@
     - name: Create the test bucket
       run_once: true
       command: "podman run --env MC_HOST_local=http://minio:miniopassword@backup:9000 --network host --rm quay.io/minio/mc mb local/backuptest"
+    - systemd:
+        name: tabacco-metadb.service
+        state: restarted
+    - systemd:
+        name: user-meta-server.service
+        state: restarted
+    - local_action: shell sleep 60