AWS Storage Blog

Remap Amazon EKS namespaces on restore with AWS Backup resource modifiers

Teams that run multi-tenant workloads on Amazon Elastic Kubernetes Service (Amazon EKS) often organize applications by namespace: one namespace per team, per environment, or per customer. When something goes wrong with one of those namespaces, the fastest path to recovery is not always to put it back exactly where it was. You might want to restore last night’s copy next to the live namespace so you can compare the two. If a namespace was deleted, you can bring it back under a new name while you investigate what removed it. You can also clone a production namespace into a staging namespace on the same cluster. With AWS Backup resource modifiers, you can now remap a namespace during a restore.

With AWS Backup, you can protect Amazon EKS cluster state, cluster configuration, and persistent volumes as a single composite recovery point. You can restore the whole cluster or individual namespaces from it. Until now, a namespace-level restore always recreated objects in their original namespace. Resource modifiers change that. A resource modifier is a YAML file of rules that you store in an Amazon Simple Storage Service (Amazon S3) bucket. AWS Backup reads the rules and applies them to each Kubernetes object before it creates the object in the cluster. Each rule pairs match conditions with JSON Patch operations, and you need only two rules to move a namespace. If you use Velero today, resource modifiers give you the same namespace remapping that the Velero --namespace-mappings flag provides, natively in AWS Backup.

In this post, you deploy a sample application into a namespace named original-namespace and back up the cluster with AWS Backup. Then you use a resource modifier to restore that namespace into new-namespace on the same cluster.

Solution overview

The following diagram shows the flow. AWS Backup backs up the cluster into a composite recovery point. At restore time, the restore job reads the resource modifier file from Amazon S3, patches each Kubernetes object, and creates the patched objects in the target cluster.

AWS Backup creates a recovery point, then the restore job applies the Amazon S3 resource modifier to recreate objects in the target cluster

Figure 1: Flow of this walkthrough to restore a namespace into a new namespace with a resource modifier using AWS Backup

This walkthrough has five steps, shown in Figure 1:

  1. Create the cluster and deploy the sample application. Create an Amazon EKS Auto Mode cluster and deploy a Deployment, ConfigMap, and ServiceAccount into original-namespace.
  2. Back up the cluster. Create a backup vault and run an on-demand AWS Backup job against the cluster.
  3. Write the resource modifier and upload it to Amazon S3. Define the rules that rename the namespace and move its objects.
  4. Restore the namespace with the resource modifier. Start a namespace-level restore job into the same cluster that references the modifier file.
  5. Validate the restore. Confirm that the application now runs in new-namespace.

This post uses the AWS Command Line Interface (AWS CLI) so you can copy each step. You can also specify a resource modifier file when you start an Amazon EKS restore job in the AWS Backup console.

How resource modifiers work

When a restore job has a resource modifier, AWS Backup does the following for each Kubernetes object in the recovery point:

  1. Reads the object as it was backed up from the backup vault.
  2. Checks the object against each rule in the order the rules appear in the file.
  3. For every rule whose conditions match, applies that rule’s patches. Each rule sees the object as earlier rules patched it, so a later rule must match on the patched values. For example, after a rule moves an object to new-namespace, later rules must match new-namespace.
  4. Creates the patched object in the target cluster.

The objects in the recovery point never change. AWS Backup applies patches only to the copy it creates in the cluster, so you can restore the same recovery point again with a different modifier file, or with none.

A resource modifier file has a version (v1) and a list of resourceModifierRules. Each rule has conditions that select the Kubernetes objects and patches that modify them:

version: v1
resourceModifierRules:
  - conditions:
      groupResource: apps/v1/deployments   # <group>/<version>/<resource>; core resources use v1/<resource>
      resourceNameRegex: "web"           # full regular expression match on metadata.name
      namespaces:                          # only objects in these namespaces
        - team-a
      labelSelector:
        matchLabels:
          example: label                   # only objects that have these labels
    patches:
      - operation: replace
        path: "/spec/replicas"
        value: 1
      - operation: remove
        path: "/metadata/labels/example"

All conditions are optional, but you must provide at least one condition, and an object must satisfy every condition you specify in a rule for that rule’s patches to apply. AWS Backup evaluates rules in the order they appear in the file. The supported operations are the JSON Patch add, remove, and replace.

How resource modifiers compare to Velero

Many teams protect Kubernetes workloads with Velero, an open source backup tool. Velero remaps namespaces with a dedicated --namespace-mappings flag and patches other fields with a separate resource modifier ConfigMap. AWS Backup uses a single resource modifier file for both. Namespace remapping is only another set of rules, so you can rename a namespace and patch fields inside it from the same file. The following table maps common Velero tasks to their AWS Backup equivalents.

Task Velero AWS Backup
Remap a namespace velero restore create --namespace-mappings original-namespace:new-namespace Two resource modifier rules (Step 3)
Patch fields at restore time Resource modifiers ConfigMap, referenced with --resource-modifier-configmap Resource modifier YAML file in Amazon S3, referenced in the restore metadata "resourceModifiers" field
Resource selector format <resource>.<group>, for example deployments.apps <group>/<version>/<resource>, for example apps/v1/deployments

Prerequisites

To follow this walkthrough, you need:

Set the following environment variables. Replace the placeholder values with your own:

export AWS_ACCOUNT_ID=123456789012
export REGION=us-west-2
export CLUSTER_NAME=namespace-remapping-demo
export BACKUP_VAULT_NAME=eks-backup-vault
export IAM_ROLE_ARN=arn:aws:iam::${AWS_ACCOUNT_ID}:role/AWSBackupEKSRole
export RESOURCE_ARN=arn:aws:eks:${REGION}:${AWS_ACCOUNT_ID}:cluster/${CLUSTER_NAME}
export BUCKET_NAME=amzn-s3-demo-bucket
export S3_KEY=resource-modifiers/namespace-remapping-modifiers.yaml

Step 1: Create the cluster and deploy the sample application

This walkthrough uses Amazon EKS Auto Mode, but resource modifiers work with any EKS cluster that AWS Backup supports. If you already have a cluster, skip this step.

  1. Save the following as cluster-config.yaml:
    apiVersion: eksctl.io/v1alpha5
    kind: ClusterConfig
    
    metadata:
      name: namespace-remapping-demo
      region: us-west-2
      version: "1.37" # or any supported EKS version
    
    autoModeConfig:
      enabled: true
  2. Create the cluster, wait for it to become Active, and update your kubeconfig:
    eksctl create cluster -f cluster-config.yaml
    aws eks update-kubeconfig --region $REGION --name $CLUSTER_NAME
  3. Save the sample application as sample-app.yaml. It contains a namespace, a ServiceAccount with an IAM role annotation, a ConfigMap, and a Deployment that reads its environment from the ConfigMap:
    apiVersion: v1
    kind: Namespace
    metadata:
      name: original-namespace
      labels:
        app.kubernetes.io/part-of: namespace-remapping-demo
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: app-sa
      namespace: original-namespace
      annotations:
        eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/demo-app
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: app-config
      namespace: original-namespace
    data:
      DB_HOST: mydb.cluster-123456789012.us-west-2.rds.amazonaws.com
      S3_BUCKET: amzn-s3-demo-bucket
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: web
      namespace: original-namespace
      labels:
        app: web
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: web
      template:
        metadata:
          labels:
            app: web
        spec:
          serviceAccountName: app-sa
          containers:
            - name: web
              image: public.ecr.aws/nginx/nginx:1.27
              ports:
                - containerPort: 80
              envFrom:
                - configMapRef:
                    name: app-config
  4. Apply the manifest to deploy the sample application:
    kubectl apply -f sample-app.yaml
  5. Confirm that the application is running:
kubectl get namespaces,configmap,serviceaccount,deployment,pod -n original-namespace

You see output similar to the following:

NAME                           STATUS   AGE
namespace/default              Active   13m
namespace/kube-node-lease      Active   13m
namespace/kube-public          Active   13m
namespace/kube-system          Active   13m
namespace/original-namespace   Active   42s

NAME                         DATA   AGE
configmap/app-config         2      42s
configmap/kube-root-ca.crt   1      42s

NAME                     AGE
serviceaccount/app-sa    42s
serviceaccount/default   42s

NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/web   2/2     2            2           42s

NAME                       READY   STATUS    RESTARTS   AGE
pod/web-5bd7884746-rpzxj   1/1     Running   0          42s
pod/web-5bd7884746-zmq8l   1/1     Running   0          42s

Step 2: Back up the EKS cluster

  1. Create a backup vault, or reuse an existing backup vault:
    aws backup create-backup-vault \
        --region $REGION \
        --backup-vault-name $BACKUP_VAULT_NAME
    {
        "BackupVaultName": "eks-backup-vault",
        "BackupVaultArn": "arn:aws:backup:us-west-2:123456789012:backup-vault:eks-backup-vault",
        "CreationDate": "2026-11-10T23:07:00.366000+00:00"
    }
  2. Start an on-demand backup job for the cluster. AWS Backup returns the Amazon Resource Name (ARN) of the composite recovery point that the job creates:
    OUTPUT=$(aws backup start-backup-job \
        --region $REGION \
        --backup-vault-name $BACKUP_VAULT_NAME \
        --resource-arn $RESOURCE_ARN \
        --iam-role-arn $IAM_ROLE_ARN)
    
    export BACKUP_JOB_ID=$(jq -r '.BackupJobId' <<< "$OUTPUT")
    export RECOVERY_POINT_ARN=$(jq -r '.RecoveryPointArn' <<< "$OUTPUT")
    echo "Backup Job: $BACKUP_JOB_ID"
    echo "Recovery Point ARN: $RECOVERY_POINT_ARN"
    Backup Job: a6aabf21-74b4-4b2c-be05-b45459134f34
    Recovery Point ARN: arn:aws:backup:us-west-2:123456789012:recovery-point:composite:eks/namespace-remapping-demo-20261110231014-1f7a7420
  3. Check the job until State is COMPLETED:
aws backup describe-backup-job \
    --region $REGION \
    --backup-job-id $BACKUP_JOB_ID \
    --query '{BackupJobId: BackupJobId, RecoveryPointArn: RecoveryPointArn, State: State, StatusMessage: StatusMessage}'

When the backup finishes, you see output similar to the following:

{
    "BackupJobId": "a6aabf21-74b4-4b2c-be05-b45459134f34",
    "RecoveryPointArn": "arn:aws:backup:us-west-2:123456789012:recovery-point:composite:eks/namespace-remapping-demo-20261110231014-1f7a7420",
    "State": "COMPLETED",
    "StatusMessage": null
}

You can also schedule EKS backups with a backup plan. Resource modifiers apply at restore time and don’t modify the recovery point, so you can use them with existing EKS recovery points.

Step 3: Write the resource modifier and upload it to Amazon S3

Remapping a namespace takes two rules, because the namespace itself and the objects inside it are stored differently:

  • The Namespace object is cluster-scoped, and its name lives in metadata.name. The first rule matches the v1/namespaces resource named original-namespace and replaces that name.
  • Every namespaced object (Deployments, ConfigMaps, ServiceAccounts, and so on) records its namespace in metadata.namespace. The second rule matches every object in original-namespace, regardless of kind, and replaces that namespace field to restore it in new-namespace.
  1. Save the following as namespace-remapping-modifiers.yaml:
version: v1
resourceModifierRules:
  # Rename the Namespace object itself.
  - conditions:
      groupResource: v1/namespaces
      resourceNameRegex: "original-namespace"
    patches:
      - operation: replace
        path: "/metadata/name"
        value: "new-namespace"
  # Move every object in original-namespace into new-namespace.
  - conditions:
      namespaces:
        - original-namespace
    patches:
      - operation: replace
        path: "/metadata/namespace"
        value: "new-namespace"

To see the effect of the second rule, compare the web Deployment as it was backed up with how AWS Backup restores it. The following manifest shows the Deployment in the recovery point:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: original-namespace
  labels:
    app: web
spec:
  # ...unchanged

After the second rule applies, AWS Backup creates the Deployment with this manifest:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: new-namespace      # patched by the second rule
  labels:
    app: web
spec:
  # ...unchanged

Only the metadata.namespace changes. The ServiceAccount, ConfigMap, and any other objects in that namespace get the same patch. The Deployment still refers to app-sa and app-config by name, and they are now in the same namespace as the Deployment, so those references still work after the restore.

  1. Upload the file to Amazon S3:
aws s3api put-object \
    --region $REGION \
    --bucket $BUCKET_NAME \
    --key $S3_KEY \
    --body namespace-remapping-modifiers.yaml

Note: If you turn on versioning for the bucket, AWS Backup uses the latest version of the object when the restore job starts.

Step 4: Restore the namespace with the resource modifier

This step deletes the original namespace to simulate losing it. You can also skip the deletion and restore next to the live namespace. Because the restored objects are created in new-namespace, they don’t conflict with the originals. A restore never overwrites existing objects. If an object with the same name already exists in the target namespace, AWS Backup skips it.

  1. Delete the sample application:
    kubectl delete -f sample-app.yaml
    namespace "original-namespace" deleted
    serviceaccount "app-sa" deleted
    configmap "app-config" deleted
    deployment.apps "web" deleted
  2. Verify that all objects in original-namespace are deleted:
kubectl get namespaces,configmap,serviceaccount,deployment,pod -n original-namespace

You now see only the initial namespaces:

NAME              STATUS   AGE
default           Active   17m
kube-node-lease   Active   17m
kube-public       Active   17m
kube-system       Active   17m
  1. (Optional) Turn on restore notifications. AWS Backup can publish an Amazon Simple Notification Service (Amazon SNS) notification for each Kubernetes object it fails to restore or skips because it already exists. It also publishes one for each resource modifier patch that succeeds or fails. These notifications are the quickest way to find a rule that did not match what you expected. To turn them on for the vault, see Notification options with AWS Backup.
  2. Set CHILD_EKS_RECOVERY_POINT_ARN to the child EKS recovery point. You need it in the Clean up section. Optionally, review the restore metadata that AWS Backup recorded for it, which shows the keys you can set when you restore.
    OUTPUT=$(aws backup list-recovery-points-by-backup-vault \
        --region $REGION \
        --backup-vault-name $BACKUP_VAULT_NAME \
        --by-resource-type EKS \
        --by-parent-recovery-point-arn $RECOVERY_POINT_ARN)
    
    export CHILD_EKS_RECOVERY_POINT_ARN=$(jq -r '.RecoveryPoints[0].RecoveryPointArn' <<< "$OUTPUT")
    echo "Child EKS Recovery Point ARN: $CHILD_EKS_RECOVERY_POINT_ARN"
    
    aws backup get-recovery-point-restore-metadata \
        --region $REGION \
        --backup-vault-name $BACKUP_VAULT_NAME \
        --recovery-point-arn $CHILD_EKS_RECOVERY_POINT_ARN
  3. Build the restore metadata. The values in the metadata map are strings, so the namespaces list is passed as a JSON-encoded string. Using jq avoids hand-escaping the quotes:
export RESTORE_METADATA=$(jq -nc \
    --arg cluster "$CLUSTER_NAME" \
    --arg modifiers "s3://${BUCKET_NAME}/${S3_KEY}" \
    '{
        clusterName: $cluster,
        newCluster: "false",
        namespaceLevelRestore: "true",
        namespaces: (["original-namespace"] | tojson),
        resourceModifiers: $modifiers
    }')
echo "$RESTORE_METADATA"

The output is a single line:

{"clusterName":"namespace-remapping-demo","newCluster":"false","namespaceLevelRestore":"true","namespaces":"[\"original-namespace\"]","resourceModifiers":"s3://amzn-s3-demo-bucket/resource-modifiers/namespace-remapping-modifiers.yaml"}

The following table describes each key in this example. For the full list of restore metadata keys, see Restore an Amazon EKS cluster.

Key Value in this example Purpose
clusterName namespace-remapping-demo The name of the cluster to restore into.
newCluster "false" Restore into an existing cluster.
namespaceLevelRestore "true" Restore only the namespaces listed in namespaces.
namespaces ["original-namespace"] The namespaces to restore, named as they were at backup time.
resourceModifiers s3://.../namespace-remapping-modifiers.yaml The resource modifier file to apply in S3 URI format.
  1. Call the StartRestoreJob API to start a namespace-level restore from the composite recovery point:
    OUTPUT=$(aws backup start-restore-job \
        --region $REGION \
        --resource-type EKS \
        --recovery-point-arn $RECOVERY_POINT_ARN \
        --iam-role-arn $IAM_ROLE_ARN \
        --metadata "$RESTORE_METADATA")
    
    export RESTORE_JOB_ID=$(jq -r '.RestoreJobId' <<< "$OUTPUT")
    echo "Restore Job: $RESTORE_JOB_ID"
    Restore Job: f560b2dd-bf77-491c-8b0f-06d6076580b8
  2. Check the job until Status is COMPLETED:
aws backup describe-restore-job \
    --region $REGION \
    --restore-job-id $RESTORE_JOB_ID \
    --query '{RestoreJobId: RestoreJobId, Status: Status, StatusMessage: StatusMessage}'

When the restore finishes, you see:

{
    "RestoreJobId": "f560b2dd-bf77-491c-8b0f-06d6076580b8",
    "Status": "COMPLETED",
    "StatusMessage": null
}

Step 5: Validate the restore

  1. List the namespaces. new-namespace is present, and original-namespace is not, because you deleted original-namespace in Step 4 and the resource modifier renamed it during the restore:
    kubectl get namespaces
    NAME              STATUS   AGE
    default           Active   34m
    kube-node-lease   Active   34m
    kube-public       Active   34m
    kube-system       Active   34m
    new-namespace     Active   4m20s
  2. Confirm that the application objects were restored into the new namespace and that the pods are running:
kubectl get configmap,serviceaccount,deployment,pod -n new-namespace

You see the same objects you deployed in Step 1, now in new-namespace:

NAME                         DATA   AGE
configmap/app-config         2      13m
configmap/kube-root-ca.crt   1      13m

NAME                     AGE
serviceaccount/app-sa    13m
serviceaccount/default   13m

NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/web   2/2     2            2           13m

NAME                       READY   STATUS    RESTARTS   AGE
pod/web-5bd7884746-jlpgg   1/1     Running   0          13m
pod/web-5bd7884746-q2gl2   1/1     Running   0          13m

Considerations

Keep the following in mind when you use resource modifiers for namespace remapping.

References to the old namespace are not rewritten for you

The rules in this post change where objects are created. They do not change values inside objects that mention the old namespace by name. Before you remap a real application, check for references to the old namespace. You need to update these yourself:

  • RoleBinding and ClusterRoleBinding subjects that reference a ServiceAccount in original-namespace.
  • In-cluster DNS names such as my-service.original-namespace.svc.cluster.local in ConfigMaps, environment variables, or Ingress rules.
  • Trust policies for IAM roles for service accounts (IRSA). Their sub condition includes system:serviceaccount:original-namespace:app-sa.
  • Amazon EKS Pod Identity associations, which are scoped to a namespace and ServiceAccount.
  • Any other field that contains the namespace name, such as annotations or command arguments.

More rules can be added to the same file

AWS Backup applies the rules in the order they appear in the file. For example, the following file adds a third rule that updates a hostname in a ConfigMap. The third rule matches new-namespace because rule 2 has already moved the ConfigMap from original-namespace to new-namespace:

version: v1
resourceModifierRules:
  # ...rules 1 and 2 from namespace-remapping-modifiers.yaml...
  - conditions:
      groupResource: v1/configmaps
      resourceNameRegex: "app-config"
      namespaces:
        - new-namespace # rule 2 already moved it
    patches:
      - operation: replace
        path: "/data/DB_HOST"
        value: "mydb.cluster-987654321098.us-west-2.rds.amazonaws.com"

Test your rules first

Restore into a test cluster or a scratch namespace before you rely on a resource modifier in a recovery event. Keep the modifier file in source control next to your application manifests. Store it in an S3 bucket with versioning turned on so you can roll back to an earlier version of your rules.

Persistent volumes follow their remapped claims

If the namespace contains PersistentVolumeClaims backed by Amazon Elastic Block Store (Amazon EBS), AWS Backup restores the volumes and binds each remapped claim to its restored volume. You don’t need a rule for PersistentVolumes. Don’t patch spec.claimRef or spec.csi on them, because AWS Backup manages those fields during restores.

More ways to use resource modifiers

The same mechanism handles other restore-time changes. For example, you can:

  • Pull container images from an Amazon Elastic Container Registry (Amazon ECR) repository in the target Region or account by replacing /spec/template/spec/containers/0/image.
  • Scale Deployments or StatefulSets down in a staging copy by replacing /spec/replicas, using a labelSelector to choose which workloads.
  • Point ConfigMaps at another Region’s endpoints, such as an Amazon Relational Database Service (Amazon RDS) host or an S3 bucket name, when you restore a copy in another Region.
  • Update IRSA annotations to use a role in a different account, when you restore a cross-account copy.
  • Remove nodePort values from Services to avoid port collisions on the target cluster.
  • Add a label such as restored-from to track where restored objects came from.

Clean up

To avoid ongoing charges, run the following AWS CLI commands to delete the resources that you created:

# Delete the cluster
eksctl delete cluster -f cluster-config.yaml

# Delete the child EKS recovery point first, then the parent EKS recovery point
aws backup delete-recovery-point \
    --region $REGION \
    --backup-vault-name $BACKUP_VAULT_NAME \
    --recovery-point-arn $CHILD_EKS_RECOVERY_POINT_ARN

aws backup delete-recovery-point \
    --region $REGION \
    --backup-vault-name $BACKUP_VAULT_NAME \
    --recovery-point-arn $RECOVERY_POINT_ARN
# Recovery point deletion is asynchronous. If delete-backup-vault fails because the vault isn't empty, wait a few minutes and run it again.

# Delete the backup vault
aws backup delete-backup-vault \
    --region $REGION \
    --backup-vault-name $BACKUP_VAULT_NAME

# Delete the resource modifier file
aws s3api delete-object \
    --region $REGION \
    --bucket $BUCKET_NAME \
    --key $S3_KEY

If you created an Amazon SNS topic or IAM role for this walkthrough, delete them too.

Conclusion

In this post, we showed how to restore an Amazon EKS namespace into a new namespace on the same cluster with AWS Backup resource modifiers. We backed up a cluster and wrote a two-rule resource modifier that renames a namespace and moves its objects. Then we ran a namespace-level restore that applied those rules before creating each object.

You can use resource modifiers with AWS Backup for Amazon EKS in every AWS Region where AWS Backup supports Amazon EKS. There is no additional charge for using resource modifiers, though standard AWS Backup and related service charges apply. For more information, see AWS Backup Pricing. To learn more, see Restore an Amazon EKS cluster in the AWS Backup Developer Guide. For more on protecting Amazon EKS workloads, see Secure EKS clusters with the new support for Amazon EKS in AWS Backup.


About the author

Quinn Winter

Quinn Winter

Quinn is a Senior Software Development Engineer on the AWS Backup team. Quinn led the design of Amazon EKS support in AWS Backup and is passionate about data protection, distributed systems, and Kubernetes. Outside of work, Quinn enjoys snowboarding, playing basketball, and spending time outdoors.