From b50c2e6b95c81cc5ea207b95761d25e177896492 Mon Sep 17 00:00:00 2001 From: DevOps Date: Thu, 20 Aug 2026 22:52:02 +0200 Subject: [PATCH 1/3] fix(docker): stage nginx config from RO source instead of envsubst The base image's 20-envsubst-on-templates.sh entrypoint tried to render docker/nginx.conf.template into /etc/nginx/conf.d/default.conf on every container start. On Kubernetes with readOnlyRootFilesystem=true that write failed with 'Read-only file system', blocking the container. The template has no $VAR placeholders, so envsubst adds nothing anyway. Ship the config at /etc/openconcho/nginx.conf and have 40-openconcho-config.sh install it into the chart's writable /etc/nginx/conf.d/ tmpfs before nginx starts. The base image's 10-listen and 20-envsubst scripts exit 0 cleanly with the templates dir empty. The chart's tmpfsMounts entry for /etc/nginx/conf.d stays required (fsGroup chowns it to 101, then install -o 101 -g 101 writes the file the same UID the entrypoint runs as). --- Dockerfile | 8 ++++++-- charts/openconcho/README.md | 2 +- docker/40-openconcho-config.sh | 7 +++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6ac2e92..48a47ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,8 +31,12 @@ RUN pnpm --filter @openconcho/web build FROM nginxinc/nginx-unprivileged:alpine COPY --chown=101:101 --from=builder /app/packages/web/dist /usr/share/nginx/html -# Rendered to /etc/nginx/conf.d/default.conf by the image's envsubst entrypoint. -COPY --chown=101:101 docker/nginx.conf.template /etc/nginx/templates/default.conf.template +# Served verbatim by 40-openconcho-config.sh from /etc/openconcho/nginx.conf +# to /etc/nginx/conf.d/default.conf at container start. Stored outside +# /etc/nginx/templates/ on purpose so the base image's 20-envsubst-on-templates.sh +# does not try to render it back into the read-only root filesystem — the +# template has no $VAR placeholders, so envsubst adds nothing. +COPY --chown=101:101 docker/nginx.conf.template /etc/openconcho/nginx.conf # Writes /usr/share/nginx/html/config.js from OPENCONCHO_DEFAULT_HONCHO_URL. # --chmod=0755 so nginx's docker-entrypoint.d actually executes it. COPY --chown=101:101 --chmod=0755 docker/40-openconcho-config.sh /docker-entrypoint.d/40-openconcho-config.sh diff --git a/charts/openconcho/README.md b/charts/openconcho/README.md index 82a0529..3f2ae60 100644 --- a/charts/openconcho/README.md +++ b/charts/openconcho/README.md @@ -226,4 +226,4 @@ helm test openconcho --logs | `seccompProfile` | `RuntimeDefault` | | `allowPrivilegeEscalation` | `false` | | `automountServiceAccountToken` | `false` | -| Writable paths | `/var/cache/nginx`, `/var/run`, `/tmp` (tmpfs) | +| Writable paths | `/etc/nginx/conf.d`, `/var/cache/nginx`, `/var/run`, `/tmp` (tmpfs) | diff --git a/docker/40-openconcho-config.sh b/docker/40-openconcho-config.sh index 8e4937a..863373e 100644 --- a/docker/40-openconcho-config.sh +++ b/docker/40-openconcho-config.sh @@ -7,6 +7,13 @@ # so the container works cleanly under a read-only root filesystem. set -eu +# Stage the openconcho server config into the writable tmpfs the chart mounts +# at /etc/nginx/conf.d. The image ships the config at /etc/openconcho/nginx.conf +# so the base image's envsubst step has nothing to render against the read-only +# filesystem. The base image's 10-listen-on-ipv6-by-default.sh exits 0 when +# default.conf is absent, so it's safe to stage the file here. +install -m 0644 -o 101 -g 101 /etc/openconcho/nginx.conf /etc/nginx/conf.d/default.conf + cat > /tmp/openconcho-config.js < Date: Fri, 21 Aug 2026 10:49:47 +0200 Subject: [PATCH 2/3] fix(helm): sanitize volume name so emptyDir at conf.d is applied The previous fix assumed the chart's emptyDir at /etc/nginx/conf.d was being mounted, but on a fresh K8s cluster the kubelet rejected the mount because the rendered volume name 'etc-nginx-conf.d' (note trailing dot, from the source path '/etc/nginx/conf.d') is not a valid DNS-1123 label. Without the mount, the image's default.conf remained visible on the RO layer; the 10-listen script reported 'can not modify ... read-only file system?' and 40-openconcho-config.sh's install command failed with 'File exists' (busybox install refuses to overwrite). Add 'replace "." "-"' to the volume name pipeline in deployment.yaml so 'etc/nginx/conf.d' sanitizes to 'etc-nginx-conf-d'. Also switch 40-openconcho-config.sh from 'install' to 'cp -f' + chmod + chown so the file is forced-overwritten even if 10-listen touched it earlier in the boot chain (the emptyDir is RW on a working mount). Verified 'helm template' now renders all four volumes with valid DNS-1123 names; smoke test still passes 8/8. --- charts/openconcho/templates/deployment.yaml | 4 ++-- docker/40-openconcho-config.sh | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/charts/openconcho/templates/deployment.yaml b/charts/openconcho/templates/deployment.yaml index 900d459..e0bf8a4 100644 --- a/charts/openconcho/templates/deployment.yaml +++ b/charts/openconcho/templates/deployment.yaml @@ -54,14 +54,14 @@ spec: {{- if .Values.tmpfsMounts }} volumeMounts: {{- range .Values.tmpfsMounts }} - - name: {{ .mountPath | trimPrefix "/" | replace "/" "-" | trunc 63 | trimSuffix "-" }} + - name: {{ .mountPath | trimPrefix "/" | replace "/" "-" | replace "." "-" | trunc 63 | trimSuffix "-" }} mountPath: {{ .mountPath }} {{- end }} {{- end }} {{- if .Values.tmpfsMounts }} volumes: {{- range .Values.tmpfsMounts }} - - name: {{ .mountPath | trimPrefix "/" | replace "/" "-" | trunc 63 | trimSuffix "-" }} + - name: {{ .mountPath | trimPrefix "/" | replace "/" "-" | replace "." "-" | trunc 63 | trimSuffix "-" }} emptyDir: medium: Memory {{- end }} diff --git a/docker/40-openconcho-config.sh b/docker/40-openconcho-config.sh index 863373e..0d7fda8 100644 --- a/docker/40-openconcho-config.sh +++ b/docker/40-openconcho-config.sh @@ -10,9 +10,11 @@ set -eu # Stage the openconcho server config into the writable tmpfs the chart mounts # at /etc/nginx/conf.d. The image ships the config at /etc/openconcho/nginx.conf # so the base image's envsubst step has nothing to render against the read-only -# filesystem. The base image's 10-listen-on-ipv6-by-default.sh exits 0 when -# default.conf is absent, so it's safe to stage the file here. -install -m 0644 -o 101 -g 101 /etc/openconcho/nginx.conf /etc/nginx/conf.d/default.conf +# filesystem. The base image's 10-listen-on-ipv6-by-default.sh may have created +# default.conf already on a writable mount, so we force-overwrite with cp -f. +cp -f /etc/openconcho/nginx.conf /etc/nginx/conf.d/default.conf +chmod 0644 /etc/nginx/conf.d/default.conf +chown 101:101 /etc/nginx/conf.d/default.conf cat > /tmp/openconcho-config.js < Date: Fri, 21 Aug 2026 11:01:08 +0200 Subject: [PATCH 3/3] ci(fork): add workflow_dispatch chart publish Adds .github/workflows/chart-publish.yml so the fork can publish charts/openconcho to ghcr.io//charts on demand, instead of waiting for an upstream release tag. The existing docker-publish.yml publish-chart job is gated on startsWith(github.ref, 'refs/tags/') and depends on the image build job, so it can't be triggered manually. This new workflow is standalone and takes a version input (default 0.14.0-dev) so the fork can publish a chart with the same fix as the image (e.g. 0.14.0-sha-) and install from oci://ghcr.io/timoa/charts/ without depending on the upstream OCI registry. Mirrors the image pattern: push the workflow, then 'gh workflow run' to publish. --- .github/workflows/chart-publish.yml | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/chart-publish.yml diff --git a/.github/workflows/chart-publish.yml b/.github/workflows/chart-publish.yml new file mode 100644 index 0000000..fb521ed --- /dev/null +++ b/.github/workflows/chart-publish.yml @@ -0,0 +1,42 @@ +name: Publish chart + +on: + workflow_dispatch: + inputs: + version: + description: 'Chart version to publish (e.g. 0.14.0-sha-fdfc584)' + required: false + default: '0.14.0-dev' + type: string + +permissions: + contents: read + packages: write + +jobs: + publish-chart: + name: Package & push Helm chart to GHCR + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: azure/setup-helm@v4 + + - name: Log in to GHCR (Helm OCI) + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io \ + --username "${{ github.actor }}" \ + --password-stdin + + - name: Package chart + run: | + VERSION="${{ inputs.version }}" + helm package charts/openconcho \ + --version "$VERSION" \ + --app-version "$VERSION" + + - name: Push chart + run: | + VERSION="${{ inputs.version }}" + helm push "openconcho-$VERSION.tgz" \ + oci://ghcr.io/${{ github.repository_owner }}/charts