TL;DR: I built a .NET library that renders Helm charts and drives Kubernetes releases without shelling out to the helm CLI. 129/129 templates across ingress-nginx, cert-manager, external-dns, podinfo, and metrics-server now render successfully. The main entry point is HelmSharp.Action, with lower-level packages available for chart loading, rendering, Kubernetes operations, and release storage. MIT licensed, looking for feedback and early adopters.
Why I Built This
At work, our .NET services deploy to Kubernetes through Helm. Every Docker image had to bundle the helm binary — another dependency to manage, another layer in the image, another surface for CVEs. I wanted to cut that out entirely and do Helm-style rendering directly in-process.
The .NET ecosystem doesn't really have this. There are YAML libraries. There are Kubernetes client libraries. There are template engines. But nothing ties them together the way helm template does — values merging, named templates, include, range, toYaml, the whole Sprig function set, all wired into a single render pipeline. So I started building one. (This is also my first real open source project — I'd spent years consuming OSS without contributing back, and HelmSharp is what came out of deciding to change that.)
What HelmSharp Does
HelmSharp is a multi-package .NET SDK (net8.0 / net9.0 / net10.0) that covers:
| Package | What it does |
|---|---|
HelmSharp.Action |
High-level Helm client — TemplateAsync, UpgradeInstallAsync, RollbackAsync
|
HelmSharp.Chart |
Chart loading from directories and .tgz, values merging, --set / --set-json style overrides |
HelmSharp.Engine |
Helm-style template rendering — 100+ Sprig/Helm functions |
HelmSharp.Kube |
Kubernetes apply, delete, and wait (no kubectl needed) |
HelmSharp.Release |
Release history stored in Kubernetes Secrets (Helm-compatible) |
HelmSharp.Repo |
Chart repository index, pull, and search |
Plus Registry, Storage, PostRenderer extension points |
Here's the lower-level rendering API — no result objects, no stdout strings, just in-process template evaluation:
var chart = await HelmChartLoader.LoadAsync("/charts/my-chart", ct);
var values = await HelmValues.BuildAsync(chart, null, null, new Dictionary<string, object?>
{
["image"] = new Dictionary<string, object?> { ["tag"] = "1.2.3" },
["replicaCount"] = 3
}, null, null, null, ct);
var renderer = new HelmTemplateRenderer(chart, "my-app", "production", values);
var manifests = renderer.Render();
// manifests is a string of valid YAML — feed it to kubectl apply, store it,
// diff it, whatever you'd do with `helm template` output.
No helm binary. No Process.Start. Values are native .NET objects, not --set strings. The output is just a string — you decide what to do with it.
There's also a higher-level HelmClient API (TemplateAsync, UpgradeInstallAsync, RollbackAsync) that mirrors the Helm CLI workflow if you prefer that shape.
Where We Are Now (v1.0.3)
This week I hit a milestone I'm genuinely proud of: 129 out of 129 templates across 5 real-world public charts now render without a single parser exception.
| Chart | Version | Templates |
|---|---|---|
| ingress-nginx | 4.12.1 | 42/42 ✅ |
| cert-manager | 1.17.1 | 41/41 ✅ |
| external-dns | 1.21.1 | 7/7 ✅ |
| podinfo | 6.14.0 | 21/21 ✅ |
| metrics-server | 3.13.1 | 18/18 ✅ |
This was blocked by two parser bugs that took a while to track down — one in pipeline splitting where | inside parenthesized expressions like (empty .x) was incorrectly treated as a pipeline separator, and one in else-if chain reconstruction where C# string interpolation was silently producing single braces instead of double braces. The kind of bug where you stare at it for hours, then the fix is four characters.
The test suite is at 214 tests across three target frameworks, with a golden-test harness that runs helm template as the oracle and diffs the output document-by-document against HelmSharp's renderer.
What's Next
The project follows numbered milestones:
- M1 (in progress): Helm template parity — functions, whitespace, built-in objects, control flow
- M2: Subchart and dependency resolution
- M3: Release lifecycle (install/upgrade/rollback/uninstall with hook execution)
- M4: Kubernetes resource lifecycle hardening
- M5: OCI registry and provenance
- M6: API polish, XML docs, stable surface
No hard dates — I'm pushing forward steadily (you can see the cadence in the commit history). That said, if someone needs a specific feature, I'm happy to re-prioritize. Open source moves faster when real use cases drive it.
What I'm Looking For
If you're writing a .NET Kubernetes operator, a CD pipeline, or any tooling that currently shells out to helm — this is for you. dotnet add package HelmSharp.Action and render one of your charts. If it works, great. If it breaks, I want to know exactly how — open an issue with the chart and template that failed, and I'll fix it.
If you find it useful, a star is appreciated — but stars are earned, not asked for. What I really want is feedback, bug reports, and feature requests from people who actually deploy things to Kubernetes.
Contributions are welcome too. The golden-test harness makes it easy to verify template fixes — add a chart fixture, run the test, see the diff.
If you use Helm in .NET-based tooling today, what would you expect from a managed SDK like this?
GitHub: github.com/GaTTGeng/HelmSharp
NuGet: nuget.org/packages/HelmSharp.Action
License: MIT
Top comments (0)