Create an imagePullSecret with kubectl
To pull images from a private registry, a Kubernetes cluster needs a Secret of type kubernetes.io/dockerconfigjson holding the registry login — commonly called an imagePullSecret. Without it, a Pod stays in ImagePullBackOff and the application never starts. This applies whether you're a developer deploying an app that uses a private image, a platform engineer preparing the Secret for several namespaces, or a CI/CD pipeline that re-creates it every time credentials rotate. By the end of this tutorial, you have a Secret in your namespace and a workload that successfully pulls a private image from Container Registry.
Prerequisites
kubectlis connected to your cluster. Check withkubectl get nodes.- You have permission to create Secrets in the target namespace. Check with
kubectl auth can-i create secret -n <namespace>. - You have Container Registry login credentials. On the FPT Portal, go to Container Registry > Get Token.
Use a Robot Account scoped to pull-only access for this instead of a personal account. See Create a robot account.
Create the Secret
kubectl create secret docker-registry regcred \
--docker-server=registry.fptcloud.com \
--docker-username='robot$my-project+ci' \
--docker-password='Xy9!kP2$mQ' \
--namespace=my-app
A successful run returns:
secret/regcred created
Three details here commonly produce a Secret that looks fine but doesn't authenticate.
- Always wrap the password in single quotes (
'...'). Characters like$,!, or&left unquoted get altered by the shell, so the Secret stores the wrong value even though the command reports success. --docker-servermust match only the host part at the start of the image name — nohttps://prefix, no project name.--namespacemust be the same namespace the workload runs in. A Secret doesn't work across namespaces.
Pass the username and password through environment variables instead of typing them directly into the command, and never commit a YAML file containing .dockerconfigjson to Git.
See imagePullSecret field reference for every flag kubectl create secret docker-registry accepts, and for the Secret naming rules.
Attach the Secret to a workload
Add imagePullSecrets to the Pod template:
spec:
template:
spec:
imagePullSecrets:
- name: regcred
containers:
- name: api
image: registry.fptcloud.com/my-project/api:v1
kubectl apply -f deployment.yaml
If several workloads in the same namespace pull from the same registry, attach the Secret to the ServiceAccount instead of editing every manifest:
kubectl patch serviceaccount default -n my-app \
-p '{"imagePullSecrets":[{"name":"regcred"}]}'
Patching the ServiceAccount only affects Pods created after the patch. A Pod that's already running needs kubectl rollout restart to pick it up.
Verify the Secret works
kubectl get pods -n my-app
| Status | Meaning | Action |
|---|---|---|
ContainerCreating | Still pulling. | Wait and check again. |
Running | The pull succeeded. | Nothing else needed. |
ErrImagePull | The first pull attempt failed. | See Troubleshooting. |
ImagePullBackOff | Kubernetes retried and gave up. | See Troubleshooting. |
To see the exact cause, check the Pod's Events:
kubectl describe pod <pod-name> -n my-app | tail -20
The Events section at the end distinguishes an authentication failure from an image that doesn't exist.
To inspect the credentials stored in the Secret:
kubectl get secret regcred -n my-app \
-o jsonpath='{.data.\.dockerconfigjson}' | base64 -d
A correct result contains an auths block with the registry address and a non-empty auth field, and the Pod reaches Running.
Create the Secret from a Docker config file
Use this instead when the password contains characters that are hard to escape, or when you already ran docker login.
docker login registry.fptcloud.com
kubectl create secret docker-registry regcred \
--from-file=$HOME/.docker/config.json \
--namespace=my-app
Check the file before using this method:
cat ~/.docker/config.json
If the file contains "credsStore" instead of an "auths" block with an "auth" field, Docker is storing the credential in the OS keychain instead. The Secret created from it would be empty — use the kubectl create secret docker-registry command from the previous section instead.
Re-create or reuse the Secret across namespaces
Re-running kubectl create secret on a Secret that already exists fails with AlreadyExists. In a script or CI/CD pipeline, pipe to kubectl apply instead so the command is safe to run repeatedly:
kubectl create secret docker-registry regcred \
--docker-server=registry.fptcloud.com \
--docker-username="$REG_USER" \
--docker-password="$REG_PASS" \
--namespace=my-app \
--dry-run=client -o yaml | kubectl apply -f -
A Secret doesn't work across namespaces, so repeat the same command for each namespace that needs it:
for ns in app-dev app-staging app-prod; do
kubectl create secret docker-registry regcred \
--docker-server=registry.fptcloud.com \
--docker-username="$REG_USER" \
--docker-password="$REG_PASS" \
--namespace="$ns" \
--dry-run=client -o yaml | kubectl apply -f -
done
Update the Secret after rotating credentials
A Pod that's already running doesn't pick up a rotated credential on its own. After you re-create or update the Secret with new credentials, restart the workload so it reloads:
kubectl rollout restart deployment/api -n my-app
Next steps
- Manage repositories — view and clean up the Repositories your cluster pulls from.
- Troubleshooting — fix a Secret that doesn't authenticate.