AZURE_RESOURCE_GROUP=<YOUR_RESOURCE_GROUP>
az group create --name $AZURE_RESOURCE_GROUP --location <YOUR_LOCATION>
AZURE_RESOURCE_GROUP=<YOUR_RESOURCE_GROUP>
az group create --name $AZURE_RESOURCE_GROUP --location <YOUR_LOCATION>
AZURE_RESOURCE_GROUP=<YOUR_RESOURCE_GROUP>
az group create --name $AZURE_RESOURCE_GROUP --location <YOUR_LOCATION>
AZURE_STORAGE_ACCOUNT=<YOUR_STORAGE_ACCOUNT>
az storage account create \ --name $AZURE_STORAGE_ACCOUNT \ --resource-group $AZURE_RESOURCE_GROUP \ --sku Standard_GRS \ --encryption-services blob \ --https-only true \ --kind BlobStorage \ --access-tier Hot
AZURE_STORAGE_ACCOUNT=<YOUR_STORAGE_ACCOUNT>
az storage account create \ --name $AZURE_STORAGE_ACCOUNT \ --resource-group $AZURE_RESOURCE_GROUP \ --sku Standard_GRS \ --encryption-services blob \ --https-only true \ --kind BlobStorage \ --access-tier Hot
AZURE_STORAGE_ACCOUNT=<YOUR_STORAGE_ACCOUNT>
az storage account create \ --name $AZURE_STORAGE_ACCOUNT \ --resource-group $AZURE_RESOURCE_GROUP \ --sku Standard_GRS \ --encryption-services blob \ --https-only true \ --kind BlobStorage \ --access-tier Hot
BLOB_CONTAINER=velero
az storage container create \ --name $BLOB_CONTAINER \ --public-access off \ --account-name $AZURE_STORAGE_ACCOUNT
BLOB_CONTAINER=velero
az storage container create \ --name $BLOB_CONTAINER \ --public-access off \ --account-name $AZURE_STORAGE_ACCOUNT
BLOB_CONTAINER=velero
az storage container create \ --name $BLOB_CONTAINER \ --public-access off \ --account-name $AZURE_STORAGE_ACCOUNT
AZURE_SUBSCRIPTION_ID=$(az account list --query '[?isDefault].id' -o tsv)
AZURE_TENANT_ID=$(az account list --query '[?isDefault].tenantId' -o tsv) az ad sp create-for-rbac \ --name "velero" \ --role "Contributor" \ --scopes /subscriptions/$AZURE_SUBSCRIPTION_ID \ --query '{clientId: appId, clientSecret: password, tenantId: tenant}'
AZURE_SUBSCRIPTION_ID=$(az account list --query '[?isDefault].id' -o tsv)
AZURE_TENANT_ID=$(az account list --query '[?isDefault].tenantId' -o tsv) az ad sp create-for-rbac \ --name "velero" \ --role "Contributor" \ --scopes /subscriptions/$AZURE_SUBSCRIPTION_ID \ --query '{clientId: appId, clientSecret: password, tenantId: tenant}'
AZURE_SUBSCRIPTION_ID=$(az account list --query '[?isDefault].id' -o tsv)
AZURE_TENANT_ID=$(az account list --query '[?isDefault].tenantId' -o tsv) az ad sp create-for-rbac \ --name "velero" \ --role "Contributor" \ --scopes /subscriptions/$AZURE_SUBSCRIPTION_ID \ --query '{clientId: appId, clientSecret: password, tenantId: tenant}'
AZURE_CLIENT_ID=$(az ad sp list --display-name "velero" --query '[0].appId' -o tsv)
AZURE_CLIENT_ID=$(az ad sp list --display-name "velero" --query '[0].appId' -o tsv)
AZURE_CLIENT_ID=$(az ad sp list --display-name "velero" --query '[0].appId' -o tsv)
az role assignment create \ --assignee $AZURE_CLIENT_ID \ --role "Storage Blob Data Contributor" \ --scope /subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$AZURE_RESOURCE_GROUP/providers/Microsoft.Storage/storageAccounts/$AZURE_STORAGE_ACCOUNT
az role assignment create \ --assignee $AZURE_CLIENT_ID \ --role "Storage Blob Data Contributor" \ --scope /subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$AZURE_RESOURCE_GROUP/providers/Microsoft.Storage/storageAccounts/$AZURE_STORAGE_ACCOUNT
az role assignment create \ --assignee $AZURE_CLIENT_ID \ --role "Storage Blob Data Contributor" \ --scope /subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$AZURE_RESOURCE_GROUP/providers/Microsoft.Storage/storageAccounts/$AZURE_STORAGE_ACCOUNT
AZURE_SUBSCRIPTION_ID=<YOUR_SUBSCRIPTION_ID>
AZURE_TENANT_ID=<YOUR_TENANT_ID>
AZURE_CLIENT_ID=<YOUR_CLIENT_ID>
AZURE_CLIENT_SECRET=<YOUR_CLIENT_SECRET>
AZURE_RESOURCE_GROUP=<YOUR_RESOURCE_GROUP>
AZURE_CLOUD_NAME=AzurePublicCloud
AZURE_ENVIRONMENT=AzurePublicCloud
AZURE_SUBSCRIPTION_ID=<YOUR_SUBSCRIPTION_ID>
AZURE_TENANT_ID=<YOUR_TENANT_ID>
AZURE_CLIENT_ID=<YOUR_CLIENT_ID>
AZURE_CLIENT_SECRET=<YOUR_CLIENT_SECRET>
AZURE_RESOURCE_GROUP=<YOUR_RESOURCE_GROUP>
AZURE_CLOUD_NAME=AzurePublicCloud
AZURE_ENVIRONMENT=AzurePublicCloud
AZURE_SUBSCRIPTION_ID=<YOUR_SUBSCRIPTION_ID>
AZURE_TENANT_ID=<YOUR_TENANT_ID>
AZURE_CLIENT_ID=<YOUR_CLIENT_ID>
AZURE_CLIENT_SECRET=<YOUR_CLIENT_SECRET>
AZURE_RESOURCE_GROUP=<YOUR_RESOURCE_GROUP>
AZURE_CLOUD_NAME=AzurePublicCloud
AZURE_ENVIRONMENT=AzurePublicCloud
kubectl create namespace velero
kubectl create namespace velero
kubectl create namespace velero
apiVersion: v1
kind: Secret
metadata: name: bsl-creds namespace: velero
type: Opaque
data: cloud: <BASE64_ENCODED_VALUE> # Encode the following as base64: # [default] # storageAccount: <YOUR_STORAGE_ACCOUNT> # storageAccountKey: <YOUR_STORAGE_ACCOUNT_KEY> # subscriptionId: <YOUR_SUBSCRIPTION_ID> # resourceGroup: <YOUR_RESOURCE_GROUP>
apiVersion: v1
kind: Secret
metadata: name: bsl-creds namespace: velero
type: Opaque
data: cloud: <BASE64_ENCODED_VALUE> # Encode the following as base64: # [default] # storageAccount: <YOUR_STORAGE_ACCOUNT> # storageAccountKey: <YOUR_STORAGE_ACCOUNT_KEY> # subscriptionId: <YOUR_SUBSCRIPTION_ID> # resourceGroup: <YOUR_RESOURCE_GROUP>
apiVersion: v1
kind: Secret
metadata: name: bsl-creds namespace: velero
type: Opaque
data: cloud: <BASE64_ENCODED_VALUE> # Encode the following as base64: # [default] # storageAccount: <YOUR_STORAGE_ACCOUNT> # storageAccountKey: <YOUR_STORAGE_ACCOUNT_KEY> # subscriptionId: <YOUR_SUBSCRIPTION_ID> # resourceGroup: <YOUR_RESOURCE_GROUP>
apiVersion: v1
kind: Secret
metadata: name: cloud-creds namespace: velero
type: Opaque
data: cloud: <BASE64_ENCODED_VALUE> # Encode the following as base64: # AZURE_SUBSCRIPTION_ID=<YOUR_SUBSCRIPTION_ID> # AZURE_TENANT_ID=<YOUR_TENANT_ID> # AZURE_CLIENT_ID=<YOUR_CLIENT_ID> # AZURE_CLIENT_SECRET=<YOUR_CLIENT_SECRET> # AZURE_RESOURCE_GROUP=<YOUR_RESOURCE_GROUP> # AZURE_CLOUD_NAME=AzurePublicCloud
apiVersion: v1
kind: Secret
metadata: name: cloud-creds namespace: velero
type: Opaque
data: cloud: <BASE64_ENCODED_VALUE> # Encode the following as base64: # AZURE_SUBSCRIPTION_ID=<YOUR_SUBSCRIPTION_ID> # AZURE_TENANT_ID=<YOUR_TENANT_ID> # AZURE_CLIENT_ID=<YOUR_CLIENT_ID> # AZURE_CLIENT_SECRET=<YOUR_CLIENT_SECRET> # AZURE_RESOURCE_GROUP=<YOUR_RESOURCE_GROUP> # AZURE_CLOUD_NAME=AzurePublicCloud
apiVersion: v1
kind: Secret
metadata: name: cloud-creds namespace: velero
type: Opaque
data: cloud: <BASE64_ENCODED_VALUE> # Encode the following as base64: # AZURE_SUBSCRIPTION_ID=<YOUR_SUBSCRIPTION_ID> # AZURE_TENANT_ID=<YOUR_TENANT_ID> # AZURE_CLIENT_ID=<YOUR_CLIENT_ID> # AZURE_CLIENT_SECRET=<YOUR_CLIENT_SECRET> # AZURE_RESOURCE_GROUP=<YOUR_RESOURCE_GROUP> # AZURE_CLOUD_NAME=AzurePublicCloud
kubectl apply -f bsl-creds.yaml -n velero
kubectl apply -f cloud-creds.yaml -n velero
kubectl apply -f bsl-creds.yaml -n velero
kubectl apply -f cloud-creds.yaml -n velero
kubectl apply -f bsl-creds.yaml -n velero
kubectl apply -f cloud-creds.yaml -n velero
configuration: backupStorageLocation: - name: default provider: azure bucket: velero config: resourceGroup: <YOUR_RESOURCE_GROUP> storageAccount: <YOUR_STORAGE_ACCOUNT> subscriptionId: <YOUR_SUBSCRIPTION_ID> credential: name: bsl-creds key: cloud volumeSnapshotLocation: - name: default provider: azure config: resourceGroup: <YOUR_RESOURCE_GROUP> subscriptionId: <YOUR_SUBSCRIPTION_ID> credential: name: cloud-creds key: cloud credentials: useSecret: true existingSecret: cloud-creds deployNodeAgent: true nodeAgent: podVolumePath: /var/lib/kubelet/pods privileged: true
configuration: backupStorageLocation: - name: default provider: azure bucket: velero config: resourceGroup: <YOUR_RESOURCE_GROUP> storageAccount: <YOUR_STORAGE_ACCOUNT> subscriptionId: <YOUR_SUBSCRIPTION_ID> credential: name: bsl-creds key: cloud volumeSnapshotLocation: - name: default provider: azure config: resourceGroup: <YOUR_RESOURCE_GROUP> subscriptionId: <YOUR_SUBSCRIPTION_ID> credential: name: cloud-creds key: cloud credentials: useSecret: true existingSecret: cloud-creds deployNodeAgent: true nodeAgent: podVolumePath: /var/lib/kubelet/pods privileged: true
configuration: backupStorageLocation: - name: default provider: azure bucket: velero config: resourceGroup: <YOUR_RESOURCE_GROUP> storageAccount: <YOUR_STORAGE_ACCOUNT> subscriptionId: <YOUR_SUBSCRIPTION_ID> credential: name: bsl-creds key: cloud volumeSnapshotLocation: - name: default provider: azure config: resourceGroup: <YOUR_RESOURCE_GROUP> subscriptionId: <YOUR_SUBSCRIPTION_ID> credential: name: cloud-creds key: cloud credentials: useSecret: true existingSecret: cloud-creds deployNodeAgent: true nodeAgent: podVolumePath: /var/lib/kubelet/pods privileged: true
helm install velero vmware-tanzu/velero \ --namespace velero \ -f values.yaml
helm install velero vmware-tanzu/velero \ --namespace velero \ -f values.yaml
helm install velero vmware-tanzu/velero \ --namespace velero \ -f values.yaml
kubectl get pods -n velero
kubectl get pods -n velero
kubectl get pods -n velero
apiVersion: v1
kind: PersistentVolumeClaim
metadata: name: mysql-pvc namespace: default
spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata: name: mysql-pod namespace: default labels: app: mysql
spec: containers: - name: mysql image: mysql:8.0 env: - name: MYSQL_ROOT_PASSWORD value: rootpassword - name: MYSQL_DATABASE value: testdb volumeMounts: - name: mysql-storage mountPath: /var/lib/mysql volumes: - name: mysql-storage persistentVolumeClaim: claimName: mysql-pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata: name: mysql-pvc namespace: default
spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata: name: mysql-pod namespace: default labels: app: mysql
spec: containers: - name: mysql image: mysql:8.0 env: - name: MYSQL_ROOT_PASSWORD value: rootpassword - name: MYSQL_DATABASE value: testdb volumeMounts: - name: mysql-storage mountPath: /var/lib/mysql volumes: - name: mysql-storage persistentVolumeClaim: claimName: mysql-pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata: name: mysql-pvc namespace: default
spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata: name: mysql-pod namespace: default labels: app: mysql
spec: containers: - name: mysql image: mysql:8.0 env: - name: MYSQL_ROOT_PASSWORD value: rootpassword - name: MYSQL_DATABASE value: testdb volumeMounts: - name: mysql-storage mountPath: /var/lib/mysql volumes: - name: mysql-storage persistentVolumeClaim: claimName: mysql-pvc
kubectl apply -f mysql-pod.yaml
kubectl apply -f mysql-pod.yaml
kubectl apply -f mysql-pod.yaml
kubectl exec -it mysql-pod -- /bin/bash
kubectl exec -it mysql-pod -- /bin/bash
kubectl exec -it mysql-pod -- /bin/bash
echo "test data 1" > /var/lib/mysql/test1.txt
echo "test data 2" > /var/lib/mysql/test2.txt
echo "test data 1" > /var/lib/mysql/test1.txt
echo "test data 2" > /var/lib/mysql/test2.txt
echo "test data 1" > /var/lib/mysql/test1.txt
echo "test data 2" > /var/lib/mysql/test2.txt
velero backup create mysql-backup \ --include-namespaces default \ --default-volumes-to-fs-backup \ --wait
velero backup create mysql-backup \ --include-namespaces default \ --default-volumes-to-fs-backup \ --wait
velero backup create mysql-backup \ --include-namespaces default \ --default-volumes-to-fs-backup \ --wait
velero backup get
velero backup get
velero backup get
# Change these in values.yaml for destination cluster
configuration: backupStorageLocation: - name: default # Keep all values the same as source — point to the same blob container accessMode: ReadOnly # Destination reads from source's storage
# Change these in values.yaml for destination cluster
configuration: backupStorageLocation: - name: default # Keep all values the same as source — point to the same blob container accessMode: ReadOnly # Destination reads from source's storage
# Change these in values.yaml for destination cluster
configuration: backupStorageLocation: - name: default # Keep all values the same as source — point to the same blob container accessMode: ReadOnly # Destination reads from source's storage
velero backup get
velero backup get
velero backup get
apiVersion: velero.io/v1
kind: Restore
metadata: name: mysql-restore namespace: velero
spec: backupName: mysql-backup includedNamespaces: - default restorePVs: true itemOperationTimeout: 4h
apiVersion: velero.io/v1
kind: Restore
metadata: name: mysql-restore namespace: velero
spec: backupName: mysql-backup includedNamespaces: - default restorePVs: true itemOperationTimeout: 4h
apiVersion: velero.io/v1
kind: Restore
metadata: name: mysql-restore namespace: velero
spec: backupName: mysql-backup includedNamespaces: - default restorePVs: true itemOperationTimeout: 4h
kubectl apply -f restore.yaml -n velero
kubectl apply -f restore.yaml -n velero
kubectl apply -f restore.yaml -n velero
velero restore get
velero restore describe mysql-restore --details
velero restore get
velero restore describe mysql-restore --details
velero restore get
velero restore describe mysql-restore --details
velero backup describe mysql-backup --details
velero backup describe mysql-backup --details
velero backup describe mysql-backup --details
velero backup logs mysql-backup
velero backup logs mysql-backup
velero backup logs mysql-backup
kubectl logs -n velero deployment/velero | grep mysql-backup
kubectl logs -n velero deployment/velero | grep mysql-backup
kubectl logs -n velero deployment/velero | grep mysql-backup
node-agent-xxxxx 0/1 Pending 0 5m
node-agent-xxxxx 0/1 Pending 0 5m
node-agent-xxxxx 0/1 Pending 0 5m
velero restore delete mysql-restore
velero restore delete mysql-restore
velero restore delete mysql-restore - Take backups of your cluster and restore in case of loss
- Migrate cluster resources to other clusters
- Replicate your production cluster to development and testing clusters - Velero CLI Runs on your local machine.
Used to create, schedule, and manage backups and restores.
- Runs on your local machine.
- Used to create, schedule, and manage backups and restores.
- Kubernetes API Server Receives backup requests from the Velero CLI.
Stores Velero custom resources (like Backup) in etcd.
- Receives backup requests from the Velero CLI.
- Stores Velero custom resources (like Backup) in etcd.
- Velero Server (BackupController) Runs inside the Kubernetes cluster.
Watches the Kubernetes API for Velero backup requests.
Collects Kubernetes resource data and triggers backups.
- Runs inside the Kubernetes cluster.
- Watches the Kubernetes API for Velero backup requests.
- Collects Kubernetes resource data and triggers backups.
- Cloud Provider / Object Storage Stores backup data and metadata.
Creates volume snapshots using the cloud provider's API (e.g., Azure Disk Snapshots).
- Stores backup data and metadata.
- Creates volume snapshots using the cloud provider's API (e.g., Azure Disk Snapshots). - Runs on your local machine.
- Used to create, schedule, and manage backups and restores. - Receives backup requests from the Velero CLI.
- Stores Velero custom resources (like Backup) in etcd. - Runs inside the Kubernetes cluster.
- Watches the Kubernetes API for Velero backup requests.
- Collects Kubernetes resource data and triggers backups. - Stores backup data and metadata.
- Creates volume snapshots using the cloud provider's API (e.g., Azure Disk Snapshots). - User runs a Velero backup command using the CLI: velero backup create my-backup
- CLI creates a backup request in Kubernetes
- The Velero server detects the request and gathers cluster resources
- Backup data is uploaded to cloud object storage
- Persistent volumes are backed up using cloud snapshots (if enabled) - Runs inside a namespace of the host cluster
- Has an API server, control plane, and syncer
- Maintains its own set of Kubernetes resources, operating like a full cluster - Promoting apps from dev to staging or prod: Backing up and restoring workloads between vClusters allows smooth promotion of applications across environments, ensuring consistent configurations and deployments without manual rework.
- Replicating test environments: It helps recreate identical test setups quickly, enabling developers to reproduce issues, validate fixes, or test new features in isolated environments.
- Disaster recovery (DR) setup: Regular backups across vClusters ensure business continuity by allowing workloads to be restored rapidly in another cluster if the primary one fails.
- Tenant migration in multi-tenant environments: vClusters make it easier to move tenants between isolated environments without affecting others, maintaining data security and minimizing downtime.
- Cluster version upgrades or deprecations: When upgrading or decommissioning a cluster, backing up workloads to another vCluster ensures a seamless transition without losing data or configurations. - Two clusters up and running on Azure (any cloud offering works)
- Two running vClusters (source and destination)
- Velero CLI installed on your machine - Create an Azure storage account and blob container
- Get the resource group details
- Set permissions for Velero - BSL (Backup Storage Location) — the blob container where Velero stores backups. Velero needs a secret to access this storage location.
- cloud-creds — credentials required to access the Azure cluster. - Delete all resources created by the restore job (pods, statefulsets, deployments, PVCs, etc.)
OR
If restoring a whole namespace, delete the entire restored namespace.
- Delete the restore job: - After the restore job is deleted, ArgoCD (if used) will automatically sync and recreate the restore job, triggering the Velero restoration.