# GoModel Helm chart Deploys [GoModel](https://gomodel.enterpilot.io), an OpenAI-compatible AI gateway, on Kubernetes. ## Quick start ```bash helm install gomodel ./helm -n gomodel --create-namespace \ --set secretEnv.GOMODEL_MASTER_KEY=change-me \ --set secretEnv.OPENAI_API_KEY=sk-... kubectl -n gomodel port-forward svc/gomodel 8080:8080 curl -H "Authorization: Bearer change-me" http://127.0.0.1:8080/v1/models ``` The default install runs one pod with SQLite on a 1Gi PersistentVolumeClaim, Prometheus metrics on `/metrics`, a read-only root filesystem, and the image's non-root user (UID 65532). ## Configuration GoModel is configured entirely through environment variables. Any variable from [`.env.template`](../.env.template) goes in one of two maps: | Value | Stored in | Use for | | ----------- | --------- | -------------------------------------------------- | | `env` | ConfigMap | Plain settings: `LOG_LEVEL`, `REDIS_URL`, `BASE_PATH`, … | | `secretEnv` | Secret | `GOMODEL_MASTER_KEY`, provider API keys, database URLs | Changing either rolls the pods. To keep secrets out of Helm values, create the Secret yourself and reference it: ```yaml extraEnvFrom: - secretRef: name: gomodel-keys # keys: GOMODEL_MASTER_KEY, OPENAI_API_KEY, ... ``` Set `GOMODEL_MASTER_KEY`. Without it the gateway accepts unauthenticated requests and the install notes print a warning. ### config.yaml Use `config` for settings that env vars cannot express (per-provider resilience, custom provider names, model allowlists). It is mounted at `/app/config/config.yaml`; env vars still override it. ```yaml config: | providers: openai-eu: type: openai api_key: ${OPENAI_EU_API_KEY} base_url: https://eu.api.openai.com/v1 ``` Or point `existingConfigMap` at a ConfigMap that has a `config.yaml` key. ### Storage and replicas | `storage.type` | Replicas | Data location | | -------------- | -------- | ---------------------------------------------- | | `sqlite` | 1 | PVC at `/app/data` (`persistence.enabled: true`) | | `postgresql` | any | `storage.url` or `POSTGRES_URL` | | `mongodb` | any | `storage.url` or `MONGODB_URL` | SQLite is per pod, so the chart refuses `replicaCount > 1` or autoscaling until you switch to PostgreSQL or MongoDB: ```yaml replicaCount: 3 storage: type: postgresql url: postgres://gomodel:secret@postgresql:5432/gomodel ``` `storage.url` lands in the chart Secret. If the URL already lives in a Secret of your own, load it through `extraEnvFrom` or an `extraEnv` entry named `POSTGRES_URL` / `MONGODB_URL` instead and leave `storage.url` empty. `helm uninstall` deletes the claim with the release. To keep the database, add `persistence.annotations: {helm.sh/resource-policy: keep}`. With SQLite on a ReadWriteOnce volume the Deployment uses the `Recreate` strategy, so upgrades briefly stop the gateway. Set `persistence.enabled: false` for a throwaway install; the data then lives on an emptyDir and is lost when the pod is replaced. ### Exposing the gateway Ingress: ```yaml ingress: enabled: true className: nginx annotations: cert-manager.io/cluster-issuer: letsencrypt hosts: - host: gomodel.example.com paths: - path: / pathType: Prefix tls: - secretName: gomodel-tls hosts: [gomodel.example.com] ``` Gateway API: ```yaml httpRoute: enabled: true parentRefs: - name: public-gateway namespace: gateway-system hostnames: [gomodel.example.com] ``` To serve under a path prefix set `env.BASE_PATH: /g`; probe, metrics and HTTPRoute paths follow automatically. For pod-to-pod HTTPS without an ingress, mount a certificate Secret through `extraVolumes`/`extraVolumeMounts` and set `env.SERVER_TLS_CERT_FILE` and `env.SERVER_TLS_KEY_FILE`; both probes (unless a probe sets its own `httpGet.scheme`), `helm test` and the port-forward install notes switch to HTTPS on their own. The ServiceMonitor scrapes plain HTTP, so define your own with `scheme: https`. With mTLS (`env.SERVER_TLS_CLIENT_CA_FILE`) `/metrics` needs a client certificate too, so give it a `tlsConfig` with `ca` for the gateway's CA plus `cert` and `keySecret`. See [Native TLS](https://gomodel.enterpilot.io/guides/production#native-tls) for a cert-manager example. ### Metrics `metrics.enabled` (default `true`) exposes `/metrics` without authentication. With the Prometheus Operator, set `metrics.serviceMonitor.enabled: true` and add the labels your Prometheus selects on under `metrics.serviceMonitor.labels`. ### Cloud provider credentials Give the pod's ServiceAccount a cloud identity for AWS Bedrock or Google Vertex AI: ```yaml serviceAccount: annotations: eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/gomodel ``` ## Values | Key | Default | Description | | --- | --- | --- | | `replicaCount` | `1` | Pods. More than one needs `storage.type` postgresql or mongodb. | | `image.repository` | `enterpilot/gomodel` | | | `image.tag` | `""` | Defaults to the chart `appVersion`. | | `env` | `{}` | Plain environment variables. | | `secretEnv` | `{}` | Secret environment variables. | | `extraEnvFrom` | `[]` | Existing Secrets/ConfigMaps loaded as env vars. | | `extraEnv` | `[]` | Raw container env entries (`valueFrom`). | | `config` | `""` | Inline `config.yaml`. | | `existingConfigMap` | `""` | ConfigMap with a `config.yaml` key. | | `storage.type` | `sqlite` | `sqlite`, `postgresql`, or `mongodb`. | | `storage.url` | `""` | Connection URL for postgresql/mongodb. | | `persistence.enabled` | `true` | PVC for SQLite data. | | `persistence.size` | `1Gi` | | | `persistence.storageClass` | `""` | Cluster default. `-` disables dynamic provisioning. | | `persistence.existingClaim` | `""` | Reuse a PVC. | | `updateStrategy` | `{}` | Overrides the automatic Recreate/RollingUpdate choice. | | `metrics.enabled` | `true` | Expose `/metrics`. | | `metrics.serviceMonitor.enabled` | `false` | Prometheus Operator ServiceMonitor. | | `service.type` | `ClusterIP` | | | `service.port` | `8080` | | | `ingress.enabled` | `false` | | | `httpRoute.enabled` | `false` | Gateway API HTTPRoute; needs `httpRoute.parentRefs`. | | `serviceAccount.create` | `true` | | | `serviceAccount.annotations` | `{}` | Cloud IAM bindings. | | `resources` | `100m/128Mi` requests, `512Mi` limit | No CPU limit by default. | | `autoscaling.enabled` | `false` | HPA on CPU (70%) and optionally memory. | | `podDisruptionBudget.enabled` | `true` | Created only when more than one pod can run. | | `terminationGracePeriodSeconds` | `45` | Covers GoModel's 30s drain. | | `livenessProbe` / `readinessProbe` | `/health`, `/health/ready` | Readiness fails while storage is down. | | `podSecurityContext` / `securityContext` | non-root 65532, read-only FS | | | `extraVolumes` / `extraVolumeMounts` | `[]` | CA bundles, local model catalogs, … | | `nodeSelector`, `tolerations`, `affinity`, `topologySpreadConstraints`, `priorityClassName`, `podAnnotations`, `podLabels` | | Standard scheduling knobs. | ## Upgrading from chart 0.1.x Chart 0.2.0 replaced the per-provider values and the Redis subchart with the generic `env` / `secretEnv` maps: | 0.1.x | 0.2.0 | | --- | --- | | `auth.masterKey` | `secretEnv.GOMODEL_MASTER_KEY` | | `providers.openai.apiKey` | `secretEnv.OPENAI_API_KEY` (same pattern for every provider) | | `providers.openai.baseUrl` | `env.OPENAI_BASE_URL` | | `providers.existingSecret` | `extraEnvFrom: [{secretRef: {name: ...}}]` | | `redis.enabled` / `cache.redis.url` | `env.REDIS_URL` pointing at your own Redis | | `gateway.*` | `httpRoute.*` | | `server.basePath` | `env.BASE_PATH` | The chart refuses to render while any of the old top-level keys (`auth`, `providers`, `redis`, `cache`, `gateway`, `server`, `logging`) are still present, so an upgrade cannot silently drop the master key. The default `replicaCount` dropped from 2 to 1 because SQLite is per pod, the pod now runs as the image's UID 65532, and `/app/data` is on a PVC. ## Testing the chart ```bash helm lint --strict ./helm -f helm/ci/default-values.yaml helm template gomodel ./helm -f helm/ci/full-values.yaml | kubeconform -strict helm install gomodel ./helm -n gomodel --create-namespace -f helm/ci/default-values.yaml --wait helm test gomodel -n gomodel ``` The `ci/*-values.yaml` profiles are what the CI workflow lints, validates, and installs on a kind cluster.