Deploying Hudu with Helm

Hudu's official Helm Chart lets you install and run self-hosted Hudu on Kubernetes. This article walks through the full lifecycle: getting the chart set up, verifying a healthy install, finding logs when something goes wrong, and keeping your deployment up to date. For the complete option reference (every value the chart accepts), see the chart's README. This article is the narrative, step-by-step version.

   If you're installing for the first time, read "Setting up" through once before running any commands. A couple of the defaults, TLS and file storage, need a decision from you up front, and it's easier to decide before your first install than to change it after.

Setup and deployment

Before you start

You'll need:

A Kubernetes cluster

Version 1.24 or later.

Helm and kubectl

Helm v3 and kubectl installed and pointed at that cluster.

An ingress controller

For example nginx-ingress, or the AWS Load Balancer Controller on EKS. The chart creates an Ingress by default, so you'll need a controller in the cluster to adopt it.

A way to terminate TLS

Either cert-manager for automatic certificates, or your own certificate and load balancer. Hudu redirects plain HTTP to HTTPS, so this isn't optional for a real deployment. See "A note on TLS" below.

A file storage decision

The chart can use a ReadWriteMany Kubernetes volume, or S3 (real S3, an S3-compatible service, or a bundled evaluation-only MinIO for trying the chart out first). More on this in "Choosing file storage" below.

Everything else, database, cache, and app secrets, has a working default or is generated for you on first install.

Adding the chart

The chart is published as an OCI artifact on a public ECR registry, so there's no helm repo add step. You can also browse it on Artifact Hub to see available versions and the full README without pulling anything first.

helm show chart oci://public.ecr.aws/q4n0g0t7/hudu-helm-chart

confirms you can reach the registry and shows the latest published chart version. To download the chart itself, for example to inspect its templates and default values locally, or to install from a local copy:

helm pull oci://public.ecr.aws/q4n0g0t7/hudu-helm-chart --untar

This creates a hudu/ directory with the chart's templates and values.yaml. Add --version <version> to either command to target a specific release instead of the latest one. Neither command needs a login. ECR Public allows anonymous reads.

   config.domain is the only value the chart requires. It's the hostname Hudu will be served from, with no scheme or port (hudu.example.com, not https://hudu.example.com). For a deployment that's actually reachable, also set ingress.host to the same hostname. The chart doesn't enforce this, but a blank ingress.host renders an Ingress that doesn't route anywhere.

A minimal install

This relies on the chart's defaults: a bundled single-replica Postgres and Redis, and local-disk file storage on a ReadWriteMany PVC.

helm install my-hudu oci://public.ecr.aws/q4n0g0t7/hudu-helm-chart \
  --set config.domain=hudu.example.com \
  --set ingress.host=hudu.example.com

The four app secrets, secrets.secretKeyBase, secrets.passwordKey, secrets.twoFactorKey, and the bundled Postgres password, are generated for you automatically the first time you install, if you don't set them yourself. See "Handling secrets" further down. It matters once you get to upgrades.

If you'd rather set them explicitly up front:

helm install my-hudu oci://public.ecr.aws/q4n0g0t7/hudu-helm-chart \
  --set config.domain=hudu.example.com \
  --set ingress.host=hudu.example.com \
  --set secrets.secretKeyBase=$(openssl rand -hex 64) \
  --set secrets.passwordKey=$(openssl rand -hex 16) \
  --set secrets.twoFactorKey=$(openssl rand -hex 16) \
  --set postgresql.auth.password=$(openssl rand -hex 16)

A note on TLS

Hudu runs with HTTPS enforcement on, so plain HTTP traffic gets redirected to HTTPS. In practice this means:

  • If you're terminating TLS at your Ingress or load balancer (the supported setup), set ingress.tls.enabled: true with cert-manager configured, or bring your own TLS secret. Leave config.behindTlsProxy at its default of true.
  • Only set config.behindTlsProxy: false and config.disableSsl: true if there is genuinely no TLS anywhere in front of the pod, for example a throwaway evaluation cluster reached only through kubectl port-forward.

    Getting this wrong on a real deployment causes two separate symptoms: an infinite redirect loop, and, if that's worked around, forms silently failing to submit (sign-up, license validation) with no visible error. That second one is a CSRF check failing because the browser and the app disagree on http versus https.

If you're on EKS behind the AWS Load Balancer Controller rather than nginx, you'll also need:

  --set ingress.className=alb \
  --set ingress.annotations."alb\.ingress\.kubernetes\.io/scheme"=internet-facing \
  --set ingress.annotations."alb\.ingress\.kubernetes\.io/target-type"=ip
    Confirm your cluster actually has that IngressClass with kubectl get ingressclass. An Ingress referencing one that doesn't exist won't fail the install, it'll just sit there unadopted.

Choosing file storage

Local disk (default)

storage.type: pvc. Nothing else to configure for a single web replica. If you plan to run more than one web pod, your cluster's StorageClass needs to support ReadWriteMany (NFS, EFS, Longhorn, Azure Files).

S3, no credentials yet

storage.type: s3, storage.s3.useLocalS3: true. Spins up a bundled, evaluation-only MinIO and wires up bucket and credentials for you. A good way to try the chart before you've set up real S3 access. Not for production use.

Real S3

storage.type: s3, useLocalS3: false. Set storage.s3.bucket and storage.s3.region, plus one of: access key and secret, an existing Kubernetes Secret, or an IRSA-annotated ServiceAccount if you're on EKS. Reach out to Hudu support if you'd like a walkthrough of the IRSA setup specifically. It involves an IAM trust policy on your side as well as chart config.

Full option tables for all of the above, including advanced settings like autoscaling, high availability, and resource limits, are in the chart's README.

Running your install

Verify the install

kubectl get pods -l app.kubernetes.io/instance=my-hudu

You should see a web pod and a worker pod reach Running, plus postgres and redis pods if you're using the bundled versions. A one-off migration Job also runs during install or upgrade and should reach Completed.

kubectl rollout status deployment/my-hudu-hudu-web

waits until the web Deployment has finished rolling out. Useful right after an install or upgrade, or in a CI/CD pipeline.

Access the app

Once the Ingress has picked up an address (kubectl get ingress), Hudu is reachable at the hostname you set in ingress.host, over HTTPS. If you haven't wired up DNS yet and just want to confirm the app itself is healthy, port-forward directly to the service:

kubectl port-forward svc/my-hudu-hudu-web 3000:3000

then visit http://localhost:3000. Note this bypasses your Ingress and TLS entirely, so it's for a quick health check only, not a substitute for the real access path.

Everyday commands

What you want Command
Current release status and revision
helm status my-hudu
What values a release is running with
helm get values my-hudu
Web pod logs
kubectl logs -l app.kubernetes.io/component=web -f
Worker pod logs
kubectl logs -l app.kubernetes.io/component=worker -f
Shell into a running pod
kubectl exec -it deploy/my-hudu-hudu-web -- sh
List everything the release created
kubectl get all -l app.kubernetes.io/instance=my-hudu

Maintaining your install

Upgrading

helm upgrade my-hudu oci://public.ecr.aws/q4n0g0t7/hudu-helm-chart \
  --version <new-chart-version> \
  --reuse-values

--reuse-values carries forward whatever you've already set, so you only need to pass values you're actively changing. Check the chart's release notes for the version you're moving to. Some upgrades introduce a new default that's worth a deliberate decision rather than inheriting silently, for example a memory limit being added to the web pod where none existed before, or config.behindTlsProxy changing default. The chart's README calls these out explicitly under each affected setting.

The migration Job runs automatically as part of helm upgrade, before the new web and worker pods roll out.

Rolling back

helm rollback my-hudu

rolls back to the previous release revision. This rolls back chart values and the application version, but it does not reverse a completed database migration. A schema change from the release you're rolling back from can remain in place.

    For anything beyond a same-version config rollback, restore from a database backup instead. Treat helm rollback as a tool for reverting configuration, not for undoing a migrated schema.

Handling secrets

The app's core secrets, the secret key base, the password encryption key, the 2FA key, and the bundled Postgres password, are generated once on first install and stored in a real Kubernetes Secret (<release>-hudu-generated). They are not regenerated on later upgrades.

    If that Secret is ever deleted, don't expect the next helm upgrade to fix it. Pods will fail with a clear CreateContainerConfigError instead of silently getting a new key, which would otherwise make existing encrypted data permanently unreadable.

Back this Secret up:

kubectl get secret my-hudu-hudu-generated -o yaml  my-hudu-generated-backup.yaml

and store it somewhere as securely as you'd store the encrypted data it protects. It effectively is the key to that data.

Backup considerations

  • Database: if you're using the bundled Postgres, back up its PVC on whatever schedule your cluster's storage supports (volume snapshots), or point the chart at an externally-managed, already-backed-up Postgres instance instead (externalDatabase.*). Recommended for anything beyond evaluation.
  • File storage: back up the uploads PVC, or rely on your S3 bucket's own versioning and replication if you're using S3 storage.
  • The generated-secrets Secret: back it up once and keep it somewhere safe. It's not reproducible from anything else.

Changing configuration

Ordinary config changes, resource limits, ingress annotations, feature flags under config.*, are a normal helm upgrade --reuse-values --set <key>=<value>, or by maintaining your own values file and passing -f values.yaml.

   A handful of settings are effectively set-once and need care if you change them on a live deployment: the four secrets above, and the bundled Postgres password, since changing them doesn't migrate whatever they were already protecting. When in doubt about whether a setting is safe to change after go-live, check that setting's entry in the chart README before changing it.

FAQ

Do I need cert-manager to use this chart?

No, but you do need TLS terminated somewhere in front of the app. cert-manager is the easiest path if you want automatic certificates, but bringing your own certificate or load balancer works too. The only unsupported setup is no TLS at all in front of a real deployment.

Can I switch file storage backends after I've gone live?

You can, but switching storage.type or storage.s3.useLocalS3 doesn't migrate existing files. The new backend starts empty, and you'll need to manually copy your files over (for example with aws s3 sync or mc mirror) before switching over on a live deployment.

Will my app secrets change if I run helm upgrade?

No. The four app secrets and the bundled Postgres password are generated once on first install and stored in a Kubernetes Secret. Upgrades reuse them rather than regenerating them.

Does helm rollback undo a database migration?

No. It rolls back chart values and the application version, but a completed schema migration stays in place. Use a database backup restore if you need to undo a migration.

Troubleshooting

Start with pod status, then drill into the specific pod that isn't healthy:

kubectl get pods -l app.kubernetes.io/instance=my-hudu -o wide
kubectl describe pod <pod-name>
kubectl logs <pod-name>
kubectl logs <pod-name> --previous   # if the pod already restarted

    kubectl describe pod is usually the fastest path to a root cause. It shows scheduling failures, failed probes, and container-start errors, including the exact error for something like a missing Secret, in its Events section at the bottom.

Web or worker pod stuck in CreateContainerConfigError, referencing a missing Secret

Most often <release>-hudu-generated, the Secret the chart creates on first install to hold the auto-generated app secrets. If it's been deleted, the chart won't silently recreate it. See "Handling secrets" above.

Browser redirect loops on https, or forms silently fail (sign-up, license validation do nothing)

A TLS/behindTlsProxy mismatch. See "A note on TLS" above. Nearly always caused by ingress.tls.enabled being off, or config.behindTlsProxy/config.disableSsl having been set for a no-TLS test setup and left that way.

Migration Job fails and blocks the release (helm upgrade hangs, then times out)

Check its logs directly. It isn't matched by the component=web or component=worker label selectors:

kubectl logs job/my-hudu-hudu-migrate

A failed migration Job retries up to migrate.backoffLimit times (3 by default) before Helm gives up on the hook.

HorizontalPodAutoscaler shows <unknown> for its targets and never scales

The HPA needs web.resources.requests.cpu and memory set to compute a utilization percentage against. The chart ships defaults for both, so this only shows up if they were explicitly cleared.

Ingress exists but nothing routes to it

Usually one of: ingress.host left blank, or ingress.className set to an IngressClass your cluster doesn't actually have (check with kubectl get ingressclass). Neither fails the install, the Ingress just sits unadopted.

Uploads or attachments 404 after switching storage backends

Switching storage.type or storage.s3.useLocalS3 doesn't migrate existing files. The new backend starts empty. This needs a manual data copy (aws s3 sync, mc mirror) if you change it on a live deployment.

If you're stuck on something not covered here, contact Hudu support with:

  • The chart and app version you're running (helm get values my-hudu, helm list).
  • The output of kubectl describe pod for the affected pod.
  • Recent logs from the affected pod(s).

That's usually enough for us to help diagnose the issue quickly.

Was this article helpful?
0 out of 0 found this helpful