A Helm plugin that uses Common Expression Language (CEL) to validate values. Instead of using JSON Schema in values.schema.json, you can write more expressive validation rules using CEL in values.cel.yaml.
helm plugin install https://github.com/idsulik/helm-cel# Pull the image
docker pull idsulik/helm-cel:latest # main branch
docker pull idsulik/helm-cel:2.1.2 # specific version
# Run validation
docker run --rm -v $(pwd):/charts idsulik/helm-cel validate /charts/mychart
# Generate validation rules
docker run --rm -v $(pwd):/charts idsulik/helm-cel generate /charts/mychartgit clone https://github.com/idsulik/helm-cel
cd helm-cel
make installValidate your chart values using the validate command:
helm cel validate ./mychartOptions:
--values-file, -v Values files to validate (comma-separated or multiple flags)
Defaults to values.yaml
--rules-file, -r Rules files to validate against (comma-separated or multiple flags)
Defaults to values.cel.yaml
--output, -o Output format: text, json, or yaml
Defaults to textExample with custom files:
# Using single values and rules files
helm cel validate ./mychart --values-file prod.values.yaml --rules-file prod.cel.yaml
# Using JSON output format
helm cel validate ./mychart -o json
# Using multiple values files (later files take precedence)
helm cel validate ./mychart --values-file common.yaml --values-file prod.yaml
# Using comma-separated values files
helm cel validate ./mychart --values-file common.yaml,prod.yaml,overrides.yaml
# Using multiple rules files
helm cel validate ./mychart --rules-file global.cel.yaml --rules-file ingress.cel.yaml
# Combining multiple values and rules files
helm cel validate ./mychart \
--values-file common.yaml,prod.yaml \
--rules-file global.cel.yaml,ingress.cel.yaml,deployment.cel.yamlYou can automatically generate validation rules based on your values file structure:
helm cel generate ./mychartOptions:
--force, -f Force overwrite existing rules file
--values-file, -v Values file to generate rules from (defaults to values.yaml)
--output-file, -o Output file for generated rules (defaults to values.cel.yaml)Example with custom files:
helm cel generate ./mychart --values-file prod.values.yaml --output-file prod.cel.yaml --forceYou can organize your validation rules into multiple files for better maintainability. Files must have the .cel.yaml extension. Example structure:
mychart/
├── Chart.yaml
├── values.yaml
└── cel/
├── global.cel.yaml # Global configuration rules
├── ingress.cel.yaml # Ingress-specific rules
└── deployment.cel.yaml # Deployment-specific rules
When using multiple rule files, expressions are shared across all files but must be unique (no duplicate expression names allowed).
Each rule in values.cel.yaml consists of:
expr: A CEL expression that should evaluate totruefor valid valuesdesc: A description of what the rule validatesseverity: Optional severity level ("error" or "warning", defaults to "error")
Example values.cel.yaml:
rules:
- expr: "has(values.service) && has(values.service.port)"
desc: "service port is required"
- expr: "values.service.port >= 1 && values.service.port <= 65535"
desc: "service port must be between 1 and 65535"
severity: warning
- expr: "!(has(values.replicaCount)) || values.replicaCount >= 1"
desc: "if replicaCount is set, it must be at least 1"Rules can have two severity levels:
error: Validation fails if the rule is not satisfied (default)warning: Shows a warning but allows validation to pass
- Required fields:
- expr: "has(values.fieldName)"
desc: "fieldName is required"- Value constraints:
- expr: "values.number >= 0 && values.number <= 100"
desc: "number must be between 0 and 100"- Type validation:
- expr: "type(values.ports) == list"
desc: "ports must be a list"- Resource validation:
- expr: 'values.resources.requests.memory.matches("^[0-9]+(Mi|Gi)$")'
desc: "memory requests must be in Mi or Gi"- Port validation:
- expr: "values.service.port >= 1 && values.service.port <= 65535"
desc: "port must be valid"You can define expressions to reuse across rules:
expressions:
portRange: 'values.service.port >= 1 && values.service.port <= 65535'
nodePortRange: 'values.service.nodePort >= 30000 && values.service.nodePort <= 32767'
rules:
- expr: "${portRange}"
desc: "Service port must be valid"
- expr: 'values.service.type == "NodePort" ? ${nodePortRange} : true'
desc: "NodePort must be valid when type is NodePort"If validation fails, you'll get a clear error message:
❌ Validation failed: replica count must be at least 1
Rule: values.replicaCount >= 1
Path: replicaCount
Current value: 0
With warnings:
Found 1 warning(s):
⚠️ Service port must be between 1 and 65535
Rule: values.service.port >= 1 && values.service.port <= 65535
Path: service.port
Current value: 80801
-------------------------------------------------
⚠️✅ Values validation successful with warnings!
If all rules pass, you'll see a success message:
✅ Values validation successful!
The helm-cel plugin uses different exit codes to indicate the validation result, making it easy to integrate with CI/CD pipelines:
- Exit Code 0: Validation successful (no errors, no warnings)
- Exit Code 1: Validation failed with errors
- Exit Code 2: Validation successful with warnings only
This allows your pipeline scripts to handle different scenarios appropriately:
You can output validation results in JSON or YAML format for integration with CI/CD pipelines:
# JSON output
helm cel validate ./mychart -o json
# YAML output
helm cel validate ./mychart -o yamlJSON output example:
{
"has_errors": true,
"has_warnings": true,
"result": {
"errors": [
{
"description": "replicaCount must be at least 1",
"expression": "values.replicaCount >= 1",
"value": 0,
"path": "replicaCount"
}
],
"warnings": [
{
"description": "service port should be between 1 and 65535",
"expression": "values.service.port >= 1 && values.service.port <= 65535",
"value": 80801,
"path": "service.port"
}
]
}
}YAML output example:
has_errors: true
has_warnings: true
result:
errors:
- description: replicaCount must be at least 1
expression: values.replicaCount >= 1
value: 0
path: replicaCount
warnings:
- description: service port should be between 1 and 65535
expression: values.service.port >= 1 && values.service.port <= 65535
value: 80801
path: service.portWe'd love to know if you're using helm-cel! Companies and individuals using this plugin can add themselves to our ADOPTERS.md file.
Are you using helm-cel in production or development? Help us understand the community by adding your organization to the adopters list. It only takes a minute and helps the project grow! 🚀
Requirements:
- Go 1.22 or later
Build:
make buildInstall locally:
make installRun tests:
make test- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Distributed under the MIT License. See LICENSE for more information.