View this page in the latest version of Appian. Topology Changes Share Share via LinkedIn Reddit Email Copy Link Print On This Page Overview This guide provides step-by-step instructions for migrating your Appian deployment between standalone and high availability (HA) topologies. You can migrate your Appian deployment in either direction: Standalone to HA: Scale up from a single-replica deployment to a multi-replica high availability deployment HA to Standalone: Scale down from a multi-replica high availability deployment to a single-replica deployment Both migration paths preserve your application data and configuration while changing the underlying topology. Caution: Do not upgrade and make a topology change at the same time. Finish each one, including restarting your Appian site, before starting the next. Caution: Changing Kafka topology (single-node to high availability, or high availability to single-node) may block subsequent topology changes if performed too soon after the previous topology change. Wait at least one to two months after a topology change before performing another. Prerequisites Before beginning any topology migration: Backup your data: Always create a full system backup before starting the migration process Plan for downtime: The migration process requires shutting down your Appian deployment Verify storage requirements: Ensure you have adequate storage space for the migration Review Kubernetes resources: Confirm you have sufficient cluster resources for the target topology Important Considerations Persistent Volume Claims: The migration process reuses existing persistent volumes where possible to minimize data movement Same Namespace: These instructions assume the topology migration will occur in the same namespace as the Appian site. This allows the PVCs to be reused ReadWriteMany (RWX) Storage: Using an haExistingClaim RWX persistent volume claim simplifies topology changes in both directions Standalone to High Availability Migration Step 1: Shut Down and Backup Shut down your standalone Appian site Create a full system backup following your standard backup procedures Step 2: Prepare Storage If you don't currently use an haExistingClaim If webapp and service manager data currently do not use an haExistingClaim, create a ReadWriteMany (RWX) persistent volume claim: 1 2 3 4 5 6 7 8 9 10 11 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: appian-ha-shared-storage spec: accessModes: - "ReadWriteMany" storageClassName: <your-rwx-storage-class> resources: requests: storage: "<size>Gi" The RWX persistent volume must be large enough to contain data from these directories: Webapp directories: /usr/local/appian/ae/_admin/ /usr/local/appian/ae/server/msg/ /usr/local/appian/ae/server/archived-process/ Service Manager directories: /usr/local/appian/ae/server/channels/gw1/ /usr/local/appian/ae/server/collaboration/gw1/ /usr/local/appian/ae/server/forums/gw1/ /usr/local/appian/ae/server/notifications/gw1/ /usr/local/appian/ae/server/personalization/gw1/ /usr/local/appian/ae/server/portal/gw1/ /usr/local/appian/ae/server/process/analytics/0000/gw1/ /usr/local/appian/ae/server/process/analytics/0001/gw1/ /usr/local/appian/ae/server/process/analytics/0002/gw1/ /usr/local/appian/ae/server/process/design/gw1/ /usr/local/appian/ae/server/process/exec/00/gw1/ /usr/local/appian/ae/server/process/exec/01/gw1/ /usr/local/appian/ae/server/process/exec/02/gw1/ Additional analytics and exec directories may be present, depending on the number of analytics and execution engine shards configured. Create temporary migration storage Create a temporary RWX persistent volume claim for the migration process: 1 2 3 4 5 6 7 8 9 10 11 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: appian-topology-migration spec: accessModes: - "ReadWriteMany" storageClassName: <your-rwx-storage-class> resources: requests: storage: "<size>Gi" This temporary storage needs capacity for search server, data server, and Kafka data. It can be deleted after the migration completes. Step 3: Migrate Data from Standalone Create a temporary Appian custom resource with the following init containers. These init containers will copy data from the standalone site to the RWX PVCs and prevent the site services from starting up while the data migration occurs. This must be named the same as the original Appian custom resource. For sites already using haExistingClaim 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 apiVersion: k8s.appian.com/v1beta1 kind: Appian metadata: name: <appian-name> spec: # ... your existing configuration ... searchServer: initContainers: - name: migrate-topology image: <repository>/search-server:<version> command: - /bin/sh - "-c" - "rm -rf /usr/local/appian/ae/search-server/data/_state; cp -r /usr/local/appian/ae/search-server/data/* /ha-data" securityContext: runAsNonRoot: true runAsUser: 500 volumeMounts: - name: storage mountPath: /usr/local/appian/ae/search-server/data subPath: ae/search-server/data - name: migration-storage mountPath: /ha-data subPath: search-server - name: sleep image: <repository>/search-server:<version> command: - /bin/sh - "-c" - "trap 'exit 0' SIGTERM; sleep infinity & wait" securityContext: runAsNonRoot: true runAsUser: 500 additionalVolumes: - name: migration-storage persistentVolumeClaim: claimName: appian-topology-migration dataServer: initContainers: - name: migrate-topology image: <repository>/data-server:<version> command: - /bin/sh - "-c" - "rm -rf /usr/local/appian/ae/data-server/node/election; cp -r /usr/local/appian/ae/data-server/data/* /ha-data" securityContext: runAsNonRoot: true runAsUser: 500 volumeMounts: - mountPath: /usr/local/appian/ae/data-server name: storage subPath: ae/data-server - name: migration-storage mountPath: /ha-data subPath: data-server - name: sleep image: <repository>/data-server:<version> command: - /bin/sh - "-c" - "trap 'exit 0' SIGTERM; sleep infinity & wait" securityContext: runAsNonRoot: true runAsUser: 500 additionalVolumes: - name: migration-storage persistentVolumeClaim: claimName: appian-topology-migration kafka: initContainers: - name: migrate-kafka image: <repository>/appian-kafka-server:<version> command: - /bin/sh - -c - rm -rf /ha-data/kafka-logs; cp -r /usr/local/appian/ae/kafka/data/kafka-logs /ha-data/kafka-logs securityContext: runAsNonRoot: true runAsUser: 500 volumeMounts: - name: storage mountPath: /usr/local/appian/ae/kafka/data/kafka-logs subPath: ae/kafka/data/kafka-logs - name: migration-storage mountPath: /ha-data subPath: kafka - name: sleep image: <repository>/appian-kafka-server:<version> command: - /bin/sh - -c - trap 'exit 0' SIGTERM; sleep infinity & wait securityContext: runAsNonRoot: true runAsUser: 500 additionalVolumes: - name: migration-storage persistentVolumeClaim: claimName: appian-topology-migration # Add sleep init containers to ALL OTHER COMPONENTS to prevent them from starting zookeeper: initContainers: - name: sleep image: <repository>/zookeeper:<version> command: - /bin/sh - "-c" - "trap 'exit 0' SIGTERM; sleep infinity & wait" securityContext: runAsNonRoot: true runAsUser: 500 # Repeat sleep init containers for serviceManager, webapp, etc. For sites not using haExistingClaim Add the above configuration plus these additional init containers for webapp and service manager: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 serviceManager: initContainers: - name: migrate-topology image: <repository>/service-manager:<version> command: - /bin/sh - "-c" - "cp -r /usr/local/appian/ae/services/data/server/* /ha-data" securityContext: runAsNonRoot: true runAsUser: 500 volumeMounts: - name: distributed mountPath: /ha-data subPath: services/data/server - name: storage mountPath: /usr/local/appian/ae/services/data/server subPath: ae/services/data/server - name: sleep image: <repository>/service-manager:<version> command: - /bin/sh - "-c" - "trap 'exit 0' SIGTERM; sleep infinity & wait" securityContext: runAsNonRoot: true runAsUser: 500 additionalVolumes: - name: distributed persistentVolumeClaim: claimName: appian-ha-shared-storage webapp: initContainers: - name: migrate-topology image: <repository>/webapp:<version> command: - /bin/sh - "-c" - "cp -r /usr/local/appian/ae/_admin/* /ha-admin; cp -r /usr/local/appian/ae/server/archived-process /ha-server; cp -r /usr/local/appian/ae/server/msg /ha-server" securityContext: runAsNonRoot: true runAsUser: 500 volumeMounts: - name: distributed mountPath: /ha-admin subPath: _admin - name: distributed mountPath: /ha-server subPath: server - name: storage mountPath: /usr/local/appian/ae/_admin subPath: ae/_admin - name: storage mountPath: /usr/local/appian/ae/server subPath: ae/server - name: sleep image: <repository>/webapp:<version> command: - /bin/sh - "-c" - "trap 'exit 0' SIGTERM; sleep infinity & wait" securityContext: runAsNonRoot: true runAsUser: 500 additionalVolumes: - name: distributed persistentVolumeClaim: claimName: appian-ha-shared-storage Step 4: Monitor Migration Progress Apply the migration custom resource and monitor the init containers: 1 2 kubectl -n <appian-namespace> apply -f appian-temporary-migration-cr.yaml kubectl -n <appian-namespace> get pods -w Wait for the migration init containers to complete. The Init:1/2 status means the migration init container finished. Pods without the migration init container will have a status of Init:0/1 1 2 3 4 5 6 NAME READY STATUS RESTARTS AGE <appian-name>-search-server-0 0/1 Init:1/2 0 2m <appian-name>-data-server-0 0/1 Init:1/2 0 2m <appian-name>-kafka-0 0/1 Init:1/2 0 2m <appian-name>-service-manager-groups-0 0/1 Init:1/2 0 2m <appian-name>-zookeeper-0 0/1 Init:0/1 0 2m Step 5: Clean Up Migration Deployment Once the migration init containers complete, manually delete the migration custom resource. Wait for the pods to be deleted before proceeding. 1 2 kubectl -n <appian-namespace> delete appian <appian-name> --wait=false kubectl -n <appian-namespace> patch appian <appian-name> --type='json' -p='[{"op": "remove", "path": "/metadata/finalizers"}]' Step 6: Deploy Temporary HA Custom Resource Create your HA Appian custom resource with init containers to restore the migrated data. At this point, webapp and service manager must use the shared ReadWriteMany PVC. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 apiVersion: k8s.appian.com/v1beta1 kind: Appian metadata: name: <appian-name> spec: # ... your HA configuration ... searchServer: initContainers: - name: migrate-topology image: <repository>/search-server:<version> command: - /bin/sh - "-c" - "cp -r /ha-data/* /usr/local/appian/ae/search-server/data" securityContext: runAsNonRoot: true runAsUser: 500 volumeMounts: - name: storage mountPath: /usr/local/appian/ae/search-server/data subPath: ae/search-server/data - name: migration-storage mountPath: /ha-data subPath: search-server - name: sleep image: <repository>/search-server:<version> command: - /bin/sh - "-c" - "trap 'exit 0' SIGTERM; sleep infinity & wait" securityContext: runAsNonRoot: true runAsUser: 500 additionalVolumes: - name: migration-storage persistentVolumeClaim: claimName: appian-topology-migration dataServer: initContainers: - name: migrate-topology image: <repository>/data-server:<version> command: - /bin/sh - "-c" - "cp -r /ha-data/* /usr/local/appian/ae/data-server/data" securityContext: runAsNonRoot: true runAsUser: 500 volumeMounts: - mountPath: /usr/local/appian/ae/data-server/data name: storage subPath: ae/data-server/data - name: migration-storage mountPath: /ha-data subPath: data-server - name: sleep image: <repository>/data-server:<version> command: - /bin/sh - "-c" - "trap 'exit 0' SIGTERM; sleep infinity & wait" securityContext: runAsNonRoot: true runAsUser: 500 additionalVolumes: - name: migration-storage persistentVolumeClaim: claimName: appian-topology-migration kafka: initContainers: - name: migrate-kafka image: <repository>/appian-kafka-server:<version> command: - /bin/sh - -c - cp -r /ha-data/kafka-logs/. /usr/local/appian/ae/kafka/data/kafka-logs securityContext: runAsNonRoot: true runAsUser: 500 volumeMounts: - name: storage mountPath: /usr/local/appian/ae/kafka/data/kafka-logs subPath: ae/kafka/data/kafka-logs - name: migration-storage mountPath: /ha-data subPath: kafka - name: sleep image: <repository>/appian-kafka-server:<version> command: - /bin/sh - -c - trap 'exit 0' SIGTERM; sleep infinity & wait securityContext: runAsNonRoot: true runAsUser: 500 additionalVolumes: - name: migration-storage persistentVolumeClaim: claimName: appian-topology-migration # Add sleep init containers to ALL OTHER COMPONENTS to prevent them from starting zookeeper: initContainers: - name: sleep image: <repository>/zookeeper:<version> command: - /bin/sh - "-c" - "trap 'exit 0' SIGTERM; sleep infinity & wait" securityContext: runAsNonRoot: true runAsUser: 500 # Repeat sleep init containers for serviceManager, webapp, etc. Apply the temporary HA configuration and wait for the init containers to complete Force Delete the HA custom resource once migration init containers finish Step 7: Complete HA Deployment Remove the migration and sleep initContainers and additionalVolumes from your HA custom resource. At this point, the Appian CR should be a finished Highly Available CR, without any references to the data migration Redeploy the clean HA configuration Step 8: Post-Migration Clean Up Delete the temporary migration storage: 1 kubectl -n <appian-namespace> delete pvc appian-topology-migration High Availability to Standalone Migration Step 1: Shut Down and Backup Shut down your HA Appian site Take a full system backup Step 2: Deploy Temporary Standalone Site Create a temporary Appian custom resource with the following init containers. These init containers will clear out some data that is incompatible with standalone sites and prevent the site services from starting up. The Appian CR must have the same name as the original. Webapp and service manager must also reference the same RWX PVC under haExistingClaim. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 apiVersion: k8s.appian.com/v1beta1 kind: Appian metadata: name: <appian-name> spec: # ... your standalone configuration ... searchServer: initContainers: - name: migrate-topology image: <repository>/search-server:<version> command: - /bin/sh - "-c" - "rm -rf /usr/local/appian/ae/search-server/data/_state" securityContext: runAsNonRoot: true runAsUser: 500 volumeMounts: - name: storage mountPath: /usr/local/appian/ae/search-server/data subPath: ae/search-server/data - name: sleep image: <repository>/search-server:<version> command: - /bin/sh - "-c" - "trap 'exit 0' SIGTERM; sleep infinity & wait" securityContext: runAsNonRoot: true runAsUser: 500 dataServer: initContainers: - name: migrate-topology image: <repository>/data-server:<version> command: - /bin/sh - "-c" - "rm -rf /usr/local/appian/ae/data-server/node/election" securityContext: runAsNonRoot: true runAsUser: 500 volumeMounts: - mountPath: /usr/local/appian/ae/data-server/node name: storage subPath: ae/data-server/node - name: sleep image: <repository>/data-server:<version> command: - /bin/sh - "-c" - "trap 'exit 0' SIGTERM; sleep infinity & wait" securityContext: runAsNonRoot: true runAsUser: 500 # Add sleep init containers to ALL OTHER COMPONENTS to prevent them from starting zookeeper: initContainers: - name: sleep image: <repository>/zookeeper:<version> command: - /bin/sh - "-c" - "trap 'exit 0' SIGTERM; sleep infinity & wait" securityContext: runAsNonRoot: true runAsUser: 500 # Repeat sleep init containers for serviceManager, kafka, webapp, etc. Apply the temporary standalone Appian custom resource Wait for init containers to complete Force delete the temporary migration custom resource Step 3: Complete Standalone Deployment Remove the migration and sleep initContainers from your standalone custom resource Redeploy the clean standalone Appian custom resource Step 4: Clean Up Unused Resources Once your standalone site is running successfully, you can delete the persistent volume claims that were created for additional replicas. Just be sure the PVCs for running pods are not deleted. Troubleshooting Common Issues Init containers stuck: Check pod logs for specific error messages: 1 kubectl -n <appian-namespace> logs <pod-name> -c migrate-topology Insufficient storage: Verify persistent volume sizes meet requirements before starting migration Permission issues: Ensure init containers run with appropriate security context (runAsUser: 500) Component startup issues: Refer to KB-2346 if the webapp pod is unable to start and the following log messages keep repeating in the webapp pod logs: 1 2 3 [wait-for-component] INFO com.appiancorp.tools.WaitForServers - No ids have been propagated to analytics for <N> seconds [wait-for-component] INFO com.appiancorp.tools.WaitForServers - executionXX - remaining ids to propagate: 0 [wait-for-component] INFO com.appiancorp.tools.WaitForServers - Waiting for analytics to be ready... Best Practices Always backup: Create full system backups before starting any topology migration Test in non-production: Practice the migration process in a development environment first Use haExistingClaim: Configure RWX storage even for standalone deployments to simplify future migrations Monitor resources: Ensure adequate CPU, memory, and storage throughout the migration process Plan maintenance windows: Schedule migrations during planned maintenance windows to minimize user impact Feedback Was this page helpful? SHARE FEEDBACK Loading...