Helm Installation Guide
Installing the KFP Operator
This guide provides comprehensive instructions for installing the Kubeflow Pipelines Operator in your Kubernetes cluster. We recommend using Helm for a declarative approach to managing Kubernetes resources.
Overview
The KFP Operator installation consists of three main components:
- Prerequisites: Required dependencies (Argo Workflows, Argo Events)
- KFP Operator: The core operator and controllers
- Providers: ML orchestration platform integrations (KFP, Vertex AI)
Prerequisites
Before installing the KFP Operator, ensure your cluster meets these requirements:
Cluster Requirements
- Kubernetes: v1.21 or later (tested up to v1.28)
- Cluster Admin Access: Required for installing CRDs and cluster-wide resources
- Storage: Persistent storage for pipeline artifacts and metadata
- Network: Outbound internet access for downloading container images
Required Dependencies
1. Argo Workflows
Version: 3.1.6 - 3.4.x Purpose: Workflow execution engine for pipeline orchestration
# Install Argo Workflows (cluster-wide)
kubectl create namespace argo
kubectl apply -n argo -f https://github.com/argoproj/argo-workflows/releases/download/v3.4.4/install.yaml
Verification:
kubectl get pods -n argo
# Should show argo-server and workflow-controller pods running
2. Argo Events (Optional but Recommended)
Version: 1.7.4 or later Purpose: Event-driven pipeline automation
# Install Argo Events (cluster-wide)
kubectl create namespace argo-events
kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-events/stable/manifests/install.yaml
Verification:
kubectl get pods -n argo-events
# Should show eventbus-controller and eventsource-controller pods running
Optional Dependencies
Cert-Manager (Recommended for Production)
Purpose: Automatic TLS certificate management for webhooks
# Install cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
Prometheus Operator (For Monitoring)
Purpose: Metrics collection and monitoring
# Install Prometheus Operator
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack
Helm Installation
This guide assumes you have Helm 3.x installed:
# Verify Helm installation
helm version
# Should show version 3.x
Installing the KFP Operator
The KFP Operator installation consists of the core operator and at least one provider for ML orchestration.
Quick Installation
For a basic installation with default settings:
# Add the KFP Operator Helm repository
helm repo add kfp-operator https://sky-uk.github.io/kfp-operator/
helm repo update
# Install with default values
helm install kfp-operator kfp-operator/kfp-operator
Custom Installation
For production deployments, create a custom values.yaml file:
# values.yaml - Basic configuration
namespace:
create: true
name: kfp-operator-system
manager:
replicas: 2 # High availability
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
# Monitoring configuration
monitoring:
create: true
serviceMonitor:
create: true # For Prometheus Operator
# Enable event-driven workflows
statusFeedback:
enabled: true
# Logging configuration
logging:
verbosity: 1 # 0=error, 1=info, 2=debug
Install with custom configuration:
helm install kfp-operator kfp-operator/kfp-operator -f values.yaml
Installation Methods
Method 1: Helm Repository (Recommended)
# Add repository and install
helm repo add kfp-operator https://sky-uk.github.io/kfp-operator/
helm install kfp-operator kfp-operator/kfp-operator -f values.yaml
Method 2: Local Development (Requires local kubernetes cluster)
# Clone repository and install from source
git clone https://github.com/sky-uk/kfp-operator.git
cd kfp-operator
helm install kfp-operator ./helm/kfp-operator -f values.yaml
Verification
Verify the installation was successful:
# Check operator pods
kubectl get pods -n kfp-operator-system
# Check CRDs were installed
kubectl get crd | grep pipelines.kubeflow.org
# Check operator logs
kubectl logs -n kfp-operator-system deployment/kfp-operator-controller-manager
Expected output:
NAME READY STATUS RESTARTS AGE
kfp-operator-controller-manager-xxx-xxx 2/2 Running 0 2m
Namespace Configuration
The operator can be installed in any namespace. Common patterns:
Dedicated Namespace (Recommended)
namespace:
create: true
name: kfp-operator-system
Existing Namespace
namespace:
create: false
name: ml-platform
Multi-tenant Setup
# Install operator in system namespace
namespace:
name: kfp-operator-system
# Configure RBAC for multiple tenant namespaces
manager:
rbac:
create: true
# Additional cluster roles will be created
Configuration Values
Valid configuration options to override the Default values.yaml are:
| Parameter | Description | Default |
|---|---|---|
containerRegistry | Container Registry base path for all container images | "ghcr.io/kfp-operator" |
crds.create | Install the operator’s Custom Resource Definitions | true |
crds.keep | Retain the Custom Resource Definitions when the chart is uninstalled | true |
fullnameOverride | Override the fully qualified name of chart resources | "" |
kfp-provider-workflows.enabled | Enable the kfp-provider-workflows subchart. Disabled by default; install the subchart separately, one release per provider namespace | false |
logging.verbosity | Logging verbosity for all components - see the logging documentation for valid values | nil |
manager.configuration | Manager configuration as defined in Configuration (note that you can omit compilerImage and kfpSdkImage when specifying containerRegistry as default values will be applied) | {} |
manager.leaderElection.enabled | Toggle leader election - defaults to true | true |
manager.leaderElection.id | Leader election Lease resource name - defaults to kfp-operator-lock | "kfp-operator-lock" |
manager.metadata | Object Metadata for the manager’s pods | {} |
manager.monitoring.create | Create the manager’s monitoring resources | false |
manager.monitoring.rbacSecured | Enable additional RBAC-based security | false |
manager.monitoring.serviceMonitor.create | Create a ServiceMonitor for the Prometheus Operator | false |
manager.monitoring.serviceMonitor.endpointConfiguration | Additional configuration to be used in the service monitor endpoint (path, port and scheme are provided) | {} |
manager.multiversion.enabled | Enable multiversion API. Should be used in production to allow version migration, disable for simplified installation | false |
manager.multiversion.storedVersion | Specifies which CRD version should be set as the stored version. Only takes effect if manager.multiversion.enabled is set to true. Defaults to the latest version. | "v1beta1" |
manager.rbac.create | Create roles and rolebindings for the operator | true |
manager.replicas | Number of replicas for the manager deployment | 1 |
manager.resources | Manager resources as per k8s documentation | {"limits":{"cpu":"100m","memory":"300Mi"},"requests":{"cpu":"100m","memory":"200Mi"}} |
manager.runcompletionWebhook.endpoints | Array of endpoints for the run completion event handlers to be called when a run completion event is passed | [] |
manager.runcompletionWebhook.servicePort | Port for the run completion event webhook service to listen on - defaults to 8082 | 8082 |
manager.serviceAccount.create | Create the manager’s service account or expect it to be created externally | true |
manager.serviceAccount.name | Manager service account’s name | "kfp-operator-controller-manager" |
manager.webhookCertificates.caBundle | CA bundle of the certificate authority that has signed the webhook’s certificate, required if the custom provider is chosen | "" |
manager.webhookCertificates.provider | K8s conversion webhook TLS certificate provider - choose cert-manager for Helm to deploy certificates if cert-manager is available or custom otherwise (see below) | "cert-manager" |
manager.webhookCertificates.secretName | Name of a K8s secret deployed into the operator namespace to secure the webhook endpoint with, required if the custom provider is chosen | "" |
manager.webhookServicePort | Port for the webhook service to listen on - defaults to 9443 | 9443 |
namespace.create | Create the namespace for the operator | true |
namespace.metadata | Object Metadata for the operator namespace | {} |
namespace.name | Operator namespace name | "kfp-operator-system" |
provider.env | Additional environment variables for provider containers | [] |
provider.labels | Additional labels applied to provider resources | {} |
provider.metricsPort | Port for provider metrics endpoints | 8081 |
provider.podTemplateLabels | Additional labels applied to provider pod templates | {} |
provider.replicas | Number of replicas for provider deployments | 1 |
provider.resources | Provider resources as per k8s documentation | {"limits":{"cpu":"500m","memory":"256Mi"},"requests":{"cpu":"250m","memory":"128Mi"}} |
provider.servicePort | Port for provider services to listen on | 8080 |
provider.volumeMounts | Additional volume mounts for provider containers | [] |
provider.volumes | Additional volumes to mount into provider pods | [] |
runcompletionEventTrigger.enabled | Whether the run completion event trigger should be installed - defaults to false | false |
runcompletionEventTrigger.metadata | Object Metadata for the run completion event trigger’s pods | {} |
runcompletionEventTrigger.metrics.port | Port for the run completion event trigger metrics endpoint | 8081 |
runcompletionEventTrigger.monitoring | Whether monitoring resources should be created for the run completion event trigger - defaults to false | false |
runcompletionEventTrigger.nats.server.port | Port of the NATS server the run completion event trigger connects to | 4222 |
runcompletionEventTrigger.nats.subject | NATS subject the run completion event trigger publishes events to | "events" |
runcompletionEventTrigger.replicas | Number of replicas for the run completion event trigger deployment | 1 |
runcompletionEventTrigger.server.port | Port for the run completion event trigger gRPC server to listen on | 50051 |
statusFeedback.enabled | Whether run completion eventing and status update feedback loop should be installed - defaults to false | false |
Examples for these values can be found in the test configuration
Providers
Please refer to your chosen provider instructions before proceeding. Supported providers are:
To install your chosen provider, create a Provider resource in a namespace that the operator can access (see the rbac setup below for reference). Once it is applied the Provider controller will reconcile and create the Provider Deployment and Provider Service within the same namespace that the Provider resource was applied.
Provider workflows
The operator runs resource-management Argo Workflows in the same namespace as the Provider they belong to, so that each provider is isolated from the others. Argo cannot reference a WorkflowTemplate across namespaces, therefore every provider namespace must contain its own copy of the WorkflowTemplates and the execution RBAC (ServiceAccount, Role and RoleBinding) the workflows run with.
These per-namespace resources are provisioned by the kfp-provider-workflows chart, which is installed once per provider namespace. Install one release for each namespace in which you create a Provider:
helm install <release-name> ./helm/kfp-operator/charts/kfp-provider-workflows \
--namespace <provider-namespace> \
--set namespace=<provider-namespace>
By default the chart also creates the Provider resource. Provide its required spec fields, or set provider.create=false to manage the Provider yourself:
helm install <release-name> ./helm/kfp-operator/charts/kfp-provider-workflows \
--namespace <provider-namespace> \
--set namespace=<provider-namespace> \
--set provider.name=<provider-name> \
--set provider.serviceImage=<provider-service-image> \
--set provider.pipelineRootStorage=<pipeline-root-storage>
Valid configuration options to override the Default values.yaml are:
| Parameter | Description | Default |
|---|---|---|
argo.containerDefaults | Container spec defaults applied to the Argo workflow pods | {} |
argo.metadata | Metadata (labels/annotations) applied to the Argo workflow pods | {} |
argo.rbac.create | Create the namespace-scoped workflow-executor Role and its RoleBinding. Set to false to manage these resources externally | true |
argo.securityContext | Security Context applied to the Argo workflow pods. Set to null or runAsNonRoot to false to run as root | {"fsGroup":1000,"runAsNonRoot":true,"runAsUser":1000} |
argo.serviceAccount.create | Create the Argo workflow ServiceAccount. Set to false to reuse an existing one | true |
argo.serviceAccount.metadata | Object Metadata for the Argo workflow ServiceAccount | {} |
argo.serviceAccount.name | ServiceAccount the Argo workflows run as, created in the provider namespace | "kfp-operator-argo" |
argo.stepTimeoutSeconds.compile | Timeout in seconds for compiler steps | 1800 |
argo.stepTimeoutSeconds.default | Default timeout in seconds for workflow steps | 300 |
argo.ttlStrategy.secondsAfterCompletion | TTL Strategy - seconds to retain completed Argo Workflows before cleanup | 3600 |
namespace | Namespace the WorkflowTemplates, RBAC and Provider resource are created in. Must match the namespace the Provider runs in. Defaults to the release namespace when left empty | "" |
provider.allowedNamespaces | Namespaces allowed to reference this provider. An empty list allows all namespaces | [] |
provider.create | When true, the chart also renders the Provider custom resource | true |
provider.frameworks | Pipeline frameworks the provider supports (see the Provider CRD reference) | [] |
provider.metadata | Object Metadata for the Provider resource | {} |
provider.name | Provider resource name. Defaults to the release name when left empty | "" |
provider.parameters | Free-form provider parameters (map of name to value) | {} |
provider.pipelineRootStorage | Pipeline root storage location. Required when provider.create is true | "" |
provider.podTemplateEnv | Environment variables applied to the generated provider-service container, merged over the operator’s defaultProviderValues env; per-provider values take precedence on name collision | [] |
provider.podTemplateVolumeMounts | Volume mounts applied to the generated provider-service container, merged over the operator’s defaultProviderValues volume mounts; per-provider values take precedence on mount-path collision | [] |
provider.podTemplateVolumes | Volumes applied to the generated provider-service pod, merged over the operator’s defaultProviderValues volumes; per-provider values take precedence on name collision | [] |
provider.serviceAccount.create | Create the provider service ServiceAccount. Set to false to reuse an existing one | true |
provider.serviceAccount.metadata | Object Metadata for the provider service ServiceAccount | {} |
provider.serviceAccount.name | Provider service ServiceAccount name, created in the provider namespace and referenced by the Provider spec. Defaults to kfp-provider-<provider-name> when left empty. Its cluster-scoped viewer/eventing bindings are managed externally by the platform | "" |
provider.serviceImage | Provider service image. Required when provider.create is true | "" |
The argo.* execution values (argo.ttlStrategy, argo.stepTimeoutSeconds, argo.securityContext, argo.containerDefaults and argo.metadata) control how the provider’s Argo Workflows run. Override them per release if a provider namespace needs different workflow behaviour.
Note: because workflows now run in the provider namespace, the artifact-repository credentials Secret that your Argo workflow-controller references (the accessKeySecret/secretKeySecret in its artifactRepository configuration — for example the MinIO/S3 secret) must exist in each provider namespace. Argo resolves that Secret in the namespace the workflow pods run in, so a copy that only lives in the Argo or operator namespace is not sufficient. The kfp-provider-workflows chart does not manage this Secret — it holds credentials owned by the platform team — so you must create it in every provider namespace yourself.
What the chart provisions
The kfp-provider-workflows chart provisions only namespace-scoped resources — everything a provider needs to operate inside its own namespace. Cluster-scoped resources are deliberately left to the platform team (see Cluster-scoped RBAC below). A single release renders:
| Resource | Kind | Gated by | Purpose |
|---|---|---|---|
common-steps, create-simple, update-simple, create-compiled, update-compiled, compiled-workflow-steps, delete | WorkflowTemplate | always | The resource-management workflow definitions the operator invokes. |
kfp-operator-argo | ServiceAccount | argo.serviceAccount.create | The account the Argo Workflow pods run as. |
workflow-executor | Role + RoleBinding | argo.rbac.create | Grants the Argo workflow account the in-namespace permissions its steps need (pods get/patch, workflowtaskresults create/patch). Bound to the Argo workflow ServiceAccount. |
kfp-provider-<provider-name> | ServiceAccount | provider.create and provider.serviceAccount.create | The account the provider service runs as; referenced by the Provider spec. |
<provider-name> | Provider | provider.create | The Provider custom resource itself. |
Role-based access control (RBAC) for providers
The namespace-scoped RBAC above is handled by the chart. The resources in this section are cluster-scoped and are therefore not created by the chart — you must create them once (they are shared across provider namespaces). If you are not using the chart at all, see Deploying without Helm for the complete list including the namespace-scoped resources.
In order for Event Source Servers and the Controller to read the Providers you must configure their service accounts to have read permissions of Provider resources. e.g:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kfp-operator-kfp-providers-viewer-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kfp-operator-providers-viewer-role
subjects:
- kind: ServiceAccount
name: kfp-operator-kfp #Used by Event Source Server
namespace: kfp-operator-system
- kind: ServiceAccount
name: kfp-operator-controller-manager #Used by KFP Controller
namespace: kfp-operator-system
The provider service account itself (kfp-provider-<provider-name> by default) is created by the chart in the provider namespace. It still needs cluster-scoped read access to RunConfiguration and Run resources, granted by binding it to the ClusterRoles the operator ships. Create one ClusterRoleBinding per provider service account (replace kfp-provider-example and kfp-namespace with your values):
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kfp-operator-example-runconfiguration-viewer-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kfp-operator-runconfiguration-viewer-role
subjects:
- kind: ServiceAccount
name: kfp-provider-example
namespace: kfp-namespace
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kfp-operator-example-run-viewer-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kfp-operator-run-viewer-role
subjects:
- kind: ServiceAccount
name: kfp-provider-example
namespace: kfp-namespace
Note: the workflow-executor Role/RoleBinding and the Argo workflow ServiceAccount are namespace-scoped and are created for you by the kfp-provider-workflows chart. You only need to author them manually when deploying without Helm.
Kubeflow completion eventing required RBACs
If using the Kubeflow Pipelines Provider you will also need a ClusterRole for permission to interact with argo workflows for the
eventing system for run completion events.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kfp-operator-kfp-eventsource-server-role
rules:
- apiGroups:
- argoproj.io
resources:
- workflows
verbs:
- get
- list
- patch
- update
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kfp-operator-kfp-eventsource-server-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kfp-operator-kfp-eventsource-server-role
subjects:
- kind: ServiceAccount
name: kfp-provider-example
namespace: kfp-namespace
Deploying without Helm
If you do not use the kfp-provider-workflows chart, you are responsible for creating every resource a provider needs yourself. Because Argo cannot reference a WorkflowTemplate across namespaces, the namespace-scoped resources must be recreated once per provider namespace, while the cluster-scoped resources are created once per cluster and shared.
Namespace-scoped resources (once per provider namespace)
These are the resources the chart would otherwise create. All of them live in the provider’s namespace.
| Resource | Kind | Purpose |
|---|---|---|
common-steps, create-simple, update-simple, create-compiled, update-compiled, compiled-workflow-steps, delete | WorkflowTemplate | The resource-management workflow definitions. Copy them from helm/kfp-operator/charts/kfp-provider-workflows/templates into the provider namespace. They must keep these exact names — the operator references them statically. |
Argo workflow ServiceAccount | ServiceAccount | The account the Argo Workflow pods run as. |
workflow-executor | Role | Grants the Argo workflow account pods (get, patch) and argoproj.io/workflowtaskresults (create, patch). |
workflow-executor | RoleBinding | Binds the workflow-executor Role to the Argo workflow ServiceAccount. |
Provider service ServiceAccount | ServiceAccount | The account the provider service runs as; referenced by the Provider spec. |
Provider | Provider | The provider custom resource. |
| Artifact-repository credentials | Secret | The accessKeySecret/secretKeySecret referenced by the Argo workflow-controller’s artifactRepository configuration (for example the MinIO/S3 secret). Argo resolves it in the namespace the workflow pods run in, so a copy must exist in each provider namespace. Not managed by the chart in the Helm path either. |
Example of the namespace-scoped RBAC (replace argo-workflow-sa, kfp-provider-example and kfp-namespace with your values):
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: argo-workflow-sa
namespace: kfp-namespace
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: kfp-provider-example
namespace: kfp-namespace
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: workflow-executor
namespace: kfp-namespace
rules:
- apiGroups: [""]
resources: [pods]
verbs: [get, patch]
- apiGroups: [argoproj.io]
resources: [workflowtaskresults]
verbs: [create, patch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: workflow-executor
namespace: kfp-namespace
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: workflow-executor
subjects:
- kind: ServiceAccount
name: argo-workflow-sa
namespace: kfp-namespace
Cluster-scoped resources (once per cluster)
These are shared across all provider namespaces and are the same resources described in RBAC for providers and Kubeflow completion eventing above:
- The
providers-viewerClusterRoleBindingfor the Event Source Server and Controller service accounts. - A
run-viewerand arunconfiguration-viewerClusterRoleBindingper provider service account. - For the
Kubeflow Pipelinesprovider only, the eventingClusterRoleandClusterRoleBinding.
The providers-viewer-role, run-viewer-role and runconfiguration-viewer-role ClusterRoles are installed by the operator chart; when deploying the operator without Helm you must create these too (see helm/kfp-operator/templates/rbac).