Beyond Declarative vs Imperative's contrast, kubectl 5,150 offers three techniques, and the documentation warns against mixing them on one object:
| Technique | Example | Where the truth lives | Suits |
|---|---|---|---|
| Imperative commands | kubectl create namespace booknest | Only in the cluster | One-off tasks, exploration |
| Imperative object configuration | kubectl create -f / replace -f | Files, one operation each | Simple scripts |
| Declarative object configuration | kubectl apply -f dir/ | Files, merged with the live object | Git 1,932 , CI, teams |
The difference shows as soon as you run the same thing twice, or switch techniques halfway:
kubectl create namespace booknest
kubectl create configmap settings -n booknest --from-literal=PAGE_SIZE=20 \
--dry-run=client -o yaml > /tmp/settings.yaml
kubectl create -f /tmp/settings.yaml && kubectl create -f /tmp/settings.yaml
kubectl apply -f /tmp/settings.yamlOutput
namespace/booknest created configmap/settings created Error from server (AlreadyExists): error when creating "/tmp/settings.yaml": configmaps "settings" already exists Warning: resource configmaps/settings is missing the kubectl.kubernetes.io/last-applied-configuration annotation ... The missing annotation will be patched automatically. configmap/settings configured
create is an action, so the second one fails. apply records what you last applied in the last-applied-configuration annotation, which create never wrote, so it warns and patches it in. That record drives a three-way merge of your file, the previous file and the live object: a field disappears only when you remove it from your file. --dry-run=client -o yaml bridges the styles: a command writes the manifest you keep.