diff --git a/Dockerfile b/Dockerfile
index cf119fac..92eae0e0 100755
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,21 +1,58 @@
-FROM registry.access.redhat.com/ubi9/ubi-minimal:9.2-750.1697534106
+# OpenAPI generation stage
+FROM openapitools/openapi-generator-cli:v7.16.0 AS openapi-gen
 
-RUN \
-    microdnf install -y \
-    util-linux \
-    && \
-    microdnf clean all
+WORKDIR /local
 
-COPY \
-    hyperfleet-api \
-    /usr/local/bin/
+# Copy OpenAPI spec
+COPY openapi/openapi.yaml /local/openapi/openapi.yaml
+
+# Generate Go client/models from OpenAPI spec
+RUN bash /usr/local/bin/docker-entrypoint.sh generate \
+    -i /local/openapi/openapi.yaml \
+    -g go \
+    -o /local/pkg/api/openapi && \
+    rm -f /local/pkg/api/openapi/go.mod /local/pkg/api/openapi/go.sum && \
+    rm -rf /local/pkg/api/openapi/test
+
+# Build stage
+FROM golang:1.24-alpine AS builder
+
+WORKDIR /build
+
+# Copy go mod files
+COPY go.mod go.sum ./
+RUN go mod download
+
+# Copy source code
+COPY . .
+
+# Copy generated OpenAPI code from openapi-gen stage
+COPY --from=openapi-gen /local/pkg/api/openapi ./pkg/api/openapi
+
+# Build binary
+RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o hyperfleet-api ./cmd/hyperfleet-api
+
+# Runtime stage
+FROM gcr.io/distroless/static-debian12:nonroot
+
+WORKDIR /app
+
+# Copy binary from builder
+COPY --from=builder /build/hyperfleet-api /app/hyperfleet-api
+
+# Copy OpenAPI schema for validation (uses the source spec, not the generated one)
+COPY --from=builder /build/openapi/openapi.yaml /app/openapi/openapi.yaml
+
+# Set default schema path (can be overridden by Helm for provider-specific schemas)
+ENV OPENAPI_SCHEMA_PATH=/app/openapi/openapi.yaml
 
 EXPOSE 8000
 
-ENTRYPOINT ["/usr/local/bin/hyperfleet-api", "serve"]
+ENTRYPOINT ["/app/hyperfleet-api"]
+CMD ["serve"]
 
 LABEL name="hyperfleet-api" \
       vendor="Red Hat" \
       version="0.0.1" \
-      summary="HyperFleet API" \
-      description="HyperFleet API"
+      summary="HyperFleet API - Cluster Lifecycle Management Service" \
+      description="HyperFleet API for cluster lifecycle management"
diff --git a/Makefile b/Makefile
index a0695bec..bc281ee8 100755
--- a/Makefile
+++ b/Makefile
@@ -15,6 +15,16 @@ version:=$(shell date +%s)
 # a tool for managing containers and images, etc. You can set it as docker
 container_tool ?= podman
 
+# Image configuration
+IMAGE_REGISTRY ?= quay.io/openshift-hyperfleet
+IMAGE_NAME ?= hyperfleet-api
+IMAGE_TAG ?= latest
+
+# Dev image configuration - set QUAY_USER to push to personal registry
+# Usage: QUAY_USER=myuser make image-dev
+QUAY_USER ?=
+DEV_TAG ?= dev-$(git_sha)
+
 # Database connection details
 db_name:=hyperfleet
 db_host=hyperfleet-db.$(namespace)
@@ -53,6 +63,9 @@ help:
 	@echo "make generate-mocks       generate mock implementations for services"
 	@echo "make generate-all         generate all code (openapi + mocks)"
 	@echo "make clean                delete temporary generated files"
+	@echo "make image                build container image"
+	@echo "make image-push           build and push container image"
+	@echo "make image-dev            build and push to personal Quay registry"
 	@echo "$(fake)"
 .PHONY: help
 
@@ -283,3 +296,35 @@ db/login:
 db/teardown:
 	$(container_tool) stop psql-hyperfleet
 	$(container_tool) rm psql-hyperfleet
+
+# Build container image (multi-stage build, no local binary needed)
+.PHONY: image
+image:
+	@echo "Building container image $(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)..."
+	$(container_tool) build -t $(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG) .
+	@echo "✅ Image built: $(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)"
+
+# Build and push container image to registry
+.PHONY: image-push
+image-push: image
+	@echo "Pushing image $(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)..."
+	$(container_tool) push $(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)
+	@echo "✅ Image pushed: $(IMAGE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)"
+
+# Build and push to personal Quay registry (requires QUAY_USER)
+.PHONY: image-dev
+image-dev:
+ifndef QUAY_USER
+	@echo "❌ ERROR: QUAY_USER is not set"
+	@echo ""
+	@echo "Usage: QUAY_USER=myuser make image-dev"
+	@echo ""
+	@echo "This will build and push to: quay.io/$$QUAY_USER/$(IMAGE_NAME):$(DEV_TAG)"
+	@exit 1
+endif
+	@echo "Building dev image quay.io/$(QUAY_USER)/$(IMAGE_NAME):$(DEV_TAG)..."
+	$(container_tool) build -t quay.io/$(QUAY_USER)/$(IMAGE_NAME):$(DEV_TAG) .
+	@echo "Pushing dev image..."
+	$(container_tool) push quay.io/$(QUAY_USER)/$(IMAGE_NAME):$(DEV_TAG)
+	@echo ""
+	@echo "✅ Dev image pushed: quay.io/$(QUAY_USER)/$(IMAGE_NAME):$(DEV_TAG)"
diff --git a/README.md b/README.md
index 3ad1d9ce..267b0733 100755
--- a/README.md
+++ b/README.md
@@ -464,6 +464,90 @@ make db/teardown   # Remove PostgreSQL container
 make db/login      # Connect to database shell
 ```
 
+## Container Image
+
+Build and push container images using the multi-stage Dockerfile:
+
+```bash
+# Build container image
+make image
+
+# Build with custom tag
+make image IMAGE_TAG=v1.0.0
+
+# Build and push to default registry
+make image-push
+
+# Build and push to personal Quay registry (for development)
+QUAY_USER=myuser make image-dev
+```
+
+Default image: `quay.io/openshift-hyperfleet/hyperfleet-api:latest`
+
+## Kubernetes Deployment
+
+### Using Helm Chart
+
+The project includes a Helm chart for Kubernetes deployment with configurable PostgreSQL support.
+
+**Development deployment (with built-in PostgreSQL):**
+```bash
+helm install hyperfleet-api ./charts/ \
+  --namespace hyperfleet-system \
+  --create-namespace
+```
+
+**Production deployment (with external database like GCP Cloud SQL):**
+```bash
+# First, create a secret with database credentials
+kubectl create secret generic hyperfleet-db-external \
+  --namespace hyperfleet-system \
+  --from-literal=db.host=<your-cloudsql-ip> \
+  --from-literal=db.port=5432 \
+  --from-literal=db.name=hyperfleet \
+  --from-literal=db.user=hyperfleet \
+  --from-literal=db.password=<your-password>
+
+# Deploy with external database
+helm install hyperfleet-api ./charts/ \
+  --namespace hyperfleet-system \
+  --set database.postgresql.enabled=false \
+  --set database.external.enabled=true \
+  --set database.external.secretName=hyperfleet-db-external
+```
+
+**Custom image deployment:**
+```bash
+helm install hyperfleet-api ./charts/ \
+  --namespace hyperfleet-system \
+  --set image.registry=quay.io/myuser \
+  --set image.repository=hyperfleet-api \
+  --set image.tag=v1.0.0
+```
+
+**Upgrade deployment:**
+```bash
+helm upgrade hyperfleet-api ./charts/ --namespace hyperfleet-system
+```
+
+**Uninstall:**
+```bash
+helm uninstall hyperfleet-api --namespace hyperfleet-system
+```
+
+### Helm Values
+
+| Parameter | Description | Default |
+|-----------|-------------|---------|
+| `image.registry` | Container registry | `quay.io/openshift-hyperfleet` |
+| `image.repository` | Image repository | `hyperfleet-api` |
+| `image.tag` | Image tag | `latest` |
+| `database.postgresql.enabled` | Deploy built-in PostgreSQL | `true` |
+| `database.external.enabled` | Use external database | `false` |
+| `database.external.secretName` | Secret with db credentials | `""` |
+| `auth.enableJwt` | Enable JWT authentication | `true` |
+| `auth.enableAuthz` | Enable authorization | `true` |
+
 ## API Authentication
 
 **Development mode (no auth):**
diff --git a/charts/.helmignore b/charts/.helmignore
new file mode 100644
index 00000000..f3586bfd
--- /dev/null
+++ b/charts/.helmignore
@@ -0,0 +1,26 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
+# Parent directory patterns
+../.git/
+../../.git/
diff --git a/charts/Chart.yaml b/charts/Chart.yaml
new file mode 100644
index 00000000..fd9cfe32
--- /dev/null
+++ b/charts/Chart.yaml
@@ -0,0 +1,15 @@
+apiVersion: v2
+name: hyperfleet-api
+description: HyperFleet API - Cluster Lifecycle Management Service
+type: application
+version: 1.0.0
+appVersion: "1.0.0"
+maintainers:
+  - name: HyperFleet Team
+    email: hyperfleet-team@redhat.com
+keywords:
+  - hyperfleet
+  - api
+  - kubernetes
+  - cluster-management
+home: https://github.com/openshift-hyperfleet/hyperfleet-api
diff --git a/charts/templates/_helpers.tpl b/charts/templates/_helpers.tpl
new file mode 100644
index 00000000..af44a49a
--- /dev/null
+++ b/charts/templates/_helpers.tpl
@@ -0,0 +1,63 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "hyperfleet-api.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "hyperfleet-api.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "hyperfleet-api.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "hyperfleet-api.labels" -}}
+helm.sh/chart: {{ include "hyperfleet-api.chart" . }}
+{{ include "hyperfleet-api.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "hyperfleet-api.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "hyperfleet-api.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+app.kubernetes.io/component: api
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "hyperfleet-api.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "hyperfleet-api.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/charts/templates/deployment.yaml b/charts/templates/deployment.yaml
new file mode 100644
index 00000000..7ea0b7c5
--- /dev/null
+++ b/charts/templates/deployment.yaml
@@ -0,0 +1,138 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ include "hyperfleet-api.fullname" . }}
+  labels:
+    {{- include "hyperfleet-api.labels" . | nindent 4 }}
+spec:
+  {{- if not .Values.autoscaling.enabled }}
+  replicas: {{ .Values.replicaCount }}
+  {{- end }}
+  selector:
+    matchLabels:
+      {{- include "hyperfleet-api.selectorLabels" . | nindent 6 }}
+  template:
+    metadata:
+      {{- with .Values.podAnnotations }}
+      annotations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      labels:
+        {{- include "hyperfleet-api.selectorLabels" . | nindent 8 }}
+    spec:
+      {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      serviceAccountName: {{ include "hyperfleet-api.serviceAccountName" . }}
+      securityContext:
+        {{- toYaml .Values.podSecurityContext | nindent 8 }}
+      {{- if or .Values.database.postgresql.enabled .Values.database.external.enabled }}
+      initContainers:
+      {{- if .Values.database.postgresql.enabled }}
+      - name: wait-for-db
+        image: busybox:1.36
+        command: ['sh', '-c', 'until nc -z {{ include "hyperfleet-api.fullname" . }}-postgresql {{ .Values.database.postgresql.port }}; do echo waiting for postgresql; sleep 2; done']
+      {{- end }}
+      - name: db-migrate
+        image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+        imagePullPolicy: {{ .Values.image.pullPolicy }}
+        command: ["/app/hyperfleet-api", "migrate"]
+        volumeMounts:
+        - name: secrets
+          mountPath: /build/secrets
+          readOnly: true
+      {{- end }}
+      containers:
+      - name: {{ .Chart.Name }}
+        securityContext:
+          {{- toYaml .Values.securityContext | nindent 10 }}
+        image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+        imagePullPolicy: {{ .Values.image.pullPolicy }}
+        workingDir: /app
+        args:
+        - serve
+        - --api-server-bindaddress={{ .Values.server.bindAddress | default ":8000" }}
+        - --health-check-server-bindaddress={{ .Values.server.healthBindAddress | default ":8083" }}
+        - --metrics-server-bindaddress={{ .Values.server.metricsBindAddress | default ":8080" }}
+        ports:
+        - name: http
+          containerPort: 8000
+          protocol: TCP
+        - name: health
+          containerPort: 8083
+          protocol: TCP
+        - name: metrics
+          containerPort: 8080
+          protocol: TCP
+        env:
+        {{- if .Values.auth.jwksUrl }}
+        - name: JWKS_URL
+          value: {{ .Values.auth.jwksUrl | quote }}
+        {{- end }}
+        - name: ENABLE_JWT
+          value: {{ .Values.auth.enableJwt | quote }}
+        - name: ENABLE_AUTHZ
+          value: {{ .Values.auth.enableAuthz | quote }}
+        {{- if .Values.env }}
+        {{- range .Values.env }}
+        - name: {{ .name }}
+          value: {{ .value | quote }}
+        {{- end }}
+        {{- end }}
+        livenessProbe:
+          httpGet:
+            path: /healthcheck
+            port: health
+          initialDelaySeconds: 30
+          periodSeconds: 10
+          timeoutSeconds: 5
+          failureThreshold: 3
+        readinessProbe:
+          httpGet:
+            path: /healthcheck
+            port: health
+          initialDelaySeconds: 5
+          periodSeconds: 5
+          timeoutSeconds: 3
+          failureThreshold: 3
+        resources:
+          {{- toYaml .Values.resources | nindent 10 }}
+        volumeMounts:
+        - name: tmp
+          mountPath: /tmp
+        {{- if or .Values.database.external.enabled .Values.database.postgresql.enabled }}
+        - name: secrets
+          mountPath: /build/secrets
+          readOnly: true
+        {{- end }}
+        {{- if .Values.extraVolumeMounts }}
+        {{- toYaml .Values.extraVolumeMounts | nindent 8 }}
+        {{- end }}
+      volumes:
+      - name: tmp
+        emptyDir: {}
+      {{- if or .Values.database.external.enabled .Values.database.postgresql.enabled }}
+      - name: secrets
+        secret:
+          {{- if .Values.database.external.enabled }}
+          secretName: {{ .Values.database.external.secretName }}
+          {{- else }}
+          secretName: {{ include "hyperfleet-api.fullname" . }}-db-secrets
+          {{- end }}
+      {{- end }}
+      {{- if .Values.extraVolumes }}
+      {{- toYaml .Values.extraVolumes | nindent 6 }}
+      {{- end }}
+      {{- with .Values.nodeSelector }}
+      nodeSelector:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.affinity }}
+      affinity:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.tolerations }}
+      tolerations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
diff --git a/charts/templates/hpa.yaml b/charts/templates/hpa.yaml
new file mode 100644
index 00000000..cb738bb2
--- /dev/null
+++ b/charts/templates/hpa.yaml
@@ -0,0 +1,32 @@
+{{- if .Values.autoscaling.enabled }}
+apiVersion: autoscaling/v2
+kind: HorizontalPodAutoscaler
+metadata:
+  name: {{ include "hyperfleet-api.fullname" . }}
+  labels:
+    {{- include "hyperfleet-api.labels" . | nindent 4 }}
+spec:
+  scaleTargetRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    name: {{ include "hyperfleet-api.fullname" . }}
+  minReplicas: {{ .Values.autoscaling.minReplicas }}
+  maxReplicas: {{ .Values.autoscaling.maxReplicas }}
+  metrics:
+    {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
+    - type: Resource
+      resource:
+        name: cpu
+        target:
+          type: Utilization
+          averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
+    {{- end }}
+    {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
+    - type: Resource
+      resource:
+        name: memory
+        target:
+          type: Utilization
+          averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
+    {{- end }}
+{{- end }}
diff --git a/charts/templates/postgresql.yaml b/charts/templates/postgresql.yaml
new file mode 100644
index 00000000..145a8a64
--- /dev/null
+++ b/charts/templates/postgresql.yaml
@@ -0,0 +1,120 @@
+{{- if .Values.database.postgresql.enabled }}
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ include "hyperfleet-api.fullname" . }}-db-secrets
+  labels:
+    {{- include "hyperfleet-api.labels" . | nindent 4 }}
+type: Opaque
+stringData:
+  db.host: {{ include "hyperfleet-api.fullname" . }}-postgresql
+  db.port: {{ .Values.database.postgresql.port | quote }}
+  db.name: {{ .Values.database.postgresql.database }}
+  db.user: {{ .Values.database.postgresql.user }}
+  db.password: {{ .Values.database.postgresql.password }}
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "hyperfleet-api.fullname" . }}-postgresql
+  labels:
+    {{- include "hyperfleet-api.labels" . | nindent 4 }}
+    app.kubernetes.io/component: postgresql
+spec:
+  type: ClusterIP
+  ports:
+  - port: {{ .Values.database.postgresql.port }}
+    targetPort: postgresql
+    protocol: TCP
+    name: postgresql
+  selector:
+    {{- include "hyperfleet-api.selectorLabels" . | nindent 4 }}
+    app.kubernetes.io/component: postgresql
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ include "hyperfleet-api.fullname" . }}-postgresql
+  labels:
+    {{- include "hyperfleet-api.labels" . | nindent 4 }}
+    app.kubernetes.io/component: postgresql
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      {{- include "hyperfleet-api.selectorLabels" . | nindent 6 }}
+      app.kubernetes.io/component: postgresql
+  template:
+    metadata:
+      labels:
+        {{- include "hyperfleet-api.selectorLabels" . | nindent 8 }}
+        app.kubernetes.io/component: postgresql
+    spec:
+      containers:
+      - name: postgresql
+        image: {{ .Values.database.postgresql.image }}
+        ports:
+        - name: postgresql
+          containerPort: {{ .Values.database.postgresql.port }}
+          protocol: TCP
+        env:
+        - name: POSTGRES_DB
+          value: {{ .Values.database.postgresql.database }}
+        - name: POSTGRES_USER
+          value: {{ .Values.database.postgresql.user }}
+        - name: POSTGRES_PASSWORD
+          value: {{ .Values.database.postgresql.password }}
+        resources:
+          {{- toYaml .Values.database.postgresql.resources | nindent 10 }}
+        {{- if .Values.database.postgresql.persistence.enabled }}
+        volumeMounts:
+        - name: data
+          mountPath: /var/lib/postgresql/data
+        {{- end }}
+        readinessProbe:
+          exec:
+            command:
+            - pg_isready
+            - -U
+            - {{ .Values.database.postgresql.user }}
+            - -d
+            - {{ .Values.database.postgresql.database }}
+          initialDelaySeconds: 5
+          periodSeconds: 5
+        livenessProbe:
+          exec:
+            command:
+            - pg_isready
+            - -U
+            - {{ .Values.database.postgresql.user }}
+            - -d
+            - {{ .Values.database.postgresql.database }}
+          initialDelaySeconds: 30
+          periodSeconds: 10
+      {{- if .Values.database.postgresql.persistence.enabled }}
+      volumes:
+      - name: data
+        persistentVolumeClaim:
+          claimName: {{ include "hyperfleet-api.fullname" . }}-postgresql
+      {{- end }}
+---
+{{- if .Values.database.postgresql.persistence.enabled }}
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+  name: {{ include "hyperfleet-api.fullname" . }}-postgresql
+  labels:
+    {{- include "hyperfleet-api.labels" . | nindent 4 }}
+    app.kubernetes.io/component: postgresql
+spec:
+  accessModes:
+  - ReadWriteOnce
+  resources:
+    requests:
+      storage: {{ .Values.database.postgresql.persistence.size }}
+  {{- if .Values.database.postgresql.persistence.storageClass }}
+  storageClassName: {{ .Values.database.postgresql.persistence.storageClass }}
+  {{- end }}
+{{- end }}
+{{- end }}
diff --git a/charts/templates/service.yaml b/charts/templates/service.yaml
new file mode 100644
index 00000000..6eb36282
--- /dev/null
+++ b/charts/templates/service.yaml
@@ -0,0 +1,23 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "hyperfleet-api.name" . }}
+  labels:
+    {{- include "hyperfleet-api.labels" . | nindent 4 }}
+spec:
+  type: {{ .Values.service.type }}
+  ports:
+  - port: {{ .Values.service.port }}
+    targetPort: http
+    protocol: TCP
+    name: http
+  - port: 8083
+    targetPort: health
+    protocol: TCP
+    name: health
+  - port: 8080
+    targetPort: metrics
+    protocol: TCP
+    name: metrics
+  selector:
+    {{- include "hyperfleet-api.selectorLabels" . | nindent 4 }}
diff --git a/charts/templates/serviceaccount.yaml b/charts/templates/serviceaccount.yaml
new file mode 100644
index 00000000..bead449b
--- /dev/null
+++ b/charts/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ include "hyperfleet-api.serviceAccountName" . }}
+  labels:
+    {{- include "hyperfleet-api.labels" . | nindent 4 }}
+  {{- with .Values.serviceAccount.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+{{- end }}
diff --git a/charts/values.yaml b/charts/values.yaml
new file mode 100644
index 00000000..d5535f89
--- /dev/null
+++ b/charts/values.yaml
@@ -0,0 +1,126 @@
+# Default values for hyperfleet-api
+# This is a YAML-formatted file.
+
+replicaCount: 1
+
+image:
+  registry: quay.io/openshift-hyperfleet
+  repository: hyperfleet-api
+  pullPolicy: Always
+  tag: "latest"
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+# Server bind addresses
+# Use ":PORT" format to bind to all interfaces (required for Kubernetes)
+server:
+  bindAddress: ":8000"
+  healthBindAddress: ":8083"
+  metricsBindAddress: ":8080"
+
+serviceAccount:
+  # Specifies whether a service account should be created
+  create: true
+  # Annotations to add to the service account
+  annotations: {}
+  # The name of the service account to use.
+  # If not set and create is true, a name is generated using the fullname template
+  name: ""
+
+podAnnotations: {}
+
+podSecurityContext:
+  fsGroup: 65532
+  runAsNonRoot: true
+  runAsUser: 65532
+
+securityContext:
+  allowPrivilegeEscalation: false
+  capabilities:
+    drop:
+    - ALL
+  readOnlyRootFilesystem: true
+  seccompProfile:
+    type: RuntimeDefault
+
+service:
+  type: ClusterIP
+  port: 8000
+
+resources:
+  limits:
+    cpu: 500m
+    memory: 512Mi
+  requests:
+    cpu: 100m
+    memory: 128Mi
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+autoscaling:
+  enabled: false
+  minReplicas: 1
+  maxReplicas: 10
+  targetCPUUtilizationPercentage: 80
+  targetMemoryUtilizationPercentage: 80
+
+# Database configuration
+database:
+  # For PRODUCTION: Use external database (GCP Cloud SQL, etc.)
+  # Set external.enabled=true and provide connection details
+  #
+  # For DEVELOPMENT: Use built-in PostgreSQL pod
+  # Set postgresql.enabled=true (default)
+
+  # External database configuration (production)
+  external:
+    enabled: false
+    # Name of existing secret with db.host, db.port, db.name, db.user, db.password keys
+    secretName: ""
+
+  # Built-in PostgreSQL for development/testing
+  postgresql:
+    enabled: true
+    image: docker.io/library/postgres:14.2
+    database: hyperfleet
+    user: hyperfleet
+    password: hyperfleet-dev-password
+    port: 5432
+    resources:
+      limits:
+        cpu: 500m
+        memory: 512Mi
+      requests:
+        cpu: 100m
+        memory: 128Mi
+    persistence:
+      enabled: false
+      size: 1Gi
+      storageClass: ""
+
+# Authentication configuration
+auth:
+  # Enable JWT authentication
+  enableJwt: true
+  # Enable authorization (not required for MVP)
+  enableAuthz: false
+  # JWKS URL for token verification
+  # Override with: --set auth.jwksUrl="https://your-idp/.../.well-known/jwks.json"
+  jwksUrl: "https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/certs"
+
+# Additional environment variables
+env: []
+  # - name: GLOG_V
+  #   value: "10"
+
+# Volume mounts for additional configs
+extraVolumeMounts: []
+
+# Additional volumes
+extraVolumes: []
