Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions charts/rstudio-connect/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: rstudio-connect
description: Official Helm chart for Posit Connect
version: 0.8.26
version: 0.8.27
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Piling on with the others. Lets make this 0.9. Big change.

apiVersion: v2
appVersion: 2026.01.1
icon: https://raw.githubusercontent.com/rstudio/helm/main/images/posit-icon-fullcolor.svg
Expand All @@ -13,7 +13,7 @@ maintainers:
url: https://github.com/sol-eng
dependencies:
- name: rstudio-library
version: 0.1.35
version: 0.1.37
repository: https://helm.rstudio.com
annotations:
artifacthub.io/images: |
Expand Down
4 changes: 4 additions & 0 deletions charts/rstudio-connect/NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.8.27

- Adds support for a native-Go implementation of Off-Host Execution (OHE) mode. Set `kubernetes.enabled = true` to switch to the new implementation. See the [migration guide](<todo: insert migration guide docs link>) in the Connect admin documentation for guidance on migrating existing configurations.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the migration guide be in place before this PR is merged?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm planning to merge this PR shortly after the March release when we can officially announce the feature.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add more details to the change log. Maybe showing a brief example of how you would do templating with Launcher and how you now do it with the new native Kubernetes.


## 0.8.26

- Fix bug in chart where sharedStorage.subPath did not propagate to launcher-managed job volumeMounts.
Expand Down
20 changes: 14 additions & 6 deletions charts/rstudio-connect/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Posit Connect

![Version: 0.8.26](https://img.shields.io/badge/Version-0.8.26-informational?style=flat-square) ![AppVersion: 2026.01.1](https://img.shields.io/badge/AppVersion-2026.01.1-informational?style=flat-square)
![Version: 0.8.27](https://img.shields.io/badge/Version-0.8.27-informational?style=flat-square) ![AppVersion: 2026.01.1](https://img.shields.io/badge/AppVersion-2026.01.1-informational?style=flat-square)

#### _Official Helm chart for Posit Connect_

Expand Down Expand Up @@ -30,11 +30,11 @@ To ensure reproducibility in your environment and insulate yourself from future

## Installing the chart

To install the chart with the release name `my-release` at version 0.8.26:
To install the chart with the release name `my-release` at version 0.8.27:

```{.bash}
helm repo add rstudio https://helm.rstudio.com
helm upgrade --install my-release rstudio/rstudio-connect --version=0.8.26
helm upgrade --install my-release rstudio/rstudio-connect --version=0.8.27
```

To explore other chart versions, look at:
Expand All @@ -48,7 +48,7 @@ helm search repo rstudio/rstudio-connect -l
### 0.8.0

- When upgrading to version 0.8.0 or later, Connect now runs in [Off-Host Execution mode](https://docs.posit.co/connect/admin/getting-started/off-host-install/) by default
- If you desire to run Connect not in Off-Host Execution mode, then set `securityContext.privileged: true` and `launcher.enabled: false`
- If you desire to run Connect in Local Execution mode, then set `securityContext.privileged: true` and `launcher.enabled: false`

## Required configuration

Expand Down Expand Up @@ -248,6 +248,14 @@ The Helm `config` values are converted into the `rstudio-connect.gcfg` service c
| ingress.ingressClassName | string | `""` | The ingressClassName for the ingress resource. Only used for clusters that support networking.k8s.io/v1 Ingress resources |
| ingress.tls | list | `[]` | |
| initContainers | bool | `false` | The initContainer spec that will be used verbatim |
| kubernetes.defaultInitContainer.enabled | bool | `true` | Whether to enable the defaultInitContainer. If disabled, you must ensure that the session components are available another way. |
| kubernetes.defaultInitContainer.repository | string | `"ghcr.io/rstudio/rstudio-connect-content-init"` | The repository to use for the Content InitContainer image |
| kubernetes.defaultInitContainer.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. |
| kubernetes.defaultInitContainer.tagPrefix | string | `"ubuntu2204-"` | A tag prefix for the Content InitContainer image (common selections: jammy-, ubuntu2204-). Only used if tag is not defined |
| kubernetes.defaultJobOverlay | object | `{"apiVersion":"batch/v1","kind":"Job","spec":{"template":{"spec":{"containers":[{"name":"connect-content","volumeMounts":[{"mountPath":"/opt/rstudio-connect/R","name":"rsc-volume","subPath":"R"},{"mountPath":"/opt/rstudio-connect/python","name":"rsc-volume","subPath":"python"},{"mountPath":"/opt/rstudio-connect/scripts","name":"rsc-volume","subPath":"scripts"},{"mountPath":"/opt/rstudio-connect/ext","name":"rsc-volume","subPath":"ext"}]}],"initContainers":[{"name":"connect-content-init","volumeMounts":[{"mountPath":"/mnt/rstudio-connect-runtime/","name":"rsc-volume"}]}],"volumes":[{"emptyDir":{},"name":"rsc-volume"}]}}}}` | defaultJobOverlay contains the Kubernetes Job definition which is used as an overlay "base" when launching a content job in Kubernetes. Conceptually this is a similar to a Kustomize base. Connect then applies any required job configuration on-top of the overlay base to produce a final job definition before submitting the Job to Kubernetes. https://docs.posit.co/connect/__unreleased__/admin/appendix/off-host/direct-runner/kubernetes-job-overlays.html https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/#bases-and-overlays |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there a populated defaultJobOverlay and not one for defaultServiceOverlay?

Are the values you have listed there meaningful? ie, if I override this with a less complete object, does the content fail?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the defaults defined in the chart are required to configure the init-container. Any customization should be additive to the default values for the Job overlay.

The service overlay doesn't include any defaults because Connect will create a ClusterIP service by default.

Note: We'll be renaming defaultJobOverlay/defaultServiceOverlay to be defaultResourceJobBase/defaultResourceServiceBase for clarity. Calling them overlays is a little misleading.

Copy link
Contributor

@samcofer samcofer Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this behavior:

Connect will create a ClusterIP service by default.

And this behavior:

the defaults defined in the chart are required to configure the init-container.

Seems counter intuitive right? Why does Connect do something without anything configured in the first case, but then require configuration to do the basics in the second case? My vote is to add the redundant serviceOverlay to the chart with the values that Connect is already defaulting too.

Copy link
Contributor Author

@dbkegley dbkegley Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it help if we moved the init container configuration into connect the helm chart logic so that no default job manifest is required unless you want to customize the job ? Maybe that simplification in the chart along with a fully commented (optional/example) job structure would be best.

More generally about the overlays, while it’s definitely more complex because of the deep nesting, the new job overlays are intended to be more straightforward and less surprising than the launcher templates. The job overlay is a real kubernetes job manifest so the official kubernetes documentation can be used as a valid reference rather than having to rely only on the examples from our helm chart docs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no default job manifest is required unless you want to customize the job

I think this approach makes sense. Just like with the old template values, you don't need to set anything unless you're doing customization.

The job overlay is a real kubernetes job manifest so the official kubernetes documentation can be used as a valid reference rather than having to rely only on the examples from our helm chart docs.

I think we should include several examples in the Helmchart documentation. And let's make it really clear that what you're putting in here is a job/service spec and we can link to the official Kubernetes docs.

| kubernetes.defaultServiceOverlay | object | `{}` | defaultServiceOverlay contains the Kubernetes Service definition which is used as an overlay "base" when creating a content job's Service in Kubernetes. Conceptually this is a similar to a Kustomize base. Connect then applies any required Service configuration on-top of the overlay base to produce a final Service definition. |
| kubernetes.enabled | bool | `false` | Whether to enable the native Go implementation of off-host execution for running content-jobs in remote Kubernetes pods. |
| kubernetes.namespace | string | `""` | The namespace to launch connect-content jobs into. Uses the Release namespace by default |
| launcher.additionalRuntimeImages | list | `[]` | Optional. Additional images to append to the end of the "launcher.customRuntimeYaml" (in the "images" key). If `customRuntimeYaml` is a "map", then "additionalRuntimeImages" will only be used if it is a "list". |
| launcher.customRuntimeYaml | string | `"base"` | Optional. The runtime.yaml definition of Kubernetes runtime containers. Defaults to "base", which pulls in the default runtime.yaml file. If changing this value, be careful to include the images that you have already used. If set to "pro", will pull in the "pro" versions of the default runtime images (i.e. including the pro drivers at the cost of a larger image). Starting with Connect v2023.05.0, this configuration is used to bootstrap the initial set of execution environments the first time the server starts. If any execution environments already exist in the database, these values are ignored; execution environments are not created or modified during subsequent restarts. |
| launcher.defaultInitContainer | object | `{"enabled":true,"imagePullPolicy":"","repository":"ghcr.io/rstudio/rstudio-connect-content-init","resources":{},"securityContext":{},"tag":"","tagPrefix":"ubuntu2204-"}` | Image definition for the default Posit Connect Content InitContainer |
Expand Down Expand Up @@ -309,8 +317,8 @@ The Helm `config` values are converted into the `rstudio-connect.gcfg` service c
| prometheusExporter.resources | object | `{}` | resource specification for the prometheus exporter sidecar |
| prometheusExporter.securityContext | object | `{}` | securityContext for the prometheus exporter sidecar |
| rbac.clusterRoleCreate | bool | `false` | Whether to create the ClusterRole that grants access to the Kubernetes nodes API. This is used by the Launcher to get all of the IP addresses associated with the node that is running a particular job. In most cases, this can be disabled as the node's internal address is sufficient to allow proper functionality. |
| rbac.create | bool | `true` | Whether to create rbac. (also depends on launcher.enabled = true) |
| rbac.serviceAccount | object | `{"annotations":{},"create":true,"labels":{},"name":""}` | The serviceAccount to be associated with rbac (also depends on launcher.enabled = true) |
| rbac.create | bool | `true` | Whether to create rbac. (also depends on launcher.enabled = true or kubernetes.enabled = true) |
| rbac.serviceAccount | object | `{"annotations":{},"create":true,"labels":{},"name":""}` | The serviceAccount to be associated with rbac (also depends on launcher.enabled = true or kubernetes.enabled = true) |
| readinessProbe | object | `{"enabled":true,"failureThreshold":3,"httpGet":{"path":"/__ping__","port":3939},"initialDelaySeconds":3,"periodSeconds":3,"successThreshold":1,"timeoutSeconds":1}` | Used to configure the container's readinessProbe. Only included if enabled = true |
| replicas | int | `1` | The number of replica pods to maintain for this service |
| resources | object | `{}` | Defines resources for the rstudio-connect container |
Expand Down
2 changes: 1 addition & 1 deletion charts/rstudio-connect/README.md.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
### 0.8.0

- When upgrading to version 0.8.0 or later, Connect now runs in [Off-Host Execution mode](https://docs.posit.co/connect/admin/getting-started/off-host-install/) by default
- If you desire to run Connect not in Off-Host Execution mode, then set `securityContext.privileged: true` and `launcher.enabled: false`
- If you desire to run Connect in Local Execution mode, then set `securityContext.privileged: true` and `launcher.enabled: false`

## Required configuration

Expand Down
4 changes: 4 additions & 0 deletions charts/rstudio-connect/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ Please consider removing this configuration value.

{{- /* chart deprecations and misconfiguration warnings */ -}}

{{- if and .Values.launcher.enabled .Values.kubernetes.enabled }}
{{- fail "\n\n`launcher.enabled` and `kubernetes.enabled` cannot both be true"}}
{{- end }}

{{- if and .Values.launcher.useTemplates .Values.launcher.enabled }}
{{- range $k,$v := .Values.launcher.launcherKubernetesProfilesConf }}
{{- if hasKey $v "job-json-overrides" }}
Expand Down
27 changes: 27 additions & 0 deletions charts/rstudio-connect/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,33 @@ app.kubernetes.io/instance: {{ .Release.Name }}
{{- $launcherDict := dict "Launcher" ( $launcherSettingsDict ) }}
{{- $defaultConfig = merge $defaultConfig $launcherDict }}
{{- end }}
{{- if .Values.kubernetes.enabled }}
{{- $namespace := default $.Release.Namespace .Values.kubernetes.namespace }}
{{- $kubernetesSettingsDict := dict "Enabled" ("true") "Namespace" ($namespace) }}
{{- if and (or .Values.sharedStorage.create .Values.sharedStorage.mount) .Values.sharedStorage.mountContent }}
{{- $dataDirPVCName := default (print (include "rstudio-connect.fullname" .) "-shared-storage" ) .Values.sharedStorage.name }}
{{- $_ := set $kubernetesSettingsDict "DataDirPVCName" $dataDirPVCName }}
{{- end }}
{{- if .Values.kubernetes.defaultJobOverlay }}
{{- if and .Values.kubernetes.defaultInitContainer.enabled }}
{{- $defaultVersion := .Values.versionOverride | default $.Chart.AppVersion }}
{{- $initContainerImageTag := .Values.kubernetes.defaultInitContainer.tag | default (printf "%s%s" .Values.kubernetes.defaultInitContainer.tagPrefix $defaultVersion )}}
{{- $initContainerImage := print .Values.kubernetes.defaultInitContainer.repository ":" ( $initContainerImageTag ) }}
{{- range (.Values.kubernetes.defaultJobOverlay.spec.template.spec).initContainers | default list }}
{{- if eq .name "connect-content-init" }}
{{- $_ := set . "image" $initContainerImage }}
{{- break }}
{{- end }}
{{- end }}
{{- end }}
{{- $_ := set $kubernetesSettingsDict "DefaultJobOverlay" (default "/etc/rstudio-connect/job.yaml" .Values.config.Kubernetes.DefaultJobOverlay) }}
{{- end }}
{{- if .Values.kubernetes.defaultServiceOverlay }}
{{- $_ = set $kubernetesSettingsDict "DefaultServiceOverlay" (default "/etc/rstudio-connect/service.yaml" .Values.config.Kubernetes.DefaultServiceOverlay) }}
{{- end }}
{{- $kubernetesDict := dict "Kubernetes" ( $kubernetesSettingsDict ) }}
{{- $defaultConfig = merge $defaultConfig $kubernetesDict }}
{{- end }}
{{- /* default licensing configuration */}}
{{- if .Values.license.server }}
{{- $licenseDict := dict "Licensing" ( dict "LicenseType" ("Remote") ) }}
Expand Down
2 changes: 1 addition & 1 deletion charts/rstudio-connect/templates/configmap-prestart.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{- if .Values.launcher.enabled }}
{{- if or .Values.launcher.enabled .Values.kubernetes.enabled }}
---
apiVersion: v1
kind: ConfigMap
Expand Down
10 changes: 10 additions & 0 deletions charts/rstudio-connect/templates/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ metadata:
data:
rstudio-connect.gcfg: |
{{- include "rstudio-connect.config" . | nindent 4 }}
{{- if .Values.kubernetes.enabled }}
{{- if .Values.kubernetes.defaultJobOverlay }}
job.yaml: |
{{- .Values.kubernetes.defaultJobOverlay | toYaml | nindent 4 }}
{{- end }}
{{- if .Values.kubernetes.defaultServiceOverlay }}
service.yaml: |
{{- .Values.kubernetes.defaultServiceOverlay | toYaml | nindent 4 }}
{{- end }}
{{- end }}
{{- $sessionTemplate := deepCopy .Values.launcher.templateValues }}
{{- if .Values.launcher.enabled }}
runtime.yaml: |
Expand Down
22 changes: 18 additions & 4 deletions charts/rstudio-connect/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ spec:
{{- if and .Values.prometheus.legacy .Values.prometheusExporter.enabled }}
checksum/config-graphite: {{ include (print $.Template.BasePath "/configmap-graphite-exporter.yaml") . | sha256sum }}
{{- end }}
{{- if .Values.launcher.enabled }}
{{- if or .Values.launcher.enabled .Values.kubernetes.enabled }}
checksum/config-prestart: {{ include (print $.Template.BasePath "/configmap-prestart.yaml") . | sha256sum }}
{{- end }}
{{- if .Values.prometheus }}
Expand Down Expand Up @@ -76,7 +76,7 @@ spec:
* and also the "Note" callout at the end of this section:
* https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-multiple-service-accounts
*/}}
{{- if and .Values.rbac.create .Values.launcher.enabled }}
{{- if and .Values.rbac.create (or .Values.launcher.enabled .Values.kubernetes.enabled) }}
{{ $serviceAccountName := default (include "rstudio-connect.fullname" .) .Values.rbac.serviceAccount.name }}
serviceAccountName: {{ $serviceAccountName }}
{{- else }}
Expand Down Expand Up @@ -156,7 +156,7 @@ spec:
{{- if .Values.command }}
command:
{{ toYaml .Values.command | indent 10 }}
{{- else if .Values.launcher.enabled }}
{{- else if (or .Values.launcher.enabled .Values.kubernetes.enabled) }}
command:
- tini
- -s
Expand All @@ -165,7 +165,7 @@ spec:
{{- if .Values.args }}
args:
{{ toYaml .Values.args | indent 10 }}
{{- else if .Values.launcher.enabled }}
{{- else if (or .Values.launcher.enabled .Values.kubernetes.enabled) }}
args:
- /scripts/prestart.bash
- /usr/local/bin/startup.sh
Expand All @@ -186,6 +186,18 @@ spec:
- name: rstudio-connect-config
mountPath: "/etc/rstudio-connect/rstudio-connect.gcfg"
subPath: "rstudio-connect.gcfg"
{{- if .Values.kubernetes.enabled }}
{{- if .Values.kubernetes.defaultJobOverlay }}
- name: rstudio-connect-config
mountPath: "/etc/rstudio-connect/job.yaml"
subPath: "job.yaml"
{{- end }}
{{- if .Values.kubernetes.defaultServiceOverlay }}
- name: rstudio-connect-config
mountPath: "/etc/rstudio-connect/service.yaml"
subPath: "service.yaml"
{{- end }}
{{- end }}
{{- if .Values.launcher.enabled }}
- name: rstudio-connect-config
mountPath: "/etc/rstudio-connect/runtime.yaml"
Expand Down Expand Up @@ -317,6 +329,8 @@ spec:
name: {{ include "rstudio-connect.fullname" . }}-overrides
defaultMode: 0644
{{- end }}
{{- end }}
{{- if or .Values.launcher.enabled .Values.kubernetes.enabled }}
- name: rstudio-connect-prestart
configMap:
name: {{ include "rstudio-connect.fullname" . }}-prestart
Expand Down
9 changes: 7 additions & 2 deletions charts/rstudio-connect/templates/rbac.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
{{- if and (.Values.rbac.create) (.Values.launcher.enabled) }}
{{- if and (.Values.rbac.create) (or .Values.launcher.enabled .Values.kubernetes.enabled) }}
{{ $namespace := $.Release.Namespace }}
{{ $targetNamespace := default $.Release.Namespace .Values.launcher.namespace }}
{{ $targetNamespace := $.Release.Namespace }}
{{- if .Values.launcher.enabled }}
{{ $targetNamespace = default $targetNamespace .Values.launcher.namespace }}
{{- else }}
{{ $targetNamespace = default $targetNamespace .Values.kubernetes.namespace }}
{{- end }}
{{ $serviceAccountName := default (include "rstudio-connect.fullname" .) .Values.rbac.serviceAccount.name }}
{{ $serviceAccountCreate := .Values.rbac.serviceAccount.create }}
{{ $serviceAccountAnnotations := .Values.rbac.serviceAccount.annotations }}
Expand Down
24 changes: 24 additions & 0 deletions charts/rstudio-connect/tests/kubernetes-values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
launcher:
enabled: false

kubernetes:
enabled: true
defaultJobOverlay:
apiVersion: batch/v1
kind: Job
spec:
template:
spec:
containers:
- name: connect-content
volumeMounts:
- mountPath: /opt/rstudio-connect
name: rsc-volume
initContainers:
- name: connect-content-init
volumeMounts:
- mountPath: /mnt/rstudio-connect-runtime/
name: rsc-volume
volumes:
- emptyDir: {}
name: rsc-volume
Comment on lines +7 to +24
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samcofer something like this?

Suggested change
apiVersion: batch/v1
kind: Job
spec:
template:
spec:
containers:
- name: connect-content
volumeMounts:
- mountPath: /opt/rstudio-connect
name: rsc-volume
initContainers:
- name: connect-content-init
volumeMounts:
- mountPath: /mnt/rstudio-connect-runtime/
name: rsc-volume
volumes:
- emptyDir: {}
name: rsc-volume
apiVersion: batch/v1
kind: Job
metadata:
labels: # templateValues.job.labels
app: data-processing
environment: staging
annotations: # templateValues.job.annotations
my.company.com/annotation1: value
spec:
# Job lifecycle management
ttlSecondsAfterFinished: 86400 # Time to live for the Job and its pods after they complete (e.g., 24 hours)
template:
metadata:
labels: # templateValues.pod.labels
app: data-processing
annotations: # templateValues.pod.annotations
my.company.com/annotation1: value
spec:
containers:
- name: processing-container # sidecar container
image: busybox
command:
[
"sh",
"-c",
"echo 'Starting job...'; sleep 60; echo 'Job finished'",
]
- name: connect-content
resources: # templateValues.pod.resources
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
env: # templateValues.pod.env
- name: LOG_LEVEL
value: "INFO"
volumeMounts:
- mountPath: /opt/rstudio-connect
name: rsc-volume
initContainers:
- name: connect-content-init
volumeMounts:
- mountPath: /mnt/rstudio-connect-runtime/
name: rsc-volume
volumes:
- emptyDir: {}
name: rsc-volume
nodeSelector: # templateValues.pod.nodeSelector
kubernetes.io/os: linux
tolerations: # templateValues.pod.tolerations
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, that's pretty much what I'm talking about. I still think it would be a benefit to have an example in the examples folder that has both a full pod: and new launcher defined, with some comments explaining how they produce the same yaml.

Loading