Deploy
Deploy and operate Feature Form on Kubernetes.
| Redis Feature Form |
|---|
Install Redis Feature Form on Kubernetes with the Helm chart, durable PostgreSQL state, OpenID Connect (OIDC) authentication, and a license key.
Install
Prerequisites: For production environments, use an external PostgreSQL database and keep credentials and the license key in Kubernetes Secrets.
To get started:
- Get the chart.
- Create the namespace and Secrets.
- Create a values file.
- Render and install the release.
- Verify the deployment.
- Install and connect the
ffcommand-line interface (CLI).
Prerequisites
- Kubernetes 1.27+.
- Helm 3.14+.
- Network access to the chart and image repositories. Configure
imagePullSecretsif your cluster requires registry credentials. - An OIDC issuer URL, an API audience, and a CLI client configured for device authorization. See Register a CLI client.
- An external PostgreSQL database for production state. Its role must be able to create and alter Feature Form tables during migrations.
- A Feature Form license key from your Redis account team.
- A public domain name and Transport Layer Security (TLS) certificate for each externally exposed endpoint.
1. Get the chart
The Feature Form Helm chart is published as an Open Container Initiative (OCI) artifact on Docker Hub:
oci://registry-1.docker.io/redisfeatureform/featureform
Install the chart directly from this path. Always pin --version to the Feature Form version you intend to run.
2. Create the namespace and Secrets
Create the release namespace before creating referenced Secrets:
kubectl create namespace <namespace>
Use your normal secret-management process to create these Secrets in the same namespace as Feature Form:
featureform-postgres, with aPOSTGRES_URLkey containing the PostgreSQL connection URL. Require TLS according to your database policy.featureform-license, with the license key stored inlicense.key.
The Secret names and local file paths in these examples aren't required. If you choose different Secret names or keys, update the matching postgres.* and license.* settings in the values file. You can also choose a different values filename and pass it to Helm with --values.
For example, create the PostgreSQL Secret from a restricted environment file whose entry is POSTGRES_URL=<postgres-connection-url>:
kubectl --namespace <namespace> create secret generic featureform-postgres \
--from-env-file=<path-to-postgres-secret-env-file>
Create the license Secret from a restricted key file:
kubectl --namespace <namespace> create secret generic featureform-license \
--from-file=license.key=<path-to-license-key-file>
Create an image-pull Secret as well if your cluster requires registry authentication.
Add this entry when you create values-production.yaml:
imagePullSecrets:
- name: <registry-secret-name>
3. Create a values file
Create values-production.yaml and set auth.deploymentID to a stable, lowercase ASCII identifier for this Feature Form environment, such as acme-featureform-prod-us-west-2. Keep the value the same across replicas and upgrades, and use a different value for each environment.
Feature Form accepts any value that isn't empty after trimming whitespace. Changing it requires CLI users to sign in again.
stateBackend: postgres
auth:
enabled: true
oidcIssuerURL: "https://idp.example.com/realms/featureform"
oidcClientID: "featureform-api"
oidcCLIClientID: "featureform-cli"
oidcCLIScopes: "openid profile offline_access"
oidcCLILoginMethods: "device_code"
deploymentID: "<stable-deployment-id>"
publicRestEndpoint: "https://api.example.com"
publicGrpcEndpoint: "grpc.example.com:443"
postgres:
url: ""
secretName: featureform-postgres
secretKey: POSTGRES_URL
license:
existingSecret: featureform-license
secretKey: license.key
rest:
ingress:
enabled: true
className: "<ingress-class-name>"
hosts:
- host: api.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: featureform-api-tls
hosts:
- api.example.com
grpc:
ingress:
enabled: true
className: "<ingress-class-name>"
annotations:
"<grpc-backend-annotation>": "<grpc-backend-value>"
hosts:
- host: grpc.example.com
paths:
- path: /
pathType: ImplementationSpecific
tls:
- secretName: featureform-grpc-tls
hosts:
- grpc.example.com
In values-production.yaml, replace the ingress class and gRPC backend annotation placeholders with the values required by your maintained ingress controller. The controller must support gRPC backends and TLS termination.
auth.publicRestEndpoint and auth.publicGrpcEndpoint show the public endpoints through authentication discovery. Use addresses reachable by your CLI users. If internal services reach the identity provider through a different URL, configure the internal and public OIDC URLs as described in Configure authentication and role-based access control.
4. Render and install the release
Render the chart before changing the cluster:
helm template featureform \
oci://registry-1.docker.io/redisfeatureform/featureform \
--version <featureform-version> \
--namespace <namespace> \
--values values-production.yaml \
> /tmp/featureform-rendered.yaml
Install the release and wait for its workloads to become ready:
helm upgrade --install featureform \
oci://registry-1.docker.io/redisfeatureform/featureform \
--version <featureform-version> \
--namespace <namespace> \
--values values-production.yaml \
--wait \
--timeout 10m
5. Verify the deployment
Confirm the server rollout, services, and PostgreSQL migration init container:
kubectl --namespace <namespace> rollout status \
deployment/featureform-featureform-server
kubectl --namespace <namespace> get pods
kubectl --namespace <namespace> get services
Confirm that the migrate init container completed with exit code 0 for every server pod:
kubectl --namespace <namespace> get pods \
--selector app.kubernetes.io/instance=featureform,app.kubernetes.io/component=server \
--output jsonpath='{range .items[*]}{.metadata.name}{"\t"}{range .status.initContainerStatuses[?(@.name=="migrate")]}{.state.terminated.reason}{"\t"}{.state.terminated.exitCode}{"\n"}{end}{end}'
Each line must end with Completed and 0. If the status fields are empty, wait for the init container to finish and run the command again.
If migration fails, inspect its logs before restarting the pod:
kubectl --namespace <namespace> logs <server-pod> --container migrate
Verify the mounted license. Inspect the printed Status: value rather than relying only on the command's exit status:
kubectl --namespace <namespace> exec \
deployment/featureform-featureform-server \
-- featureformctl license status
Then verify REST readiness through the public endpoint:
curl --fail --silent --show-error https://api.example.com/health/ready
The readiness endpoint checks the server and registered scheduler dependencies. It doesn't prove that every external provider is healthy.
6. Install and connect the ff CLI
The Feature Form CLI ships as the redis-featureform package on PyPI. Don't run pip install featureform — that installs an unrelated upstream project. Install it in a virtual environment and pin it to the deployment version:
python3 -m venv .venv
source .venv/bin/activate
pip install redis-featureform==<featureform-version>
ff version --client-only
When you expose the gRPC endpoint publicly, connect the CLI through that endpoint. gRPC is the CLI default, but the explicit transport makes the saved profile clear:
ff --server grpc.example.com:443 --transport grpc \
auth login --profile production
If you expose only REST, log in through that endpoint instead:
ff --server https://api.example.com --transport rest \
auth login --profile production
If neither endpoint is public, forward the gRPC service from one terminal:
kubectl --namespace <namespace> port-forward \
service/featureform-featureform-grpc 9090:9090
Then log in from another terminal:
ff --server localhost:9090 --transport grpc \
auth login --profile production
After login, verify the authenticated principal and the selected endpoint:
ff auth status
ff auth whoami
ff ping
Login creates or updates the production profile and makes it active for subsequent commands. ff ping returns a failure status when the selected endpoint is unavailable.
Configure production state
Use external PostgreSQL so Feature Form state survives restarts and remains consistent across server replicas. Protect the database with your normal TLS, availability, monitoring, backup, and restore controls. Back up the database before upgrading Feature Form.
This database stores durable Feature Form application state. Register production data providers separately after deploying Feature Form.
Before starting the server, the chart runs an init container that applies all pending schema migrations. The database role must be able to create and alter the Feature Form schema. A migration failure keeps the server pod from becoming ready.
Configure external access
All services default to ClusterIP. Choose one exposure model:
- Use
rest.ingress.*,grpc.ingress.*, anddashboard.ingress.*for separate hosts. - Use
ingress.*for one host with chart-managed paths for the API and dashboard. - Use the service
type=LoadBalancervalues when your platform terminates TLS at a load balancer.
Don't combine ingress.* with service-specific ingress settings. The chart rejects that configuration.
Terminate TLS at the ingress controller or load balancer. Use grpc.ingress.* only with a controller that supports gRPC backends. If ingress isn't suitable, use grpc.service.type=LoadBalancer and rest.service.type=LoadBalancer.
Dashboard requirements
Enabling the dashboard requires:
dashboard.enabled=true.dashboard.publicAPIURL, a REST ingress host, or unifiedingress.*configuration.- A resolvable dashboard authentication URL.
- An OIDC client secret and dashboard session secret. Use
dashboard.auth.existingSecretfor production.
The dashboard Secret keys default to FEATUREFORM_OIDC_CLIENT_SECRET and FEATUREFORM_DASHBOARD_AUTH_SECRET.
Configure availability and capacity
The chart defaults to one server replica. For multiple replicas, use PostgreSQL state. Configure resource requests before enabling horizontal autoscaling. Each server replica also runs scheduler workers, so adding replicas increases both API and job-processing capacity.
This example sets resource boundaries, two or more replicas, and a PodDisruptionBudget:
server:
resources:
requests:
cpu: <server-cpu-request>
memory: <server-memory-request>
limits:
cpu: <server-cpu-limit>
memory: <server-memory-limit>
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: <maximum-server-replicas>
targetCPUUtilizationPercentage: 80
podDisruptionBudget:
enabled: true
minAvailable: 1
Horizontal Pod Autoscaling requires the Kubernetes resource metrics API. Use server.nodeSelector, server.tolerations, server.affinity, or server.topologySpreadConstraints when your cluster requires workload placement controls.
By default, each server process starts two effective scheduler workers. To bound per-replica job concurrency, use scheduler.workerCount and scheduler.workerMaxInflight. Tune these values with server replica count and downstream provider capacity; increasing API replicas also increases the number of workers that can claim jobs.
Configure Kubernetes Secret access
No token is needed to consume Secrets already referenced by pod environment variables or volumes. The chart doesn't mount a Kubernetes service-account token by default.
If you register a Kubernetes secret provider for server-side secret resolution, enable the token mount and grant the Feature Form service account get access to the required Secret objects. For Secrets in the release namespace:
serviceAccount:
automountServiceAccountToken: true
rbac:
create: true
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["<provider-secret-name>"]
verbs: ["get"]
rbac.* values configure Kubernetes permissions. Feature Form application roles are configured separately in Configure authentication and role-based access control.Configure observability
The server exports metrics and traces through OpenTelemetry Protocol (OTLP). Because the Helm chart disables both signals by default, set the endpoint and enable the signals you want. Configure an external collector using an authority without a URL scheme:
observability:
otlpEndpoint: "otel-collector.telemetry.svc.cluster.local:4317"
tracingEnabled: true
metricsEnabled: true
serviceName: featureform
serviceVersion: "<featureform-version>"
environment: production
logLevel: info
traceSampleRate: 0.1
Feature Form writes server logs to standard output and standard error. Collect them with your Kubernetes logging system.
Upgrade and roll back
Before upgrading:
- Back up the Feature Form PostgreSQL database.
- Confirm the license key is valid for the target release.
- Render the target chart version with the current values file.
- Upgrade with an explicit
--version,--wait, and--timeout. - Verify the migration init container, server rollout, license status, REST readiness, and
ff ping.
Keep the server, dashboard, chart, and ff versions aligned. Replacing an external license Secret doesn't restart the server; roll out the server deployment after updating the key.
Helm rollback restores chart-managed resources, but it doesn't reverse PostgreSQL migrations or restore externally managed Secrets. Preserve the database backup, previous license key, image version, and Helm revision until the rollback window closes.
Troubleshoot installation
- Helm reports missing authentication values. Set
auth.enabled=true,auth.oidcIssuerURL,auth.oidcClientID, and a stableauth.deploymentID. - A pod shows
ImagePullBackOff. Confirm registry network access and configureimagePullSecretswhen credentials are required. - The migration init container fails. Check PostgreSQL reachability, TLS settings, credentials, and the database role's schema permissions.
- The server reports a missing or invalid license. Check the Secret name and
license.keyentry, then inspectfeatureformctl license statuswithout printing the key. - OIDC discovery or login fails. Verify the internal and public issuer URLs, client IDs, redirect URIs, and endpoint reachability.
- The dashboard fails chart validation. Supply its public API URL, authentication URL, OIDC client secret, and session secret.
- Ingress values conflict. Configure either unified
ingress.*or service-specific ingress settings, not both.
Next steps
- Configure authentication and role-based access control — set up OIDC and grant roles.
- Manage workspaces — create your first workspace.
- Register providers — connect production data infrastructure.
- Quickstart — verify the install end to end.