Package a runeset
A runeset is to Rune what a chart is to Helm — but smaller, opinionated, and built into the CLI. Reach for one when “deploy my app” means more than one resource.
When to use a runeset
Section titled “When to use a runeset”| Situation | Use a runeset? |
|---|---|
| One service, one YAML. | No. |
| One service + one configmap + one secret. | Probably no. |
| API + worker + scheduler + secrets + configs. | Yes. |
| Multi-environment (dev / staging / prod values). | Yes. |
| You want a shareable artifact with a version. | Yes. |
Layout
Section titled “Layout”my-app/├── runeset.yaml├── values.yaml├── templates/│ ├── api.yaml.tmpl│ ├── worker.yaml.tmpl│ ├── secrets.yaml.tmpl│ └── configs.yaml.tmpl└── README.mdruneset.yaml
Section titled “runeset.yaml”name: my-appversion: 1.2.0description: API + worker + redismaintainers:values: - name: image.tag type: string default: "1.2.0" - name: api.replicas type: int default: 2 - name: worker.replicas type: int default: 4 - name: redis.password type: string sensitive: truevalues.yaml
Section titled “values.yaml”image: tag: "1.2.0"api: replicas: 2worker: replicas: 4redis: password: "" # required at install timeA template
Section titled “A template”templates/api.yaml.tmpl:
service: name: api namespace: {{ .Release.Namespace }} image: ghcr.io/example/api:{{ .Values.image.tag }} scale: {{ .Values.api.replicas }} ports: - { name: http, port: 8080 } envFrom: - secretRef: {{ .Release.Name }}-secrets health: liveness: type: http path: /healthz port: 8080Available template values:
| Name | What it is |
|---|---|
.Values.* | Merged values (defaults + files + --set). |
.Release.Name | Set via --release at install time. |
.Release.Namespace | Target namespace (default: default). |
.Runeset.Name | The runeset’s name from runeset.yaml. |
.Runeset.Version | The runeset’s version. |
Render — see what you’ll deploy
Section titled “Render — see what you’ll deploy”rune cast ./my-app --renderrune cast ./my-app --render \ --set=image.tag=1.3.0 \ --set=api.replicas=5 \ --values=production.values.yaml--render prints the final YAML and exits without applying — your “diff before deploy” loop.
Install
Section titled “Install”rune cast ./my-app \ --release=my-app-prod \ --namespace=prod \ --create-namespace \ --values=production.values.yaml--release is the install identity. Re-running with the same release upgrades in place. Different release names give you parallel installs (e.g., my-app-prod and my-app-staging).
Package and distribute
Section titled “Package and distribute”rune pack ./my-app -o my-app-1.2.0.runeset.tgz --sha256Outputs:
my-app-1.2.0.runeset.tgzmy-app-1.2.0.runeset.tgz.sha256
Install from any location Rune can fetch:
# HTTPSrune cast https://example.com/my-app-1.2.0.runeset.tgz \ --release=my-app-prod
# Local archiverune cast ./my-app-1.2.0.runeset.tgz --release=my-app-prod
# GitValues precedence
Section titled “Values precedence”Highest wins:
--set key=value— CLI flags.--values file.yaml— extra files (later files override earlier).values.yaml— bundled defaults.
Patterns
Section titled “Patterns”Secrets in templates
Section titled “Secrets in templates”secrets: - name: {{ .Release.Name }}-secrets namespace: {{ .Release.Namespace }} data: - { key: redis_password, value: {{ .Values.redis.password | quote }} }Pass the password at install time:
rune cast ./my-app \ --release=my-app-prod \ --set=redis.password=$REDIS_PASSWORDOr keep a separate untracked file:
rune cast ./my-app --values=secrets.local.yaml --release=my-app-prodPer-environment values files
Section titled “Per-environment values files”my-app/├── runeset.yaml├── values.yaml # defaults└── envs/ ├── dev.yaml ├── staging.yaml └── prod.yamlrune cast ./my-app --values=envs/prod.yaml --release=my-app-prodAnti-patterns
Section titled “Anti-patterns”- Templating everything. If a value never changes, hardcode it. Less surface area.
- Cross-runeset references. A runeset shouldn’t reach into another runeset’s resources by name. Use stable namespace conventions and standalone resources for shared state.
- Big monolithic runesets. If your runeset has 30 services across 5 teams, split it.