How To

Copy Kubernetes Secrets Between Namespaces

Kubernetes secrets are namespace-scoped, meaning a secret created in one namespace is not accessible from another. This becomes a problem when you need the same credentials, TLS certificates, or registry pull secrets across multiple namespaces in a cluster. Instead of recreating each secret manually, you can copy them between namespaces with a single command pipeline.

Original content from computingforgeeks.com - post 67730

Common use cases include sharing Docker registry pull secrets across development, staging, and production namespaces, distributing TLS certificates issued by cert-manager, replicating database credentials for microservices spread across namespaces, and sharing API keys or tokens used by multiple teams.

This guide covers multiple methods to copy Kubernetes secrets between namespaces – from simple one-liners to automated solutions using controllers and operators.

Prerequisites

Before you begin, make sure you have the following in place:

  • A running Kubernetes cluster (v1.28 or later recommended)
  • kubectl installed and configured with cluster access
  • jq installed for JSON processing (Method 2)
  • Permissions to read secrets in the source namespace and create secrets in the target namespace

For the examples in this guide, we will use two namespaces: source-ns and target-ns. Create them if they do not exist.

kubectl create namespace source-ns
kubectl create namespace target-ns

Create a test secret in the source namespace that we will use throughout this guide.

kubectl create secret generic app-credentials \
  --from-literal=username=admin \
  --from-literal=password=S3cureP@ss123 \
  --namespace=source-ns

Verify the secret was created.

kubectl get secret app-credentials -n source-ns -o yaml

Method 1: Copy Secret Using kubectl JSON Pipe

The simplest approach is to export the secret as JSON, remove the namespace-specific metadata fields, and pipe it directly into kubectl apply targeting the destination namespace. Note that the old --export flag was removed in Kubernetes 1.18, so we handle metadata cleanup manually.

kubectl get secret app-credentials -n source-ns -o json | \
  jq 'del(.metadata.namespace, .metadata.uid, .metadata.resourceVersion, .metadata.creationTimestamp, .metadata.annotations["kubectl.kubernetes.io/last-applied-configuration"])' | \
  kubectl apply -n target-ns -f -

This command does three things: extracts the secret from the source namespace as JSON, strips the fields that are unique to the original object (namespace, uid, resourceVersion, creationTimestamp), and applies the cleaned manifest to the target namespace.

Verify the secret now exists in the target namespace.

kubectl get secret app-credentials -n target-ns

Expected output:

NAME              TYPE     DATA   AGE
app-credentials   Opaque   2      5s

Method 2: Copy Secret Using jq with Full Metadata Cleanup

For a cleaner approach, use jq to rebuild the secret manifest with only the fields you need. This avoids carrying over any unwanted metadata or annotations from the source.

kubectl get secret app-credentials -n source-ns -o json | \
  jq '{apiVersion: .apiVersion, kind: .kind, metadata: {name: .metadata.name}, type: .type, data: .data}' | \
  kubectl apply -n target-ns -f -

This approach is safer because it constructs a minimal secret manifest containing only the API version, kind, name, type, and data fields. Nothing else from the original object is carried over.

If you want to rename the secret during the copy, modify the name field in the jq filter.

kubectl get secret app-credentials -n source-ns -o json | \
  jq '{apiVersion: .apiVersion, kind: .kind, metadata: {name: "app-credentials-copy"}, type: .type, data: .data}' | \
  kubectl apply -n target-ns -f -

Method 3: Copy Secret Using YAML Output

If you prefer working with YAML, you can use sed or grep to strip metadata fields. This method works without jq installed.

kubectl get secret app-credentials -n source-ns -o yaml | \
  grep -v '^\s*namespace:\|^\s*uid:\|^\s*resourceVersion:\|^\s*creationTimestamp:\|^\s*selfLink:' | \
  kubectl apply -n target-ns -f -

Alternatively, if you have yq installed (the YAML equivalent of jq), you can clean the metadata more precisely.

kubectl get secret app-credentials -n source-ns -o yaml | \
  yq 'del(.metadata.namespace, .metadata.uid, .metadata.resourceVersion, .metadata.creationTimestamp)' | \
  kubectl apply -n target-ns -f -

The yq approach is more reliable than grep because it understands YAML structure and will not accidentally remove data fields that happen to match the pattern.

Method 4: Copy TLS Secrets Between Namespaces

TLS secrets contain tls.crt and tls.key fields and have the type kubernetes.io/tls. Copying them follows the same pattern, but you must preserve the secret type to ensure Kubernetes treats it correctly.

First, check the TLS secret in the source namespace.

kubectl get secret my-tls-secret -n source-ns -o yaml

Copy the TLS secret to the target namespace while preserving all required fields.

kubectl get secret my-tls-secret -n source-ns -o json | \
  jq '{apiVersion: .apiVersion, kind: .kind, metadata: {name: .metadata.name}, type: .type, data: .data}' | \
  kubectl apply -n target-ns -f -

The type field is critical here. If you drop it, Kubernetes defaults to Opaque, and Ingress controllers or other components expecting a TLS secret will reject it.

Verify the copied secret has the correct type.

kubectl get secret my-tls-secret -n target-ns

Expected output showing the TLS type is preserved:

NAME            TYPE                DATA   AGE
my-tls-secret   kubernetes.io/tls   2      3s

If you manage TLS certificates with cert-manager, consider using its built-in secret syncing feature instead of manual copying. cert-manager can automatically replicate TLS secrets across namespaces using the reflector annotation.

Method 5: Copy Docker Registry Secrets

Docker registry secrets (type kubernetes.io/dockerconfigjson) store credentials for pulling container images from private registries. These are one of the most commonly copied secrets because every namespace that runs pods from a private registry needs its own copy.

Copy a registry secret from one namespace to another.

kubectl get secret regcred -n source-ns -o json | \
  jq '{apiVersion: .apiVersion, kind: .kind, metadata: {name: .metadata.name}, type: .type, data: .data}' | \
  kubectl apply -n target-ns -f -

To copy a registry secret to all namespaces in the cluster, loop through each namespace.

for ns in $(kubectl get namespaces -o jsonpath='{.items[*].metadata.name}'); do
  if [ "$ns" != "source-ns" ]; then
    kubectl get secret regcred -n source-ns -o json | \
      jq '{apiVersion: .apiVersion, kind: .kind, metadata: {name: .metadata.name}, type: .type, data: .data}' | \
      kubectl apply -n "$ns" -f -
  fi
done

After copying, verify the secret exists in a target namespace and confirm pods can pull images.

kubectl get secret regcred -n target-ns -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d | jq .

For more on configuring registry secrets, see our guide on adding Harbor registry pull secrets to Kubernetes.

Method 6: Automate with a Shell Script

When you regularly copy secrets across namespaces, a reusable shell script saves time and reduces errors. The following script accepts the secret name, source namespace, and target namespace as arguments.

#!/bin/bash
# copy-secret.sh - Copy a Kubernetes secret between namespaces

SECRET_NAME="$1"
SOURCE_NS="$2"
TARGET_NS="$3"

if [ -z "$SECRET_NAME" ] || [ -z "$SOURCE_NS" ] || [ -z "$TARGET_NS" ]; then
  echo "Usage: $0 <secret-name> <source-namespace> <target-namespace>"
  exit 1
fi

# Verify the source secret exists
if ! kubectl get secret "$SECRET_NAME" -n "$SOURCE_NS" > /dev/null 2>&1; then
  echo "Error: Secret '$SECRET_NAME' not found in namespace '$SOURCE_NS'"
  exit 1
fi

# Copy the secret
kubectl get secret "$SECRET_NAME" -n "$SOURCE_NS" -o json | \
  jq '{apiVersion: .apiVersion, kind: .kind, metadata: {name: .metadata.name}, type: .type, data: .data}' | \
  kubectl apply -n "$TARGET_NS" -f -

if [ $? -eq 0 ]; then
  echo "Secret '$SECRET_NAME' copied from '$SOURCE_NS' to '$TARGET_NS'"
else
  echo "Error: Failed to copy secret"
  exit 1
fi

Make the script executable and run it.

chmod +x copy-secret.sh
./copy-secret.sh app-credentials source-ns target-ns

Expected output:

secret/app-credentials configured
Secret 'app-credentials' copied from 'source-ns' to 'target-ns'

You can extend this script to copy a secret to multiple namespaces at once by accepting a comma-separated list of target namespaces or by looping through all namespaces in the cluster.

Method 7: Use Kubernetes Replicator or External Secrets Operator

Manual copying works for one-off tasks, but production environments usually need automatic synchronization. When a secret changes in the source namespace, all copies should update automatically. Two popular tools handle this.

Kubernetes Replicator

Kubernetes Replicator by Mittwald is a lightweight controller that watches for annotations on secrets and config maps, then replicates them across namespaces automatically. Install it with Helm.

helm repo add mittwald https://helm.mittwald.de
helm repo update
helm install kubernetes-replicator mittwald/kubernetes-replicator -n kube-system

To replicate a secret, add the replication annotation to the source secret. This uses push-based replication where the controller pushes the secret to all allowed namespaces.

apiVersion: v1
kind: Secret
metadata:
  name: shared-credentials
  namespace: source-ns
  annotations:
    replicator.v1.mittwald.de/replicate-to: "target-ns,staging,production"
type: Opaque
data:
  username: YWRtaW4=
  password: UzNjdXJlUEBzczEyMw==

You can also use pull-based replication where the target secret references the source. Create this in the target namespace.

apiVersion: v1
kind: Secret
metadata:
  name: shared-credentials
  namespace: target-ns
  annotations:
    replicator.v1.mittwald.de/replicate-from: source-ns/shared-credentials
type: Opaque
data: {}

The replicator controller will populate the data from the source secret and keep it synchronized whenever the source changes.

External Secrets Operator

If your secrets originate from an external secret management system like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault, the External Secrets Operator (ESO) is the better choice. ESO can also replicate secrets within the cluster using the Kubernetes provider.

Install External Secrets Operator with Helm.

helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace

Create a SecretStore that points to the source namespace, then define an ExternalSecret in the target namespace.

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: kubernetes-store
spec:
  provider:
    kubernetes:
      remoteNamespace: source-ns
      server:
        caProvider:
          type: ConfigMap
          name: kube-root-ca.crt
          key: ca.crt
          namespace: kube-system
      auth:
        serviceAccount:
          name: external-secrets
          namespace: external-secrets

Then create an ExternalSecret in the target namespace that references the source secret.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-credentials
  namespace: target-ns
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: kubernetes-store
    kind: ClusterSecretStore
  target:
    name: app-credentials
  dataFrom:
    - extract:
        key: app-credentials

ESO will create and maintain the secret in the target namespace, refreshing it based on the configured interval.

Kubernetes Secret Types Reference

The following table lists the built-in secret types in Kubernetes. When copying secrets, always preserve the type field to maintain compatibility with components that expect a specific type. See the official Kubernetes Secrets documentation for full details.

Secret TypeDescriptionRequired Keys
OpaqueDefault type for arbitrary user-defined dataNone
kubernetes.io/tlsTLS certificate and private keytls.crt, tls.key
kubernetes.io/dockerconfigjsonDocker registry credentials.dockerconfigjson
kubernetes.io/dockercfgLegacy Docker registry credentials.dockercfg
kubernetes.io/basic-authBasic authentication credentialsusername, password
kubernetes.io/ssh-authSSH authentication credentialsssh-privatekey
kubernetes.io/service-account-tokenService account tokentoken, ca.crt, namespace
bootstrap.kubernetes.io/tokenBootstrap token dataMultiple token fields

Conclusion

Copying Kubernetes secrets between namespaces is a routine task that ranges from simple one-liner commands to fully automated controller-based approaches. For quick, one-off copies, the kubectl get + jq pipeline (Method 2) gives you the cleanest result with minimal metadata carried over. For production clusters where secrets change frequently, deploy Kubernetes Replicator for in-cluster synchronization or External Secrets Operator when pulling from external vaults.

The key point to remember is to always preserve the secret type field when copying. Dropping it defaults the secret to Opaque, which breaks TLS and registry secrets that Ingress controllers and kubelet expect in a specific format.

Related guides you may find useful:

Related Articles

Containers Install Kubernetes Cluster using Talos Container Linux Containers Run Wazuh Server in Docker Container using Compose Containers Run Home Assistant in Docker and Docker Compose on Ubuntu 24.04 Automation Install Vault Cluster in GKE via Helm, Terraform and BitBucket Pipelines

Leave a Comment

Press ESC to close