# Appendix H - Velero Installation Guide

This guide explains how to install Velero on an EKS cluster using only the AWS plugin (without CSIsnapshots).&#x20;

* Installation approaches: Helm or Terraform.&#x20;
* Authentication options: IRSA or EKS Pod Identity.&#x20;

## H.1  Prerequisites

* **EKS Cluster Requirements**&#x20;
  * **EKS cluster** with either IRSA or Pod Identity enabled&#x20;
  * **Kubernetes version** v1.16+
* **AWS Resources**&#x20;
  * **S3 bucket** for storing backups&#x20;
  * **IAM role** with necessary permissions&#x20;
  * **AWS CLI** configured with appropriate credentials
* **Tools**&#x20;
  * **kubectl** configured to access your cluster&#x20;
  * **Helm** for Helm approach&#x20;
  * **Terraform** for Terraform approach&#x20;

## H.2  Authentication Methods&#x20;

**Option 1: IRSA (IAM Roles for Service Accounts)**&#x20;

* Uses OIDC provider and IAM roles&#x20;
* More secure, recommended for production&#x20;
* Requires OIDC provider configuration&#x20;

**Option 2: EKS Pod Identity (pod identity agent)**&#x20;

* Simpler setup (no OIDC provider; no SSO/Identity Center involved)&#x20;
* Requires the EKS Pod Identity Agent addon&#x20;
* Trust policy principal: `Service: pods.eks.amazonaws.com`&#x20;
* Create a Pod Identity Association mapping `<namespace>/<serviceAccount>` → IAM role

## H.3  Approach 1: Helm Installation&#x20;

#### **Step 1**: Create S3 Bucket&#x20;

Create an S3 bucket through the AWS Console or CLI:

```
aws s3 mb s3://your-velero-backup-bucket --region us-east-1
aws s3api put-bucket-versioning --bucket your-velero-backup-bucket --versioning-configuration Status=Enabled 
```

#### **Step 2**: Create IAM Role and Policy&#x20;

**Required IAM Permissions**&#x20;

Velero needs specific AWS permissions to perform backups and restores. When creating the IAM policy in the AWS Console, include these permissions:&#x20;

**S3 Permissions** (scope to your Velero bucket only):&#x20;

* s3:ListBucket - on arn:aws:s3:::your-velero-backup-bucket&#x20;
* s3:GetObject - on arn:aws:s3:::your-velero-backup-bucket/\*&#x20;
* s3:PutObject  - on arn:aws:s3:::your-velero-backup-bucket/\*&#x20;
* s3:DeleteObject  - on arn:aws:s3:::your-velero-backup-bucket/\*&#x20;

**EC2 Permissions** (for EBS snapshot management, resource: \*):&#x20;

* ec2:DescribeVolumes&#x20;
* ec2:CreateSnapshot&#x20;
* ec2:DeleteSnapshot&#x20;
* ec2:DescribeSnapshots&#x20;
* ec2:CreateTags&#x20;
* ec2:CreateVolume&#x20;
* ec2:AttachVolume&#x20;
* ec2:DetachVolume&#x20;
* ec2:DescribeAvailabilityZones&#x20;
* ec2:DescribeTags&#x20;

**Understanding Service Accounts and IRSA**&#x20;

**What is IRSA?**&#x20;

IRSA (IAM Roles for Service Accounts) allows Kubernetes pods to securely use AWS services without storing static credentials. It's the recommended approach for EKS workloads.&#x20;

**How it works:**&#x20;

1. Your EKS cluster has an OIDC provider that acts as an identity issuer&#x20;
2. You create an IAM role that trusts this OIDC provider&#x20;
3. Velero's Kubernetes ServiceAccount ( `velero-server` ) gets annotated with the IAM role ARN&#x20;
4. When Velero pods run, they automatically receive temporary AWS credentials by assuming the IAM role&#x20;
5. No AWS access keys are stored in your cluster - credentials are temporary and auto-rotate&#x20;

**Creating the IAM Role (AWS Console)**&#x20;

**Step 2.1: Create IAM Policy**&#x20;

1. Go to AWS Console → IAM → Policies → Create Policy&#x20;
2. Click JSON tab and paste a policy with the permissions listed above&#x20;
3. Name it `VeleroBackupPolicy`&#x20;
4. Create the policy&#x20;

**Step 2.2: Create IAM Role with OIDC Trust**&#x20;

1. Go to IAM → Roles → Create Role&#x20;
2. Select "Web Identity" as trusted entity type&#x20;
3. Choose your EKS cluster's OIDC provider from the dropdown&#x20;
4. For "Audience", select `sts.amazonaws.com`&#x20;
5. Add a condition:&#x20;
   1. Condition: `StringEquals`&#x20;
   2. Key: `<your-oidc-provider>:sub`&#x20;
   3. Value: `system:serviceaccount:velero:velero-server`&#x20;
6. Attach the VeleroBackupPolicy you created&#x20;
7. Name the role (e.g., `VeleroIRSARole` )&#x20;
8. Copy the Role ARN - you'll need it for Helm installation&#x20;

{% hint style="info" %}
The OIDC provider URL can be found in EKS Console → Your Cluster → Overview → OpenIDConnect provider URL&#x20;
{% endhint %}

**Step 3: Install Velero with Helm**&#x20;

Replace \<YOUR\_BUCKET> , \<YOUR\_REGION> , and \<IAM\_ROLE\_ARN> with your actual values.

\# Add Helm repository

```
helm repo add vmware-tanzu https://vmware-tanzu.github.io/helm-charts
helm repo update
```

\# Install Velero with command-line flags

```
helm install velero vmware-tanzu/velero \
 --namespace velero \
 --create-namespace \
 --set configuration.backupStorageLocation[0].name=default \
 --set configuration.backupStorageLocation[0].provider=aws \
 --set configuration.backupStorageLocation[0].bucket=<YOUR_BUCKET> \
 --set configuration.backupStorageLocation[0].config.region=<YOUR_REGION> \
 --set configuration.volumeSnapshotLocation[0].name=default \ 
 --set configuration.volumeSnapshotLocation[0].provider=aws \
 --set configuration.volumeSnapshotLocation[0].config.region=<YOUR_REGION> \
 --set initContainers[0].name=velero-plugin-for-aws \ 
 --set initContainers[0].image=velero/velero-plugin-for-aws:v1.10.0 \ 
 --set initContainers[0].volumeMounts[0].mountPath=/target \ 
 --set initContainers[0].volumeMounts[0].name=plugins \ 
 --set credentials.useSecret=false \ 
 --set serviceAccount.server.create=true \ 
 --set serviceAccount.server.name=velero-server \ 
 --set serviceAccount.server.annotations."eks.amazonaws.com/role-arn"=<IAM_ROLE_ARN> \ 
 --set deployNodeAgent=true \
--set uploaderType=kopia
```

**Step 4: Verify Installation**

\#  Check pods are running

`kubectl get pods -n velero`

\# Verify ServiceAccount annotation

`kubectl get serviceaccount velero-server -n velero -o yaml`

\# Check Velero can authenticate to AWS

`kubectl exec -n velero deployment/velero -- aws sts get-caller-identity`

\# Check backup storage location

`kubectl get backupstoragelocation -n velero`

**Step 5: Install Velero CL**I&#x20;

Download the Velero CLI to manage backups from your machine:

\# Linux

```
wget https://github.com/vmware-tanzu/velero/releases/download/v1.14.0/velero-v1.14.0-linux-amd64.tar.gz
tar -xvf velero-v1.14.0-linux-amd64.tar.gz
sudo mv velero-v1.14.0-linux-amd64/velero /usr/local/bin/
```

\# macOS

`brew install velero`

\# Verify

`velero version`&#x20;

#### `Step 6: Test Backup`

\#Create a test backup

`velero backup create test-backup --include-namespaces default`

\# Check status

`velero backup describe test-backup`

\# List backups

`velero backup get`

## H.4  Approach 2: Terraform Bootstrapping&#x20;

#### H.4.2.1 Choose authentication (IRSA or EKS Pod Identity)&#x20;

**IRSA (existing/standard)**&#x20;

* Ensure an EKS OIDC provider exists.&#x20;
* Create an IAM role with trust for that OIDC provider and `system:serviceaccount::<ns>:<sa>` .&#x20;
* Annotate the ServiceAccount with the role ARN:&#x20;
  * `serviceAccount.server.annotations."eks.amazonaws.com/role-arn" = <role-arn>`
  * Use the key exactly `eks.amazonaws.com/role-arn` (case-sensitive)

**EKS Pod Identity**&#x20;

* Install addon: `eks-pod-identity-agent` .&#x20;
* Create an IAM role with trust principal `Service: pods.eks.amazonaws.com` .&#x20;
* Create a Pod Identity Association mapping `<namespace> /<serviceAccount>` → role.&#x20;
* Remove any IRSA annotations from the SA.&#x20;

#### H.4.2.2 Minimal Terraform for Velero (AWS plugin by default)&#x20;

```
resource "helm_release" "velero" {
 name = "velero" 
 repository = "https://vmware-tanzu.github.io/helm-charts" 
 chart = "velero" 
 version = "10.0.11" 
 namespace = "velero" 
 create_namespace = true 
 
values = [yamlencode({ 
 configuration = {
  # Default: AWS plugin only (no CSI) 
  features = ""
  backupStorageLocation = [{
   name = "default"
   provider = "aws" 
   bucket = "" 
   config = { region = "" }
  }] 
  # Enable CSI snapshots (optional): 
  # features = "EnableCSI" 
  # volumeSnapshotLocation = [{ name = "default", provider = "aws", config = { region = "" } }] 
  # extraArgs = ["--default-snapshot-move-data=true"] # Only applies to CSI data-mover flows 
 } 
 
 # Plugins 
 plugins = ["velero/velero-plugin-for-aws:v1.12.0"] 
 # FS backups (optional but recommended to have available) 
 deployNodeAgent = true 
 uploaderType = "kopia" 
 
 # IRSA: annotate SA with the role (if using IRSA) 
 # serviceAccount = { server = { create = true, name = "velero-server", annotations = { "eks.amazonaws.com/role-arn" = var.velero_role_arn } } } 
 })]
} 
```

#### H.4.2.3 CSI prerequisites (only if enabling CSI)&#x20;

* Install the snapshot-controller (e.g., Piraeus `snapshot-controller` chart) and CRDs.&#x20;
* Create an EBS CSI `VolumeSnapshotClass` ( `driver: ebs.csi.aws.com` ).&#x20;
* Label the VSC so Velero detects it: `velero.io/csi-volumesnapshot-class=true` .&#x20;

{% hint style="info" %}
Tip: Run snapshot-controller with `replicaCount: 2` .&#x20;
{% endhint %}

#### H.4.2.4 Deploy&#x20;

```
terraform init
terraform apply -auto-approve
```

#### H.4.2.5 Verify&#x20;

```
kubectl -n velero get deploy velero -o 
jsonpath='{.spec.template.spec.containers[0].args}'; echo
kubectl -n velero logs deploy/velero | grep -Ei 'velero-plugin|credentials|error' || true 
```

## H.5  Verification&#x20;

#### **Check Installation**

\# Check Velero pods

`kubectl get pods -n velero`

\# Test backup

`kubectl exec -n velero deployment/velero -- velero backup create test-backup --include-namespaces default`

## **H.6  Required IAM Permissions**&#x20;

**S3**: List, Get, Put, Delete objects in backup bucket&#x20;

**EC2**: Describe volumes, create/delete snapshots, create tags&#x20;

## **H.7  Troubleshooting**&#x20;

**Common Issues**&#x20;

1. **Authentication not working**: Check IRSA or Pod Identity configuration&#x20;
2. **S3 access denied**: Verify IAM role permissions&#x20;
3. **Plugin not found**: Check plugin image

#### Debug Commands

\# Check Velero logs

`kubectl logs -n velero deployment/velero`

\# Test AWS credentials

`kubectl exec -n velero deployment/velero -- aws sts get-caller-identity`

## H.8  Security Considerations&#x20;

1. **Least privilege**: Only grant necessary permissions
2. **Bucket encryption**: Enable S3 bucket encryption&#x20;
3. **Access logging**: Enable S3 access logging&#x20;

## H.9  Cost Optimization&#x20;

1. **Lifecycle policies**: Set S3 lifecycle rules&#x20;
2. **Snapshot retention**: Configure deletion policies&#x20;
3. **Storage classes**: Use appropriate S3 storage classes&#x20;

#### Comparison: Installation Methods&#x20;

| Aspect         | Helm           | Terraform         |
| -------------- | -------------- | ----------------- |
| Complexity     | Simple         | Medium            |
| Infrastructure | Manual         | Automated         |
| Multi-cluster  | Manual         | Easy to replicate |
| Best for       | Single cluster | Multiple clusters |

## H.10  Support&#x20;

For issues with this setup:&#x20;

1. Check Velero documentation: [https://velero.io/docs/ ](https://velero.io/docs/)
2. Review AWS EKS documentation: <https://docs.aws.amazon.com/eks/>
3. Check Velero GitHub issues: <https://github.com/vmware-tanzu/velero/issues>

## H.11  Additional Notes&#x20;

* To enable CSI snapshots, follow Section [H.4.2.3](#h.4.2.3-csi-prerequisites-only-if-enabling-csi) (controller, VSC, label) and set `configuration.features = "EnableCSI"` in Terraform.&#x20;
* For file-system backups, enable `deployNodeAgent = true` and `uploaderType = "kopia"` ; opt-in per PVC with `velero.io/fs-backup=true` .&#x20;
* If not on AWS, use the cloud plugin for your provider instead of the AWS plugin:&#x20;
  * GCP: `velero/velero-plugin-for-gcp`&#x20;
  * Azure: `velero/velero-plugin-for-microsoft-azure`&#x20;


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.n2ws.com/user-guide/appendix-h-velero-installation-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
