{"id":24308946,"url":"https://github.com/hashmap-kz/kubectl-envsubst","last_synced_at":"2025-09-26T14:31:21.623Z","repository":{"id":270487675,"uuid":"910470799","full_name":"hashmap-kz/kubectl-envsubst","owner":"hashmap-kz","description":"A kubectl plugin for substituting environment variables in Kubernetes manifests before applying them.","archived":false,"fork":false,"pushed_at":"2025-01-08T04:43:35.000Z","size":234,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-08T05:26:27.194Z","etag":null,"topics":["golang","kubectl","kubectl-plugin","kubectl-plugins","kubernetes"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hashmap-kz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-31T11:09:58.000Z","updated_at":"2025-01-08T04:43:35.000Z","dependencies_parsed_at":"2024-12-31T15:38:44.494Z","dependency_job_id":null,"html_url":"https://github.com/hashmap-kz/kubectl-envsubst","commit_stats":null,"previous_names":["hashmap-kz/kubectl-envsubst"],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashmap-kz%2Fkubectl-envsubst","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashmap-kz%2Fkubectl-envsubst/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashmap-kz%2Fkubectl-envsubst/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hashmap-kz%2Fkubectl-envsubst/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hashmap-kz","download_url":"https://codeload.github.com/hashmap-kz/kubectl-envsubst/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234314442,"owners_count":18812697,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","kubectl","kubectl-plugin","kubectl-plugins","kubernetes"],"created_at":"2025-01-17T05:11:58.497Z","updated_at":"2025-09-26T14:31:21.617Z","avatar_url":"https://github.com/hashmap-kz.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# **kubectl-envsubst**\n\n_A `kubectl` plugin for substituting environment variables in Kubernetes manifests before applying them._\n\n[![License](https://img.shields.io/github/license/hashmap-kz/kubectl-envsubst)](https://github.com/hashmap-kz/kubectl-envsubst/blob/master/LICENSE)\n[![Go Report Card](https://goreportcard.com/badge/github.com/hashmap-kz/kubectl-envsubst)](https://goreportcard.com/report/github.com/hashmap-kz/kubectl-envsubst)\n[![Workflow Status](https://img.shields.io/github/actions/workflow/status/hashmap-kz/kubectl-envsubst/ci.yml?branch=master)](https://github.com/hashmap-kz/kubectl-envsubst/actions/workflows/ci.yml?query=branch:master)\n[![codecov](https://codecov.io/gh/hashmap-kz/kubectl-envsubst/branch/master/graph/badge.svg)](https://codecov.io/gh/hashmap-kz/kubectl-envsubst)\n[![GitHub Issues](https://img.shields.io/github/issues/hashmap-kz/kubectl-envsubst)](https://github.com/hashmap-kz/kubectl-envsubst/issues)\n[![Go Version](https://img.shields.io/github/go-mod/go-version/hashmap-kz/kubectl-envsubst)](https://github.com/hashmap-kz/kubectl-envsubst/blob/master/go.mod#L3)\n[![Latest Release](https://img.shields.io/github/v/release/hashmap-kz/kubectl-envsubst)](https://github.com/hashmap-kz/kubectl-envsubst/releases/latest)\n\n---\n\n## Table of Contents\n\n- [Features](#features)\n- [Installation](#installation)\n    - [Using Krew](#using-krew)\n    - [Manual Installation](#manual-installation)\n    - [Package-Based Installation](#package-based-installation-for-cicd-pipelines-example-for-alpine-linux)\n- [Flags](#flags)\n- [Usage](#usage-examples)\n    - [Basic Usage](#basic-substitution-example)\n    - [Substitution Along with Other `kubectl apply` Options](#substitution-along-with-other-kubectl-apply-options)\n    - [Advanced Usage](#advanced-usage-typical-scenario-in-cicd)\n- [Implementation details](#implementation-details)\n    - [Variable expansion behaviour](#variable-expansion-and-filtering-behavior)\n- [Brief conclusion](#brief-conclusion)\n- [Contributing](#contributing)\n- [License](#license)\n- [Additional resources](#additional-resources)\n\n---\n\n## **Features**\n\n- **Environment Variable Substitution**: Replaces placeholders in Kubernetes manifests with environment variable values.\n- **Allowed Variable Filtering**: Allows you to control substitutions by specifying prefixes or permitted\n  variables.\n- **Strict Mode**: Fails if any placeholders remain unexpanded, ensuring deployment predictability.\n- **Seamless Integration**: Works with all `kubectl` arguments, that may be used in `apply`.\n- **Zero Dependencies**: No external tools or libraries are required, simplifying installation and operation. The plugin\n  is designed primarily for CI/CD, with a focus on minimizing binary size.\n\n---\n\n## **Installation**\n\n### Using `krew`\n\n1. Install the [Krew](https://krew.sigs.k8s.io/docs/user-guide/setup/) plugin manager if you haven’t already.\n2. Run the following command:\n   ```bash\n   kubectl krew install envsubst\n   ```\n3. Verify installation:\n   ```bash\n   kubectl envsubst --version\n   ```\n\n### Manual Installation\n\n1. Download the latest binary for your platform from\n   the [Releases page](https://github.com/hashmap-kz/kubectl-envsubst/releases).\n2. Place the binary in your system's `PATH` (e.g., `/usr/local/bin`).\n3. Example installation script for Unix-Based OS:\n   ```bash\n   (\n     set -euo pipefail\n\n     OS=\"$(uname | tr '[:upper:]' '[:lower:]')\"\n     ARCH=\"$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\\(arm\\)\\(64\\)\\?.*/\\1\\2/' -e 's/aarch64$/arm64/')\"\n     TAG=\"$(curl -s https://api.github.com/repos/hashmap-kz/kubectl-envsubst/releases/latest | jq -r .tag_name)\"\n\n     curl -L \"https://github.com/hashmap-kz/kubectl-envsubst/releases/download/${TAG}/kubectl-envsubst_${TAG}_${OS}_${ARCH}.tar.gz\" |\n       tar -xzf - -C /usr/local/bin \u0026\u0026 chmod +x /usr/local/bin/kubectl-envsubst\n   )\n   ```\n4. Verify installation:\n   ```bash\n   kubectl envsubst --version\n   ```\n\n### Package-Based Installation (for CI/CD pipelines, example for Alpine-Linux)\n\n```bash\napk update \u0026\u0026 apk add --no-cache bash curl\ncurl -LO https://github.com/hashmap-kz/kubectl-envsubst/releases/latest/download/kubectl-envsubst_linux_amd64.apk\napk add kubectl-envsubst_linux_amd64.apk --allow-untrusted\n```\n\n---\n\n## **Flags**:\n\n### **`--envsubst-allowed-vars`**\n\n- **Description**: Specifies a comma-separated list of variable names that are explicitly allowed for substitution.\n- **Corresponding environment variable**: **`ENVSUBST_ALLOWED_VARS`**\n- **Usage**:\n  ```bash\n  # Using CLI options\n  kubectl envsubst apply -f deployment.yaml \\\n    --envsubst-allowed-vars=IMAGE_NAME,IMAGE_TAG,APP_NAME,PKEY_PATH\n\n  # Using environment variables\n  export ENVSUBST_ALLOWED_VARS='IMAGE_NAME,IMAGE_TAG,APP_NAME,PKEY_PATH'\n  kubectl envsubst apply -f deployment.yaml\n  ```\n- **Behavior**:\n    - Variables not included in this list will not be substituted.\n    - Useful for ensuring only specific variables are processed, preventing accidental substitutions.\n\n---\n\n### **`--envsubst-allowed-prefixes`**\n\n- **Description**: Specifies a comma-separated list of prefixes to filter variables by name.\n- **Corresponding environment variable**: **`ENVSUBST_ALLOWED_PREFIXES`**\n- **Usage**:\n  ```bash\n  # Using CLI options\n  kubectl envsubst apply -f deployment.yaml \\\n    --envsubst-allowed-prefixes=CI_,APP_,IMAGE_\n\n  # Using environment variables\n  export ENVSUBST_ALLOWED_PREFIXES='CI_,APP_,IMAGE_'\n  kubectl envsubst apply -f deployment.yaml\n  ```\n- **Behavior**:\n    - Only variables with names starting with one of the specified prefixes will be substituted.\n    - Variables without matching prefixes will be ignored.\n\n---\n\n### **Note: CLI Takes Precedence Over Environment Variables**\n\n- **Priority**: If both CLI flags and environment variables are set:\n    - **CLI flags** (`--envsubst-allowed-vars`, `--envsubst-allowed-prefixes`) will **override** their respective\n      environment variable values.\n    - This ensures explicit command-line options have the highest priority.\n\n---\n\n## Usage Examples\n\n### **Basic Substitution Example**\n\nGiven a manifest file `deployment.yaml`:\n\n```yaml\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: ${APP_NAME}\n  labels:\n    app: ${APP_NAME}\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: ${APP_NAME}\n  template:\n    metadata:\n      labels:\n        app: ${APP_NAME}\n    spec:\n      containers:\n        - name: ${APP_NAME}\n          image: ${IMAGE_NAME}:${IMAGE_TAG}\n```\n\n#### Export required environment variables:\n\n```bash\nexport APP_NAME=my-app\nexport IMAGE_NAME=nginx\nexport IMAGE_TAG='1.27.3-bookworm'\n```\n\n#### Substitution with Allowed Variables\n\n```bash\nkubectl envsubst apply --filename=deployment.yaml \\\n  --envsubst-allowed-vars=APP_NAME,IMAGE_NAME,IMAGE_TAG\n```\n\n#### Substitution with Allowed Prefixes\n\n```bash\nkubectl envsubst apply --filename=deployment.yaml \\\n  --envsubst-allowed-prefixes=APP_,IMAGE_\n```\n\n---\n\n### **Substitution Along with Other `kubectl apply` Options**\n\n```bash\n# Configure CLI for Prefix Substitution:\nexport ENVSUBST_ALLOWED_PREFIXES='APP_,IMAGE_'\n```\n\n```bash\n# Apply resources in dry-run mode to see the expected output before applying:\nkubectl envsubst apply -f deployment.yaml \\\n  --dry-run=client -o yaml\n```\n\n```bash\n# Recursively process all files in a directory while using dry-run mode:\nkubectl envsubst apply -f manifests/ --recursive \\\n  --dry-run=client -o yaml\n```\n\n```bash\n# Use redirection from stdin to apply the manifest:\ncat deployment.yaml | kubectl envsubst apply -f -\n```\n\n```bash\n# Process and apply a manifest located on a remote server:\nkubectl envsubst apply \\\n  -f https://raw.githubusercontent.com/user/repo/refs/heads/master/manifests/deployment.yaml\n```\n\n```bash\n# Using with 'kubectl kustomize'\nkubectl kustomize manifests/ | kubectl envsubst apply -f -\n```\n\n---\n\n### **Advanced usage (typical scenario in CI/CD)**\n\nA typical setup for a microservice with dev, stage, and prod environments may look like this:\n\n```\n.\n├── cmd\n│   └── auth-svc.go\n├── go.mod\n├── go.sum\n├── .gitlab-ci.yml\n├── k8s-manifests\n│   ├── dev\n│   │   └── manifests.yaml\n│   ├── prod\n│   │   ├── hpa.yaml\n│   │   ├── manifests.yaml\n│   │   └── secrets.yaml\n│   └── stage\n│       └── manifests.yaml\n├── README.md\n```\n\nEach environment (dev, stage, prod) typically includes a set of Kubernetes manifests.\nThese manifests may differ between environments in minor ways, such as image names in the registry,\nor more significantly, with specific resources like HPAs or secrets for production.\n\nHowever, most parts of the manifests, such as services, deployments, ingresses, secrets,\nlabels, and naming conventions, are often duplicated across environments.\nThese duplicated elements can easily be replaced using environment variables in your CI/CD pipeline.\n\nThe specific CI/CD tool you use doesn’t matter, as each tool may provide different\nvariable names and patterns. In this example, project details like name, path, and\nlabels remain consistent across environments, while variables like image names or\nenvironment-specific resources can be adjusted based on your needs.\n\nA set of application deployment manifests:\n\n```yaml\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: \u0026app ${CI_PROJECT_NAME}\n  labels:\n    app: *app\nspec:\n  ports:\n    - port: 8080\n      targetPort: 8080\n      name: http\n  selector:\n    app: *app\n\n---\napiVersion: v1\nkind: Secret\nmetadata:\n  name: \u0026app ${CI_PROJECT_NAME}\n  labels:\n    app: *app\ntype: Opaque\nstringData:\n  vault_path: \"secret/${CI_PROJECT_PATH}/${CI_COMMIT_REF_NAME}\"\n\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: \u0026app ${CI_PROJECT_NAME}\n  labels:\n    app: *app\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: *app\n  template:\n    metadata:\n      labels:\n        app: *app\n    spec:\n      containers:\n        - name: *app\n          # image name for each environment is different (dev, stage, prod)\n          image: ${APP_IMAGE}\n          imagePullPolicy: Always\n          ports:\n            - containerPort: 8080\n              name: http\n          envFrom:\n            - secretRef:\n                name: *app\n```\n\nLet’s assume we’re deploying our application using GitLab CI (the specific tool doesn’t matter, it’s just an example).\n\nThe CI/CD stage may look like this:\n\n```yaml\ndeploy:\n  stage: deploy\n  before_script:\n    - apk update \u0026\u0026 apk add --no-cache bash curl\n    # setup kubectl\n    - curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl\n    - chmod +x ./kubectl \u0026\u0026 cp ./kubectl /usr/local/bin\n    # setup kubectl-envsubst plugin (using latest release and *.apk package)\n    - curl -LO https://github.com/hashmap-kz/kubectl-envsubst/releases/latest/download/kubectl-envsubst_linux_amd64.apk\n    - apk add kubectl-envsubst_linux_amd64.apk --allow-untrusted\n  tags:\n    - dind\n  environment:\n    name: $CI_COMMIT_REF_NAME\n  script:\n    - export APP_NAMESPACE=\"${CI_PROJECT_ROOT_NAMESPACE}-${CI_COMMIT_REF_NAME}\"\n    - export ENVSUBST_ALLOWED_PREFIXES='CI_,APP_,INFRA_,IMAGE_'\n    # create namespace, setup context\n    - kubectl create ns \"${APP_NAMESPACE}\" --dry-run=client -oyaml | kubectl apply -f -\n    - kubectl config set-context --current --namespace=\"${APP_NAMESPACE}\"\n    # substitute and apply manifests\n    - kubectl envsubst apply -f \"k8s-manifests/${CI_COMMIT_REF_NAME}\"\n    - kubectl rollout restart deploy \"${CI_PROJECT_NAME}\"\n```\n\n---\n\n## **Implementation details**\n\n### **Variable Expansion and Filtering Behavior**\n\n#### **Description**\n\nThe behavior of variable substitution is determined by the inclusion of variables in the `--envsubst-allowed-vars` or\n`--envsubst-allowed-prefixes` lists and whether the variables remain unresolved.\n\nSubstitution of environment variables without verifying their inclusion in a filter list is intentionally\navoided, as this behavior can lead to subtle errors.\n\nIf a variable is not found in the filter list, an error will be returned.\n\nExpanding manifests with all available environment variables can work fine\nfor simple cases, such as when your manifest contains only a service and a deployment\nwith a few variables to substitute.\nHowever, this approach becomes challenging to debug when dealing with more complex\nscenarios, such as applying dozens of manifests involving ConfigMaps, Secrets, CRDs, and other resources.\n\nIn such cases, you may not have complete confidence that all substitutions were\nperformed as expected.\nFor this reason, it’s better to explicitly control which variables are substituted by using a filter list.\n\n#### **Behavior**\n\n1. **Variables Included in Filters (Allowed for Substitution):**\n    - Variables listed in `--envsubst-allowed-vars` or matching a prefix in `--envsubst-allowed-prefixes`:\n        - **If Unexpanded**: This will result in an **error** during the substitution process.\n        - **Reason**: The error ensures all explicitly allowed variables are resolved to avoid deployment issues.\n\n2. **Variables Excluded from Filters (Not Allowed for Substitution):**\n    - Variables not listed in `--envsubst-allowed-vars` and not matching any prefix in `--envsubst-allowed-prefixes`:\n        - **Behavior**:\n            - The variable remains unexpanded.\n            - This does **not trigger an error** during substitution.\n            - Kubernetes deployment may fail if unresolved placeholders are incompatible with the manifest structure.\n\n3. **Expected Behavior for Specific Use Cases:**\n    - Certain placeholders, such as those in annotations, are intentionally **not expanded** unless explicitly allowed.\n    - Example:\n      ```yaml\n      annotations:\n        some.controller.annotation/snippet: |\n          set $agentflag 0;\n          if ($http_user_agent ~* \"(Android|iPhone|Windows Phone|UC|Kindle)\" ) {\n            set $agentflag 1;\n          }\n          if ( $agentflag = 1 ) {\n            return 301 http://m.company.org;\n          }\n      ```\n    - In the above case, placeholders remain unchanged unless explicitly allowed\n      via `--envsubst-allowed-vars` or `--envsubst-allowed-prefixes`.\n      This ensures manifest consistency and aligns with expected behavior.\n\n---\n\n## **Brief conclusion**\n\nWe use plain Kubernetes manifests without any complex preprocessing, relying on environment variables to\nmanage deployments across different environments (dev, stage, prod, etc.).\n\nThis approach ensures variable substitution is controlled and predictable.\nFor example, if an application includes a ConfigMap for Nginx (which uses $ frequently),\nsubstitutions won’t occur unless explicitly allowed by adding the relevant variables to\nan allow-list or prefix-list.\n\n---\n\n## **Contributing**\n\nWe welcome contributions! To contribute: see the [Contribution](CONTRIBUTING.md) guidelines.\n\n---\n\n## **License**\n\nThis project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.\n\n---\n\n## **Additional Resources**\n\n- [Kubernetes Documentation: Managing Resources](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/)\n- [Using Environment Variables in CI/CD](https://12factor.net/config)\n\nFor more information, visit the [project repository](https://github.com/hashmap-kz/kubectl-envsubst).\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhashmap-kz%2Fkubectl-envsubst","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhashmap-kz%2Fkubectl-envsubst","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhashmap-kz%2Fkubectl-envsubst/lists"}