Skip to content

feat(helm): make gateway PVC size and StorageClass configurable #3216

Open

Description

@jtatum

User Story

As a Kubernetes platform operator deploying the OpenShell gateway through Helm, I want to select the gateway PVC's size and StorageClass, so that encrypted gateway state lands on the cluster's retained stateful storage with capacity appropriate to the deployment.

Problem Statement

OpenShell Helm chart 0.0.116 hard-codes the StatefulSet volumeClaimTemplates request to 1Gi and does not set or expose storageClassName. The chart exposes workspace PVC size/class values, but not the gateway database claim.

Impact / Why This Matters

Without chart values, operators must accept the cluster's default StorageClass and fixed capacity or carry a post-render patch. This can put the gateway's encrypted credential database and control-plane state on an unintended storage tier. A later correction may require StatefulSet/PVC migration because volumeClaimTemplates fields are immutable in common upgrade paths. Post-render patches are also coupled to the template's claim order.

Proposed Design

Expose a small gateway persistence block in Helm values, for example:

persistence:
  size: 1Gi
  storageClassName: ""

The existing observable behavior should remain the default: a 1Gi claim using the cluster's default StorageClass when no values are set. Setting these values should render the requested capacity and storageClassName into the gateway StatefulSet. Naming is illustrative; the important user workflow is being able to declare both fields without a post-renderer.

Acceptance Criteria

  • The gateway PVC request size is configurable through a documented chart value.
  • The gateway PVC StorageClass is configurable through a documented chart value.
  • Omitting the new values preserves the current 1Gi/default-StorageClass behavior.
  • helm template tests cover default and overridden values.
  • Upgrade documentation notes the StatefulSet/PVC immutability implications for existing releases.

Alternatives Considered

  • Use the cluster's default StorageClass and expand the claim later. This does not guarantee the required retention/storage tier and depends on expansion support.
  • Pre-create a PVC. The StatefulSet currently owns a volumeClaimTemplate and has no existing-claim workflow.
  • Carry a Flux/Helm post-render patch. This works, but is coupled to the StatefulSet resource name and claim index, and it shifts a normal storage choice out of the chart's supported interface.

Agent Investigation

  • Verified the behavior against the released chart 0.0.116 at OCI digest sha256:df55cd1538bdfb7836834c30dfcf8373b85ffea83bbfd70d50dbe69407a0d2b3.
  • Rendering with production values still produces resources.requests.storage: 1Gi and no storageClassName until a post-render patch is applied.
  • Searched open and closed issues for Helm persistence, volumeClaimTemplates, storage size, and StorageClass; no existing issue matched this gateway PVC gap.

Checklist

  • I've reviewed existing issues and the architecture docs
  • This is a design proposal, not a "please build this" request

Activity

  1. added
    state:acceptedA maintainer decided OpenShell should pursue this issue
    and removed
    state:triage-neededOpened without agent diagnostics and needs triage
    on Sep 8, 2026
  2. lunarwhite commented on Sep 14, 2026

    @lunarwhite
    Contributor

    I'd like to work on this.

    🏗️ build-plan

    Implementation Plan

    Issue type: feat
    Complexity: Low
    Confidence: High — clear path

    Summary

    Expose the gateway StatefulSet claim's request size and storageClassName as
    server.databaseStorageSize and server.databaseStorageClass, rendering both into
    templates/statefulset.yaml. Empty defaults preserve the current 1Gi request against
    the cluster's default StorageClass. accessModes stays hard-coded at ReadWriteOnce.

    Scope

    • deploy/helm/openshell/values.yaml: add both keys under server:, adjacent to dbUrl
      (line 221), with helm-docs # -- comments following the idiom of
      server.workspaceStorageClass (lines 241-245).
    • deploy/helm/openshell/templates/statefulset.yaml: render both into the claim spec
      (line 26).
    • deploy/helm/openshell/tests/gateway_persistence_test.yaml (new): default and
      overridden rendering.
    • deploy/helm/openshell/README.md.gotmpl: a #### Database PVC storage subsection
      under ### Database backend (line 80) covering both values, the volumeClaimTemplates
      immutability constraint, and how to change storage on an existing release.
    • deploy/helm/openshell/README.md: regenerated by mise run helm:docs;
      mise run helm:docs:check is a required CI gate.
    • docs/kubernetes/setup.mdx: both values in the Configure Chart Values table
      (line 187) plus a <Warning> pointing at the chart README for the storage-change
      procedure.
    • skills/debug-openshell-cluster/SKILL.md: amend the existing PVC-unbound row
      (line 740) to name the new StorageClass value, mirroring line 741, and add a row for the
      forbidden-update upgrade failure.

    Implementation Steps

    1. Add both keys with empty defaults.
    2. Render them in the claim spec, replacing statefulset.yaml lines 22-26:
           spec:
             accessModes: ["ReadWriteOnce"]
             {{- with .Values.server.databaseStorageClass }}
             storageClassName: {{ . | quote }}
             {{- end }}
             resources:
               requests:
                 storage: {{ .Values.server.databaseStorageSize | default "1Gi" | quote }}
    1. Add the test suite following the conventions in
      gateway_pod_security_context_test.yaml, listing both templates/gateway-config.yaml
      and templates/statefulset.yaml because _gateway-workload.tpl line 14 checksums the
      config template.
    2. Add the README subsection and the setup.mdx rows and warning, regenerate the README,
      and amend the debug-skill rows.

    Test Plan

    • Unit tests: gateway_persistence_test.yaml — the default render asserts
      storage equals 1Gi and notExists on storageClassName; an overridden render
      asserts both values. The Deployment path has no PVC, and gateway_config_test.yaml
      lines 673-677 already assert that.
    • Integration tests: N/A. mise run helm:lint re-renders every ci/values-*.yaml
      overlay, none of which set storage keys.
    • E2E tests: N/A, no e2e/ change. Default rendering is unchanged.

    Risks & Open Questions

    • An empty databaseStorageClass omits the field rather than setting "", so the chart
      offers no way to request the disable-dynamic-provisioning semantics.
    • volumeClaimTemplates is immutable on existing StatefulSets. Growing a claim is
      in-place PVC expansion where the StorageClass sets allowVolumeExpansion, followed by an
      orphan-recreate so the template matches; changing StorageClass requires copying the data
      to a new claim, because a recreated StatefulSet re-adopts existing claims unchanged.
      Documented, per the acceptance criteria.
    • Setting either key under workload.kind=deployment is a silent no-op. Documented
      rather than guarded, because the HA overlay legitimately toggles workload.kind from a
      shared values file.
    • No LSM impact: this touches only Helm template rendering.

    Documentation Impact

    • deploy/helm/openshell/README.md.gotmpl and the regenerated README.md — the
      Database PVC storage subsection plus the generated values rows.
    • docs/kubernetes/setup.mdx — two values rows and the immutability warning.
    • skills/debug-openshell-cluster/SKILL.md — one amended and one new troubleshooting row.
    • No docs/reference/gateway-config.mdx change: these values render into the
      StatefulSet, not gateway.toml.

    Revision 1 — initial plan

  3. added a commit that references this issue on Sep 14, 2026
    89bfcf2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:acceptedA maintainer decided OpenShell should pursue this issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions