# How I Migrated an AKS Cluster Across Regions Using Velero

Migrating an entire Kubernetes cluster is one of those tasks that sounds straightforward until you actually begin. When I recently needed to migrate an Azure Kubernetes Service (AKS) cluster from the Central India region to the East US region, the process involved more considerations than simply exporting YAML files and applying them elsewhere.

The requirement was clear. Migrate every workload, every Persistent Volume Claim, every Secret, every ConfigMap, every Custom Resource Definition, and all namespace-level objects. Everything had to move with accuracy.

After evaluating different approaches, Velero turned out to be the most reliable and practical tool for a complete AKS region migration. Velero supports backup and restoration of cluster state as well as persistent storage, and its Azure plugin works smoothly with both Azure Blob Storage and Azure Disk snapshots.

This guide describes the exact steps I followed, the challenges I encountered, and the final workflow that resulted in a successful migration.

---

## **1\. Why Velero Is Ideal for AKS Region Migration**

Velero has several advantages for full-cluster migration:

### **Backup and restore of namespaced and cluster-level resources**

This includes Deployments, StatefulSets, Services, Secrets, ConfigMaps, RBAC objects, and Custom Resources.

### **Support for disk snapshots**

This is essential when migrating workloads that depend on Persistent Volume Claims.

### **Version compatibility with AKS**

Velero supports most Kubernetes API versions used in AKS.

### **Easy restoration into a completely new cluster**

This is useful when the source and destination are in different regions.

---

## **2\. Preparing Azure for Velero**

Velero requires an Azure Blob Storage account and a resource group for snapshot management.

### **Step 1: Create a Storage Account**

Choose a region for the storage account. You can use either the source or destination region because Velero backups are region independent.

```plaintext
az storage account create \
    --name veleroaccount123 \
    --resource-group velero-rg \
    --location eastus \
    --sku Standard_GRS
```

### **Step 2: Create a Blob Container**

```plaintext
az storage container create \
    --name velero-backups \
    --account-name veleroaccount123
```

### **Step 3: Create a Service Principal or Use a Managed Identity**

In my case, I used a managed identity created for Velero with the required permissions:

* Contributor role on the Resource Group
    
* Storage Blob Data Contributor on the Storage Account
    

This avoids the need for storing client secrets.

---

## **3\. Installing Velero on the Source AKS Cluster**

Once the storage and identity were ready, I installed Velero on the source cluster.

### **Step 1: Install Velero CLI**

```plaintext
brew install velero
```

(Or download from the official GitHub releases page if not using macOS.)

### **Step 2: Install Velero on the Cluster**

```plaintext
velero install \
    --provider azure \
    --plugins velero/velero-plugin-for-microsoft-azure:v1.10.0 \
    --bucket velero-backups \
    --secret-file ./credentials-velero \
    --backup-location-config resourceGroup=velero-rg,storageAccount=veleroaccount123 \
    --use-volume-snapshots=true \
    --snapshot-location-config resourceGroup=velero-rg
```

Now Velero runs inside the cluster with all necessary permissions.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1764095088526/7284a72c-a334-479d-9b00-a3b0a27e9587.png align="center")

---

## **4\. Creating Backups**

I wanted a complete backup of every namespace, every CRD, and all persistent volumes.

### **Step 1: Confirm Velero Is Healthy**

```plaintext
velero version
kubectl get pods -n velero
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1764095124616/bbf5426f-dc11-47ad-abe9-faab600ae076.png align="center")

### **Step 2: Run a Full Cluster Backup**

```plaintext
velero backup create aks-full-backup \
    --include-namespaces '*' \
    --wait
```

The backup took several minutes because the cluster had StatefulSets and large PVCs.

### **Step 3: Verify the Backup**

```plaintext
velero backup describe aks-full-backup
velero backup logs aks-full-backup
```

At this stage I had a complete backup stored in Azure Blob Storage and snapshots created for all PVCs.

---

## **5\. Creating the Destination AKS Cluster**

I created a new cluster in East US with the same Kubernetes version as the source cluster. Matching the Kubernetes version is important because restoring cluster-level objects may otherwise cause compatibility issues.

```plaintext
az aks create \
  --resource-group uspreprod-rg \
  --name uspreprod-aks \
  --location eastus \
  --node-count 3 \
  --kubernetes-version 1.29
```

After the cluster was ready, I connected to it:

```plaintext
az aks get-credentials \
    --resource-group uspreprod-rg \
    --name uspreprod-aks \
    --overwrite-existing
```

---

## **6\. Installing Velero on the Destination Cluster**

The installation process is similar to the source cluster:

```plaintext
velero install \
    --provider azure \
    --plugins velero/velero-plugin-for-microsoft-azure:v1.10.0 \
    --bucket velero-backups \
    --secret-file ./credentials-velero \
    --backup-location-config resourceGroup=velero-rg,storageAccount=veleroaccount123 \
    --use-volume-snapshots=true \
    --snapshot-location-config resourceGroup=velero-rg
```

Velero now has access to the same storage account and snapshots that were created from the source cluster.

---

## **7\. Restoring the Full Backup**

### **Step 1: Trigger the Restore**

```plaintext
velero restore create aks-full-restore \
    --from-backup aks-full-backup \
    --wait
```

Velero recreated:

* Namespaces
    
* Deployments
    
* StatefulSets
    
* Services
    
* Ingress objects
    
* Secrets and ConfigMaps
    
* CRDs and CRs
    
* Everything linked to snapshots
    

### **Step 2: Verify Restore Objects**

```plaintext
velero restore describe aks-full-restore
velero restore logs aks-full-restore
```

### **Step 3: Validate Cluster State**

I validated that workloads came up correctly:

```plaintext
kubectl get pods --all-namespaces
kubectl get pvc --all-namespaces
kubectl get ingress --all-namespaces
```

Any workload that depended on Persistent Volumes was able to recover because the Azure snapshots were restored successfully.

---

## **8\. Issues Encountered and Fixes**

### **Missing CRDs Before Restore**

Some CRDs must exist before restoring their corresponding objects.  
Solution: Install CRDs manually or let Velero restore cluster-level CRDs first.

### **Snapshot Restore Delay**

Azure snapshots sometimes take time to rehydrate into new disks.  
Solution: Wait a few minutes and reapply StatefulSets if needed.

### **Identity Permission Issues**

The managed identity must have Contributor access on both resource groups.  
Without this, PVC restore will fail.

### **Ingress Controller Differences**

The new cluster may create a different external IP for the ingress controller.  
Update DNS records accordingly.

---

## **9\. Final Thoughts**

Migrating an AKS cluster across regions can feel overwhelming due to the number of moving parts involved. Velero simplifies the process significantly by offering a predictable and reliable way to back up and restore clusters at scale.

In my case, Velero successfully migrated every namespace, every workload, and every Persistent Volume from the Central India AKS cluster to a completely new cluster in East US. The process was clean, repeatable, and did not require manual recreation of YAML files.

If you are planning a similar migration, I strongly recommend preparing the destination cluster with the same Kubernetes version, ensuring proper identity permissions, and validating your snapshot restores.

Velero is a powerful tool, and with the right configuration, it can handle migrations across regions with very little manual effort.
