Free cookie consent management tool by TermsFeed

Topology Changes

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:

  1. Backup your data: Always create a full system backup before starting the migration process
  2. Plan for downtime: The migration process requires shutting down your Appian deployment
  3. Verify storage requirements: Ensure you have adequate storage space for the migration
  4. 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

  1. Shut down your standalone Appian site
  2. 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.
  1. Apply the temporary HA configuration and wait for the init containers to complete
  2. Force Delete the HA custom resource once migration init containers finish

Step 7: Complete HA Deployment

  1. 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
  2. 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

  1. Shut down your HA Appian site
  2. 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.
  1. Apply the temporary standalone Appian custom resource
  2. Wait for init containers to complete
  3. Force delete the temporary migration custom resource

Step 3: Complete Standalone Deployment

  1. Remove the migration and sleep initContainers from your standalone custom resource
  2. 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

  1. Init containers stuck: Check pod logs for specific error messages:

    1
    
    kubectl -n <appian-namespace> logs <pod-name> -c migrate-topology
    
  2. Insufficient storage: Verify persistent volume sizes meet requirements before starting migration

  3. Permission issues: Ensure init containers run with appropriate security context (runAsUser: 500)

  4. 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

  1. Always backup: Create full system backups before starting any topology migration
  2. Test in non-production: Practice the migration process in a development environment first
  3. Use haExistingClaim: Configure RWX storage even for standalone deployments to simplify future migrations
  4. Monitor resources: Ensure adequate CPU, memory, and storage throughout the migration process
  5. Plan maintenance windows: Schedule migrations during planned maintenance windows to minimize user impact

Feedback