Provider Migrations

This document describes the steps for migrating openDesk from one cloud service provider to another. For the sake of simplicity this document assumes that the Kubernetes-Cluster is an off-the-shelf cluster of said CSP, hence no K8S-Migration occurs. We also assume that in-cluster persistence services are used (e.g. S3, PostgreSQL,…). CSP-Services may behave differently but follow similar patterns.

Warning

Provider migrations are complex operations. Plan the migration during a maintenance window and communicate expected downtime to users.

Note

The describes approach is generic. However, the data-flow is heavily based on a central S3-Bucket. High capacity migrations ( e.g. many TB ) may require a performance focused solution.

Guidelines & Suggestions

The following values have proven effective in practice. Adjust based on your infrastructure and requirements.

SettingSuggested ValueRationale
DNS TTL~300sShort enough for quick transition, long enough for DNS provider compatibility and debugging
Source retention7-14 daysSufficient window to detect post-migration issues
K8up snapshot retention≥14 daysAllows point-in-time recovery after migration
Maintenance window prepAnnounce 1-2 weeks aheadUsers can plan accordingly
Port-forward stabilityUse bastion/jump hostAvoids unstable local connections during long operations

Timeline & Overview

What to Migrate

1. Configuration Files

FilePurpose
databases.yaml.gotmplDatabase connection settings
objectstores.yaml.gotmplS3/object storage config
cache.yaml.gotmplCache service config
secrets.yaml.gotmplAll secrets and passwords
smtp.yaml.gotmplSMTP/mail relay config

2. Database Credentials

Important

All database credentials are derived from the master password using the Master Password algorithm. As long as the same master password is used in the target deployment, all derived credentials will match automatically.

Database credentials in openDesk work as follows:

  1. Technical usernames (e.g., keycloak_user) are defined in databases.yaml.gotmpl
  2. Database passwords are derived from MASTER_PASSWORD via the Master Password algorithm in secrets.yaml.gotmpl
  3. To migrate: Use the same MASTER_PASSWORD in target deployment, then migrate only the actual data
Note

If you must change the master password, you would need to regenerate all derived passwords manually - this is not covered in this migration guide.

3. Managed Databases (CSP Services)

openDesk uses managed database services (PostgreSQL, MariaDB) from the CSP:

DatabaseComponentData
PostgreSQLKeycloak (Nubus), ArgoCD, Nextcloud, OpenProject, XWikiUsers, settings, configs
MariaDBOX App SuiteGroupware data, contacts, calendar
Cassandra (EE)Dovecot ProMetadata and ACLs (dovecot_dictmap, dovecot_acl)
RedisNextcloud, OX App Suite, OpenProjectSession and data caching

Migration approach:

  • PostgreSQL: Use pg_dump via port-forward for logical exports
  • MariaDB: Use mysqldump via port-forward for logical exports
  • Cassandra (EE-only): Export using nodetool snapshot or cqlsh COPY commands
  • Redis: Not typically backed up (cache only); see Data Storage documentation
Note

Cassandra (EE-only): Used by Dovecot Pro for metadata and ACL storage. Ensure the target Cassandra service is compatible before migration.

Note: For CSP-managed databases, ensure the target service is created with compatible versions before data migration.

4. Physical Volumes (PVCs)

Application PersistentVolumeClaims store user data and application state:

PVCComponentContentBackup
clamav-database-clamav-simple-0ClamAVClamAV DatabaseOptional
dovecotDovecot (CE)User mail directoriesYes
var-lib-dovecot-dovecot-0Dovecot (EE)Metacache directoryYes
media-opendesk-synapse-0Matrix/SynapseAttachmentsYes
matrix-neodatefix-botMatrix BotSync and state dataYes
ox-connector-*OX ConnectorCaching of OX object dataOptional
openproject-filesOpenProjectProject attachmentsYes
ox-dataOX App SuiteGroupware data, attachmentsYes
nextcloud-dataNextcloudUser files, versioning, trashYes
shared-data-ums-ldap-server-primary-0LDAP (primary)openLDAP databaseYes
shared-data-ums-ldap-server-secondary-0LDAP (secondary)openLDAP databaseNo
data-ums-provisioning-nats-0NATSQueued provisioning objectsYes
xwiki-data-xwiki-0XWikiAttachmentsYes
postfixPostfixMail spoolYes
data-cassandra-*Cassandra (EE)Dovecot-Pro meta-dataYes

Migration approach: Use kubectl get pvc -o yaml to export PVC definitions, then use storage provider tools or kubectl cp to migrate data.

Note

Cassandra (EE-only): Cassandra appears as an external service but is commonly backed up via PVC. Ensure the Cassandra PVC (data-cassandra-*) is migrated for complete Dovecot Pro metrics recovery.

Note

LDAP Restoration: When restoring LDAP, select the primary instance (shared-data-ums-ldap-server-primary-0). Secondary instances sync from primary.

Alternative: For storage provider migrations, consider using storage-specific migration tools or CSI driver features.

5. LDAP Data

Export OpenLDAP directory from the ums-ldap-server-primary service.

Secret Service: ums-ldap-server-admin (password key)

Base DN: Get from pod environment (LDAP_BASE_DN), default: dc=swp-ldap,dc=internal

6. Object Storage (S3 Buckets)

BucketComponentContentEE-only
nextcloudNextcloudUser files, versioning, trashNo
openprojectOpenProjectAttachments, custom stylesNo
openxchangeOX App SuiteAttachments of meetings, contacts, tasksNo
elementmediaElementUploaded images, filesNo
dovecotDovecot ProUser mail (encrypted)Yes
umsPortalStatic files for PortalNo

Migration approach: Use s3 sync with server-side encryption considerations.

Important

Dovecot Pro (EE-only): Uses encryption keys stored in DOVECOT_CRYPT_KEYS to encrypt object storage (S3/Minio) data. Without the original encryption keys, emails cannot be decrypted and users will see empty mailboxes with “Decryption error: no private key available” errors.

Migration steps:

  1. Migrate the DOVECOT_CRYPT_KEYS secret to the target cluster
  2. Sync S3 bucket containing dovecot/ prefix data
  3. Run doveadm metacache rescan to repopulate metacache from S3

For detailed Dovecot Pro configuration, see Dovecot Pro Admin Guide.

Important

Migrating the S3-Backend ( e.g. changing the Bucket is currently not supported ). Nextcloud for example stores S3-Bucket information within the Database.

7. Redis Cache

Redis instances are used for session and data caching (Nextcloud, OX App Suite, OpenProject). Redis data is ephemeral and typically not backed up.

If you need to preserve session state during migration:

kubectl exec -n ${NAMESPACE} redis-master-0 -- redis-cli SAVE
kubectl exec -n ${NAMESPACE} redis-master-0 -- cat /data/dump.rdb

8. Kubernetes Secrets

Migrate all Kubernetes Secrets from the source namespace to ensure credentials, API keys, and certificates are available in the target cluster.

Alternatively, use the K8up approach:

  • The PreBackupPod secrets-pre-backup-pod exports all secrets as JSON
  • This can be restored using K8up Restore CR

9. DNS Records

Record TypePurpose
A/CNAMEMain ingress, subdomains
MXMail delivery
TXT: SPFAuthorized mail servers
TXT: DKIMEmail signing keys
TXT: DMARCEmail authentication policy
SRVService discovery

For detailed backup strategies including K8up, Restic, and rclone configurations, see the Backup & Recovery documentation.

Migration Steps

Step 1: Reduce DNS TTLs (days before)

Consider setting DNS TTL to ~300 seconds for faster propagation and easier rollback.

Many openDesks are managed by delegated DNS-Zones. Zone-TTLs are often quite high. Depending on what exactly is migrated ( zone or record ) the shortened TTL may be the zone or the records.

Step 2: Build New Infrastructure

  1. Use existing configuration files to deploy openDesk on new provider. openDesk is domain-centric, hence a Test-Deployment on a different domain may be used to verify the deployment generally works but needs to be re-deployed with the correct domain for a later migration.
  2. Verify base deployment works.

Most openDesk setups require LetsEncrypt for certificate handling. Transfer the LE Secrets. Alternatively the DNS-Challenge may issue valid certificates to an offline-cluster.

Note

Domain migration with shared services: Migrating from domain foo.de to bar.de is currently not supported. For Backup-Tests we highly recommend DNS-Spoofing instead.

Some Steps needed for Domain-Change( not exhaustive )

  • The base domain becomes bar.de
  • matrixDomain and mailDomain should point to foo.de (original domain)

Step 3: Maintenance Window - Disable Ingress

Consider disabling all ingresses to prevent new write traffic during migration.

Note

The nginx.ingress.kubernetes.io/server-snippet annotation breaks Collabora ingresses because they already use a server-snippet. If you need to abort the migration, the Collabora ingress must be redeployed. Consider scaling down write-heavy services instead.

Important

Ensure all ingresses are disabled before starting data export to prevent data inconsistencies between source and target.

Note

Run migration commands from an infrastructure-near system (e.g., a jump host or bastion) rather than your local machine to ensure stable connections.

Step 4: Export All Data

Export all databases, LDAP, object storage, PVCs, and Kubernetes Secrets to the migration S3 bucket.

Key export commands:

  • PostgreSQL: pg_dump via port-forward
  • MariaDB: mysqldump via port-forward
  • Cassandra: nodetool snapshot or cqlsh COPY
  • LDAP: ldapsearch via port-forward
  • Object Storage: aws s3 sync
  • PVCs: kubectl get pvc -o yaml + storage provider tools
  • Secrets: kubectl get secrets -o json

Step 5: Import All Data

Restore all databases, LDAP, object storage, PVCs, and Kubernetes Secrets to the target cluster.

Key import commands:

  • PostgreSQL: pg_restore via port-forward
  • MariaDB: mysql via port-forward
  • Cassandra: nodetool refresh or cqlsh COPY
  • LDAP: slapadd (stop StatefulSet first)
  • Object Storage: aws s3 sync
  • PVCs: Recreate PVCs and restore data
  • Secrets: kubectl apply -f

Step 6: Nameserver Cutover

The nameserver switch approach allows you to prepare DNS changes before the migration window, significantly reducing downtime.

DNS Records to Migrate

RecordSourceTargetNotes
A/CNAMEOld provider’s DNS zoneNew provider’s DNS zoneMain ingress IP or alias
MXOld provider’s DNS zoneNew provider’s DNS zoneMail delivery target
SPFOld provider’s DNS zoneNew provider’s DNS zoneTXT record with v=spf1 ...
DKIMOpenDKIM/Keycloak podNew DNS zoneTXT record with v=DKIM1...
DMARCOld provider’s DNS zoneNew provider’s DNS zoneTXT record with _dmarc. prefix

Copy SPF Record

  1. From: Query current DNS at old provider
    dig TXT yourdomain.com +short
    # or
    nslookup -type=TXT yourdomain.com
  2. To: Add identical TXT record at new DNS provider
  3. Verify: dig TXT yourdomain.com +short after propagation

Copy DKIM Record

  1. From: Extract public key from OpenDKIM pod or Keycloak
    # Option A: From OpenDKIM pod
    kubectl exec -n ${SOURCE_NS} opendkim-0 -c main -- cat /etc/opendkim/keys/${DOMAIN}/mail.txt
     
    # Option B: From Keycloak realm settings (if DKIM is managed there)
    # Check realm settings in Keycloak admin UI under Email -> DKIM
  2. To: Add as TXT record at new DNS provider
    • Selector: match what your current _domainkey.yourdomain.com shows (often default or mail)
    • Content: The full v=DKIM1; p=... string
  3. Verify: dig TXT default._domainkey.yourdomain.com +short

Copy DMARC Record

  1. From: Query current DNS
    dig TXT _dmarc.yourdomain.com +short
  2. To: Add identical TXT record at new DNS provider with prefix _dmarc
  3. Verify: dig TXT _dmarc.yourdomain.com +short

Execution Order

  1. Before migration window (preparation): Copy all records to new DNS provider but keep old nameservers active
  2. During migration window: Switch authoritative nameservers to new provider
  3. Wait for TTL propagation (typically 5-30 minutes)
Important

MX, SPF, DKIM, and DMARC are critical for email deliverability. Verify each record matches exactly after migration.

Step 7: Post-Migration Restart

Consider restarting applications in a logical order (databases first, then services) to ensure proper initialization.

Step 8: Verification

Consider testing using DNS-resolved hostname or test accounts.

Test Accounts

Local DNS Testing

For testing before DNS propagation, add entries to /etc/hosts:

<CLUSTER_IP> files.yourdomain.de
<CLUSTER_IP> ics.yourdomain.de
<CLUSTER_IP> id.yourdomain.de
<CLUSTER_IP> objectstore.yourdomain.de
<CLUSTER_IP> dav.yourdomain.de
<CLUSTER_IP> webmail.yourdomain.de
<CLUSTER_IP> yourdomain.de
<CLUSTER_IP> adminbot.yourdomain.de
<CLUSTER_IP> office.yourdomain.de
<CLUSTER_IP> projects.yourdomain.de
<CLUSTER_IP> chat.yourdomain.de
<CLUSTER_IP> meet.yourdomain.de
<CLUSTER_IP> matrix.yourdomain.de
<CLUSTER_IP> wiki.yourdomain.de
<CLUSTER_IP> pad.yourdomain.de
<CLUSTER_IP> portal.yourdomain.de

Step 9: Open to Users

  1. Allow access to new environment
  2. Monitor for issues
  3. Consider keeping source cluster running for 7-14 days (rollback option)

Verification Checklist

Authentication & Identity

TestDescription
SSO LoginLogin via Keycloak
LDAP SyncCheck user/group sync
2FATest two-factor authentication
Password ResetTest self-service

Mail System

TestDescription
Mail Inbound/OutboundSend/receive email
IMAP/SMTPMail client access
Calendar SharingShare calendar

Applications

TestDescription
NextcloudLogin, upload/download files
OX App SuiteWebmail, calendar, contacts
OpenProjectView/create work packages
XWikiCreate/edit pages
ElementSend messages
JitsiVideo meeting

Federation & Integrations

TestDescription
Matrix FederationExternal Matrix room
CalDAV SubscriptionExternal calendar
FilepickerOX to Nextcloud
OpenProject-NextcloudLink files

Data Integrity & DNS

TestDescription
User FilesVerify Nextcloud files
Email HistoryCheck old emails
Calendar EventsVerify past events
Project DataCheck projects
DNS RecordsA, MX, SPF, DKIM, DMARC
CertificatesCheck TLS validity

Migration-Specific Tests

TestDescription
Secrets RestoreVerify all Kubernetes Secrets migrated correctly
Database ConnectionsConfirm applications connect to databases
S3 AccessVerify object storage access for all components
LDAP UsersTest login with existing users
Backup IntegrityRun K8up Check job to verify backup repository

Matrix Neodate Bot Recovery

If the matrix-neodatefix-bot pod fails to start after migration with M_UNKNOWN_TOKEN error, the Matrix credentials need to be regenerated:

# Delete the affected secrets and helm charts
cd helmfile/apps/element
helmfile destroy -l name=matrix-neodatefix-bot-bootstrap
helmfile destroy -l name=opendesk-matrix-user-verification-bootstrap
 
# Redeploy to regenerate credentials
helmfile apply -l name=matrix-neodatefix-bot-bootstrap
helmfile apply -l name=opendesk-matrix-user-verification-bootstrap
Note

Consider running automated restore tests in a separate environment before production migration to validate backup integrity.

RTO Calculation

Cloud Interconnect (1 Gbps) migration times with safety factor (1.4×) and overhead (~45 min):

Data SizeTransfer TimeWith Overhead
10 GB~2 minutes~50 minutes
100 GB~20 minutes~1.5 hours
1000 GB~3 hours~4.5 hours

Formula used: ((Data_Size_GB × 8) / 1000) × 2 × 1.4 + 0.75 (hours)

Rollback

If migration fails:

  1. Keep DNS pointing to source
  2. Keep source cluster running
  3. Investigate and retry
  4. Use K8up Restore jobs to re-sync any partial data transfers
Note

Consider retaining source infrastructure for 7-14 days after successful migration. Suggest keeping K8up snapshots available for at least 14 days to enable point-in-time recovery if issues are discovered post-migration.