Intro
Kubectl plugins extend the Kubernetes command-line tool with custom subcommands, enabling operators and developers to automate repetitive workflows, integrate with external systems, and encapsulate complex logic. While basic plugin creation is straightforward, advanced concepts such as plugin discovery, argument parsing, environment inheritance, and lifecycle management are essential for building reliable and maintainable tools.
This article targets developers, DevOps consultants, and technical startup teams who need to move beyond simple scripts and understand how kubectl plugins work under the hood. We explore the plugin protocol, the execution environment, versioning strategies, and common failure modes, all grounded in practical examples you can run in your own cluster.
The goal is operational safety: observe before changing, limit the blast radius, use placeholders instead of secrets, verify the result, and document how to recover if the expected state is not reached. Every concept is paired with concrete commands and expected output so you can validate your understanding in a real environment.
Version and Environment Inventory
Before working with kubectl plugins, you must establish a clear baseline: the kubectl version, the plugin discovery mechanism, the operating system and shell, and the permissions available to the plugin. This inventory prevents mismatches where a plugin assumes a newer kubectl feature or a different filesystem layout.
Start by verifying your kubectl version and plugin search path:
kubectl version --client
# Example output:
# Client Version: v1.28.2
# Kustomize Version: v5.0.3
The plugin search path is determined by the PATH environment variable. List all currently discoverable plugins:
kubectl plugin list
# Example output:
# The following compatible plugins are available:
# /home/operator/.krew/bin/kubectl-access_matrix
# /home/operator/.krew/bin/kubectl-advise_psp
If a plugin does not appear, ensure its executable bit is set and its directory is on PATH. For a single plugin file named kubectl-whoami, check permissions and location:
ls -l /usr/local/bin/kubectl-whoami
# Expected: -rwxr-xr-x 1 root root 1234 Jan 1 12:00 /usr/local/bin/kubectl-whoami
To inspect the plugin's runtime environment, create a diagnostic plugin that prints key variables. Save the following as kubectl-envtest and make it executable:
#!/bin/bash
set -euo pipefail
echo "KUBECTL_PLUGINS_CALLER: $KUBECTL_PLUGINS_CALLER"
echo "KUBECTL_PLUGINS_LOCAL_FLAG: $KUBECTL_PLUGINS_LOCAL_FLAG"
echo "KUBECTL_PLUGINS_DESCRIPTIVE_COMMAND: $KUBECTL_PLUGINS_DESCRIPTIVE_COMMAND"
echo "KUBECTL_PLUGINS_GLOBAL_FLAG_KUBECONFIG: $KUBECTL_PLUGINS_GLOBAL_FLAG_KUBECONFIG"
Run it via kubectl to see how the plugin protocol injects context:
kubectl envtest --kubeconfig=/tmp/test-kubeconfig
# Expected output includes the caller path, the descriptive command, and the global flag value.
Keep the local test small: apply one manifest, inspect the generated resources, and verify traffic with kubectl port-forward or a local service type before moving to a cloud load balancer or ingress controller. The same principle applies to plugins: test them against a local cluster first, then scale to shared environments.
Safe Configuration Path
Advanced kubectl plugins often need configuration, such as API endpoints, credentials, or feature flags. A safe configuration path avoids secrets in script files, uses environment variables for runtime values, and separates cluster access from plugin logic.
Configuration Sources
Plugins inherit the kubeconfig context that kubectl uses, but they may also read their own configuration files. Prefer placing plugin-specific settings in ~/.kube/plugins/<plugin-name>/config.yaml to avoid cluttering the main kubeconfig. For global settings, rely on environment variables that are explicitly whitelisted by the plugin.
Here is a typical configuration loader pattern in a Bash plugin:
#!/bin/bash
set -euo pipefail
PLUGIN_CONFIG_DIR="${HOME}/.kube/plugins/kubectl-access-check"
PLUGIN_CONFIG_FILE="${PLUGIN_CONFIG_DIR}/config.yaml"
if [[ ! -f "$PLUGIN_CONFIG_FILE" ]]; then
echo "Error: config file not found at $PLUGIN_CONFIG_FILE" >&2
exit 1
fi
# Parse with yq (must be installed)
API_ENDPOINT=$(yq eval '.endpoint' "$PLUGIN_CONFIG_FILE")
TIMEOUT=$(yq eval '.timeout_seconds // 30' "$PLUGIN_CONFIG_FILE")
echo "Using endpoint=$API_ENDPOINT timeout=$TIMEOUT"
For sensitive values, never hard-code tokens in the plugin. Instead, require an environment variable and fail fast if missing:
if [[ -z "${PLUGIN_ACCESS_TOKEN:-}" ]]; then
echo "Error: PLUGIN_ACCESS_TOKEN must be set" >&2
exit 1
fi
Least Privilege and Context Switching
When a plugin needs elevated permissions, design it to use a dedicated kubeconfig context with the minimal required RBAC roles. For example:
kubectl --context restricted-user auth can-i list pods --namespace team-a
# Expected: yes or no
Before making changes, verify the current context:
kubectl config current-context
# Example output: gke_my-project_us-central1_cluster-1
If the plugin performs mutations, implement a dry-run flag using kubectl apply --dry-run=client or the server-side dry-run with --dry-run=server. For example, a plugin that scales deployments can support --dry-run:
kubectl scale deployment nginx --replicas=3 --dry-run=client -o yaml
# Shows the would-be change without applying it.
Verification and Diagnostics
Reliable plugins include built-in verification steps that check prerequisites, validate input, and confirm the result of any mutation. This section provides diagnostic patterns you can embed in your plugins.
Preflight Checks
Before executing the main logic, verify that required tools are installed and the cluster is reachable:
#!/bin/bash
set -euo pipefail
command -v kubectl >/dev/null || { echo "kubectl not found"; exit 1; }
command -v jq >/dev/null || { echo "jq not found"; exit 1; }
kubectl cluster-info --request-timeout=5s
# Example output:
# Kubernetes control plane is running at https://1.2.3.4
Observing Plugin Behavior
To debug plugin execution, set the KUBECTL_PLUGINS_VERBOSE environment variable before invoking a plugin. Kubectl will print the resolved plugin path and arguments:
KUBECTL_PLUGINS_VERBOSE=1 kubectl myplugin --flag value
# Output includes:
# Resolved plugin: /usr/local/bin/kubectl-myplugin
# Running plugin with args: myplugin --flag value
Post-Execution Verification
After a plugin changes a resource, verify the new state:
kubectl get deployment nginx -o jsonpath='{.spec.replicas}{"\n"}'
# Expected if changed to 3: 3
For pods, ensure they are ready:
kubectl wait --for=condition=Ready pod -l app=nginx --timeout=120s
# Expected: pod/nginx-77b4fdf86c-abcde condition met
Structured Diagnostics
Emit structured logs from your plugin using a logging library that writes to stderr, keeping stdout clean for output that may be piped. Example in Python plugin:
import sys, json, logging
logging.basicConfig(stream=sys.stderr, level=logging.INFO, format='%(levelname)s: %(message)s')
logging.info("Starting access check")
result = {"allowed": True, "reason": "RBAC permits list pods"}
print(json.dumps(result)) # stdout for output
Run it and separate streams:
kubectl access-check --namespace team-a 1>/tmp/out.json 2>/tmp/err.log
cat /tmp/out.json
# Expected: {"allowed": true, "reason": "RBAC permits list pods"}
cat /tmp/err.log
# Expected: INFO: Starting access check
Failure Modes and Recovery
Plugins fail for many reasons: missing dependencies, incorrect kubeconfig contexts, permission errors, network timeouts, or bugs in the plugin code. Anticipating these failures and providing clear recovery paths is an advanced but essential discipline.
Common Failure Modes
- Plugin not found:
kubectlsayserror: executable kubectl-myplugin not found. Checkkubectl plugin listand ensure the executable is onPATHwith the correct name and permissions. - Missing dependency: Plugin uses
jq,yq, orpython, but it is not installed. Preflight checks should catch this and print a helpful message. - Wrong context: Plugin operates on production despite expecting development. Always print the current context and require a confirmation flag for destructive operations.
- Partial failure: A plugin creates some resources then fails. Use idempotent operations or rollback logic.
- Network timeout: Cluster API unreachable. Implement retries with exponential backoff and a clear timeout.
Recovery Strategies
1. Dry-Run and Rollback
Whenever a plugin modifies resources, support a --dry-run flag and log the exact kubectl commands issued. In case of failure, you can manually revert using the recorded commands. For example, if a plugin fails after applying a manifest, retrieve the last applied configuration:
kubectl apply view-last-applied deployment nginx -o yaml > /tmp/nginx-last-applied.yaml
Then reapply a known-good version:
kubectl apply -f /tmp/nginx-good.yaml
2. Namespace Isolation
During development, run plugins against a dedicated namespace to limit blast radius. If a plugin misbehaves, delete the namespace:
kubectl create namespace plugin-test
kubectl config set-context --current --namespace=plugin-test
# Run plugin...
kubectl delete namespace plugin-test # cleanup
3. Graceful Error Handling in Code
Implement error trapping in Bash plugins to provide hints:
#!/bin/bash
set -euo pipefail
trap 'echo "Error occurred line $LINENO: $BASH_COMMAND" >&2' ERR
kubectl apply -f "$1"
When a command fails, the user sees the exact line and command. For complex plugins, write unit tests using a framework like Bats. Example test file test_plugin.bats:
@test "plugin fails when token missing" {
run kubectl myplugin
[ "$status" -eq 1 ]
[[ "$output" == *"PLUGIN_ACCESS_TOKEN must be set"* ]]
}
Run tests:
bats test_plugin.bats
# Expected: 1 test, 0 failures
Operations Checklist
Use this checklist before deploying a kubectl plugin to production. Replace placeholder values with your own concrete details, as shown in the example entries.
| # | Check Item | Concrete Example / Command | Expected Result | Owner |
|---|---|---|---|---|
| 1 | Confirm kubectl version compatibility | kubectl version --client | v1.28.x or higher as per plugin requirement | Priya Shah, Engineering Lead |
| 2 | Verify plugin executable permissions and location | ls -l /usr/local/bin/kubectl-myplugin | -rwxr-xr-x and on PATH | Priya Shah |
| 3 | List discovered plugins | kubectl plugin list | Plugin appears in list | DevOps team |
| 4 | Check required dependencies | command -v jq yq python3 | All commands return paths | DevOps team |
| 5 | Validate configuration file syntax | yq eval '.' ~/.kube/plugins/myplugin/config.yaml | No parse errors | Developer |
| 6 | Test against a scratch namespace | kubectl create namespace plugin-verify && kubectl myplugin --namespace plugin-verify | Plugin runs without error | Developer |
| 7 | Verify preflight checks run | Run plugin with missing dependency intentionally | Plugin exits with clear message, non-zero code | Priya Shah |
| 8 | Test dry-run mode | kubectl myplugin --dry-run | Outputs intended changes without applying | QA Engineer |
| 9 | Check RBAC permissions of plugin's kubeconfig context | kubectl auth can-i --list --namespace target | Lists only required permissions | Security Lead |
| 10 | Confirm rollback procedure | Simulate failure and execute revert commands | State restored to pre-run snapshot | DevOps team |
| 11 | Log plugin execution for audit | script -c "kubectl myplugin" /var/log/plugin-run.log | Log file contains full command and output | DevOps team |
| 12 | Run plugin unit tests (Bats/Pytest) | bats test_plugin.bats | All tests pass | Developer |
| 13 | Perform a canary run on one cluster node or namespace | Run plugin in dedicated canary namespace | No impact on other namespaces | QA Engineer |
| 14 | Document version and change log | Update README with new version and changes | Changelog reflects current version | Priya Shah |
| 15 | Schedule regular plugin updates | Add to sprint backlog or cron for check updates | Update check runs weekly | Engineering Manager |
Conclusion
Advanced kubectl plugins are powerful tools for Kubernetes automation, but they demand rigorous engineering practices. By understanding the plugin protocol, managing configuration safely, building in verification, and planning for failure recovery, you can create plugins that are reliable, secure, and maintainable.
As a next step, choose one low-risk verification from this article: inspect your current plugin environment with kubectl plugin list and kubectl version --client, then write a simple diagnostic plugin that prints its runtime environment. Run it against a local cluster and observe the output. From there, gradually add preflight checks, dry-run support, and error handling.
A reliable technical workflow makes failure visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision. Apply these principles to every plugin you build or adopt, and you will raise the operational bar for your entire team.