Self-hosted service
hypnoglow/helm-s3 avatar
hypnoglow/helm-s3

helm-s3: a Helm plugin that turns an S3 bucket into a chart repository

⎈ Helm plugin that adds support for AWS S3 as a chart repository.

616 stars171 forksGoMIT

At a glance

What is it?
helm-s3 is a Go plugin that adds the s3 scheme to Helm, so a bucket can hold a private or public chart repository and be added with helm repo add like any other. It supports Helm v2 and v3, ships a Docker image, and works with any S3-compatible vendor rather than only AWS.
Who is it for?
helm-s3 suits a team that already keeps artefacts in S3 and wants charts to live there too, especially one using MinIO or another S3-compatible store and one that values self-hosted infrastructure over a public chart registry. It does not suit a team that wants a hosted registry with signing, replication and a UI, and you should think twice before pointing it at a bucket shared with other workloads, since the plugin writes an index into the same prefix.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 36 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Adding the s3 scheme to Helm

helm-s3 is a Helm plugin that provides Amazon S3 protocol support, which allows you to have private or public Helm chart repositories hosted on S3. The AWS documentation has a guide with a detailed example use case overview for the pattern.

The mechanism is the plugin system. Helm calls out to the plugin binary when it sees a scheme the core binary does not understand, so the s3 scheme becomes available to every Helm command that takes a repository URL. The plugin supports both Helm v2 and v3, which is unusual for a plugin of this kind and is the reason the go.mod carries both helm.sh/helm/v3 and the older k8s.io/helm module.

The installation is a single Helm command:

bash
    $ helm plugin install https://github.com/hypnoglow/helm-s3.git

A specific release can be pinned:

bash
    $ helm plugin install https://github.com/hypnoglow/helm-s3.git --version 0.17.1

To use the plugin you do not need any special dependencies. The installer downloads a versioned release with a prebuilt binary from the GitHub releases, so the plugin arrives as a compiled artifact rather than something you have to build yourself. If you do want to build it from source, or you want to contribute, the readme points at the contributing instructions in the .github directory.

There is also a Docker distribution. Images are pushed to Docker Hub tagged with the plugin release version and suffixed with the Helm version, and an image built from the main branch is also available, with the readme being explicit that it should only be used for playing and testing and is strongly discouraged for production use cases.

Adding a bucket as a repository

The usage section walks through the flow with a bucket that already holds an index and a chart archive, at s3://bucket-name/charts/index.yaml and s3://bucket-name/charts/epicservice-0.5.1.tgz respectively.

The repository is then added with the ordinary command:

bash
$ helm repo add coolcharts s3://bucket-name/charts

And it is used like any other Helm chart repository, which is the point of the whole exercise:

bash
$ helm search coolcharts
NAME                       	VERSION	  DESCRIPTION
coolcharts/epicservice	    0.5.1     A Helm chart.

The v2 variant of the search command is noted separately, since the examples are given for Helm v3 and the tip points out that Helm v2 users run helm search coolcharts instead.

The table of contents lists the rest of the command surface: init, push, delete, reindex and uninstall. Init is the one that creates the repository structure, push uploads a chart, reindex regenerates the index file, and delete and uninstall remove a chart or the whole thing. The plugin therefore owns the repository lifecycle rather than only the read path, which is what makes it usable as the primary home for charts rather than a mirror.

The readme also documents advanced features that change how the bucket is laid out: relative chart URLs, serving charts over HTTP, ACLs, a timeout setting, using alternative S3-compatible vendors, server side encryption on the bucket, the bucket location, AWS SSO, and signed charts. Each of those corresponds to a real constraint of object storage as a chart host, and the readme covers them one by one.

IAM policy is the real configuration decision

To publish charts to buckets and to fetch from private buckets you need valid AWS credentials, and the readme is careful about how to supply them.

The credentials are configured in the same manner as for the AWS CLI tool, so if you are already using the AWS CLI you are good to go with no additional configuration. Otherwise the readme points at the official AWS guide to set up credentials. The key is that the plugin does not invent its own credential mechanism, it uses the standard chain, which means a profile, an environment variable or a role all work as they do for every other AWS tool.

What the readme emphasises is minimising the scope of those credentials, with the example of a setup where users get only read access and write access is given to a CI system that builds and pushes charts to the repository. Two example policies are given, one read only and one read and write, both in JSON.

The read only policy allows s3:ListBucket and s3:GetObject on the bucket and on the objects under it:

json
                "s3:ListBucket",
                "s3:GetObject"

The read and write policy is larger and more carefully scoped. Its file statement covers s3:PutObjectAcl, s3:PutObject, s3:GetObjectAcl, s3:GetObject and s3:DeleteObject, and its resource is the bucket plus the repository prefix under it, so the write access is confined to the repository rather than the whole bucket. A second statement gives s3:ListBucket on the bucket alone, since listing is a bucket-level action that cannot be scoped to a prefix.

The split between a bucket statement and a prefix statement is the detail to copy correctly, because a policy that omits the first will fail on the listing step even when the object permissions are right.

Helm version detection and the environment variable that overrides it

Because the plugin supports both major versions of Helm, it has to work out which one it is running under, and the readme documents the fallback for when that detection goes wrong.

The plugin is able to detect if you are using Helm v2 or v3 automatically. If for some reason it does not detect the version properly, you can set the HELM_S3_MODE environment variable to the value 2 or 3 to force the mode. The demonstration in the readme shows a session where helm version reports v3.0.2, the plugin still reports a v2 mode, and the forced variable corrects it:

bash
$ HELM_S3_MODE=3 helm s3 version --mode
helm-s3 plugin version: 0.9.2
Helm version mode: v3

That command is worth knowing on its own, because helm s3 version --mode is the quickest way to see what the plugin thinks is going on when a repository add or a push fails for no obvious reason. The version output reports both the plugin version and the mode it believes it is in, and the example shows a plugin version of 0.9.2, which is from an earlier release than the current one, since the current tags are 0.17.x.

The design decision to support both major versions at the cost of carrying two Helm modules in the build is a reasonable trade for a plugin whose user base includes long-lived Helm v2 estates. The override exists because the detection is heuristic and occasionally wrong.

A Go plugin with a Docker image that has to patch its own manifest

The build story explains why the Docker image is more than a copy of the binary.

The plugin is written in Go, and the module requires the AWS SDK for Go, the MinIO Go client, Cobra for the command line, semver, and the two Helm modules already mentioned. The presence of the MinIO client alongside the AWS SDK is what makes the alternative S3-compatible vendors section of the readme possible, since the code does not have to go through AWS to talk to a bucket that speaks the same protocol.

The Dockerfile has two stages, a Go builder and an alpine image that already contains Helm. The builder compiles the plugin with CGO disabled, with trimming, in vendor mode, and with the version stamped into the binary through a linker flag:

dockerfile
    go build  \
    -mod=vendor \
    -ldflags "-X main.version=${PLUGIN_VERSION}" \
    ./cmd/helm-s3

The subtle part comes next. The container has to install itself as a Helm plugin into the image, and the plugin manifest carries hooks that make no sense when everything is built locally from source, so the Dockerfile rewrites the manifest, removing the hooks and updating the version, before copying the fixed file and the binary into the Helm plugin cache and running helm plugin install on it.

That is the kind of detail that only appears once a plugin has been containerised, and it is a fair proxy for how much of the plugin's behaviour is about integrating with Helm's own plugin conventions rather than about S3.

The rest of the repository follows the same pattern: a cmd/ directory for the entry point, internal/ for the implementation, a plugin.yaml manifest at the root, a website/ directory for the documentation, docs/, a Dockerfile, a Taskfile.yml, a .goreleaser.yml for the release binaries, an artifacthub-repo.yml for the Artifact Hub listing, a golangci configuration, and a tests/ directory.

Editorial conclusion

helm-s3 suits a team that already keeps artefacts in S3 and wants charts to live there too, especially one using MinIO or another S3-compatible store and one that values self-hosted infrastructure over a public chart registry. It does not suit a team that wants a hosted registry with signing, replication and a UI, and you should think twice before pointing it at a bucket shared with other workloads, since the plugin writes an index into the same prefix. Before adopting it, run helm plugin install, decide which IAM policy fits read-only versus read-write use, and read the relative chart URLs section, because that is where a bucket behind a CDN or a path prefix differs from a plain HTTP chart repository.

Frequently asked questions

What does the helm-s3 plugin do?

It provides Amazon S3 protocol support for Helm, which lets you have private or public Helm chart repositories hosted on S3. It supports both Helm v2 and Helm v3, and once installed the bucket is used like any other chart repository.

How do I install the helm-s3 plugin?

With helm plugin install https://github.com/hypnoglow/helm-s3.git, or with the --version flag to pin a specific release such as 0.17.1. No extra dependencies are needed because the installer downloads a versioned release with a prebuilt binary. Docker images are also published to Docker Hub, tagged with the plugin version and suffixed with the Helm version.

What IAM permissions does helm-s3 need?

A read only setup needs s3:ListBucket and s3:GetObject. A read and write setup adds s3:PutObject, s3:PutObjectAcl, s3:GetObjectAcl and s3:DeleteObject on the repository prefix, plus s3:ListBucket on the bucket itself, since listing cannot be scoped to a prefix. The readme recommends giving users only read access and write access to the CI that pushes charts.

What happens if helm-s3 detects the wrong Helm version?

The plugin detects Helm v2 or v3 automatically, and if the detection is wrong you can set the HELM_S3_MODE environment variable to 2 or 3 to force the mode. The helm s3 version --mode command reports both the plugin version and the mode it believes it is running in.

Official sources

  1. hypnoglow/helm-s3 on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hypnoglow-helm-s3.svg)](https://hysenlabs.com/projects/hypnoglow-helm-s3)

Was this useful?