Warning
Pre-alpha. OpenEverest v2 and this provider are under active development. CRD schemas, chart values and defaults change frequently, including in breaking ways, and there is no supported upgrade path between versions yet. Not for production use.
Run TiDB — a distributed, MySQL-compatible SQL database — on Kubernetes through OpenEverest, backed by TiDB Operator v2.
OpenEverest providers translate a single, technology-agnostic Instance custom resource into
the native custom resources of an upstream Kubernetes operator. This repository is the provider
for TiDB: it owns the technology-specific knowledge — components, topologies, versions and
parameters — so that users, the API server, and the UI stay technology-agnostic.
A TiDB cluster is composed of three mandatory components plus optional TiFlash, each reconciled into its own TiDB Operator v2 resource:
| Component | Role | Operator resource |
|---|---|---|
pd |
Placement Driver — metadata, timestamps (TSO), scheduling | PDGroup |
tikv |
Distributed key-value storage engine | TiKVGroup |
tidb |
Stateless SQL layer (MySQL protocol) | TiDBGroup |
tiflash (optional) |
Columnar replicas for analytical queries (HTAP) | TiFlashGroup |
Important
This provider is not standalone. It requires an OpenEverest installation (core CRDs and controller) in the cluster. Installing this chart on its own does nothing. See Install OpenEverest.
flowchart LR
U([User / API / UI]) -->|creates| I["Instance<br/>core.openeverest.io"]
I --> P["provider-tidb<br/>(this repository)"]
P -->|reconciles into| O["Cluster + PDGroup +<br/>TiKVGroup + TiDBGroup<br/>core.pingcap.com"]
O --> W["TiDB Operator v2"]
W --> R[("Pods, Services,<br/>Secrets, PVCs")]
P -->|status, endpoint,<br/>credentials| I
The provider watches Instance resources whose spec.providerRef.name is tidb, generates a
shared Cluster plus one component group per component, and reports cluster health and the MySQL
connection endpoint back onto Instance.status. It never manages pods directly — all lifecycle
work is delegated to the operator.
| provider-tidb | OpenEverest | TiDB Operator | Kubernetes |
|---|---|---|---|
0.1.x |
>= 2.0.0 |
v2.0.1 |
1.30 – 1.36 |
The bundled operator manages TiDB engine versions independently (see Versions).
What you can do to a running instance through the Instance API. Upgrading the provider itself
is covered under Installation.
| Capability | Status | Notes |
|---|---|---|
| Provisioning | ✅ | PD + TiKV + TiDB, optionally TiFlash |
| Horizontal scaling | ✅ | Per-component replicas |
| Vertical scaling (CPU / memory) | ✅ | Per-component resources |
| Custom configuration | ✅ | Inline TOML config per component |
| Scheduling policy | ✅ | Per-component schedulingPolicy (node selector, affinity, tolerations, topology spread, scheduler) |
| Version upgrades | ❌ | Planned — ordered rolling upgrade |
| Monitoring | ❌ | Planned |
| TLS | ❌ | Planned |
Stateful components (PD, TiKV, TiFlash) additionally report:
| Capability | Status | Notes |
|---|---|---|
| Persistent storage | ✅ | Per-component size and storage class |
| Storage expansion | ✅ | Grow storage.size; shrinking is rejected. Needs an expandable StorageClass (see below) |
| Backups (on demand) | ✅ | Full snapshot to S3-compatible storage (BR) |
| Backups (scheduled) | ✅ | Cron schedules per storage with count or time retention (see below) |
| Point-in-time recovery | ❌ | Planned |
| Restore | ✅ | Into an existing Instance, or seed a new one via spec.dataSource |
See ROADMAP.md for the planned work.
Important
Growing spec.components.{pd,tikv,tiflash}.storage.size resizes the existing PVCs in place, which only
works if their StorageClass sets allowVolumeExpansion: true
(kubectl get storageclass <name> -o jsonpath='{.allowVolumeExpansion}'). On a StorageClass
without it — e.g. the k3s/kind local-path default — the TiDB Operator leaves the volumes at their
old size. Volumes can never be shrunk; Validate rejects a smaller size.
Important
TiFlash runs while spec.components.tiflash is present with a replica count other than 0
(the UI defaults to 0). Tables are only copied to TiFlash once you ask for it, e.g.
ALTER TABLE t SET TIFLASH REPLICA 1. Setting the replica count to 0 (or removing the
component) deletes the TiFlash nodes after the operator takes their stores offline. A store
can't go offline while tables still need more TiFlash replicas than the nodes left, so run
ALTER TABLE ... SET TIFLASH REPLICA 0 on those tables before scaling TiFlash in or turning it off.
PD also refuses to remove a store if fewer stores than its max-replicas (3 by default) would be
left, so a cluster with fewer TiKV nodes than that can't turn TiFlash off.
The Instance reports Updating with tiflash (removing) until the nodes are gone.
Note
TiDB Operator v2 has no controller for BackupSchedule, so the provider runs
spec.backup.storages[].schedules itself: each cron slot creates a Backup (with
spec.scheduleName) that runs like an on-demand one. A slot fires only while the Instance is
Ready or Updating and no earlier run of the same schedule is in progress, and is skipped if
it can't start within 10 minutes. Retention is anchored on successful backups: count: N keeps
the N newest successful backups, and time keeps everything inside the window plus the newest
successful backup. Pruned backups follow their own deletionPolicy. An invalid cron expression
sets the BackupConfigured condition to False with reason BackupScheduleInvalid.
The provider chart is published as an OCI artifact:
helm install provider-tidb \
oci://ghcr.io/openeverest/charts/provider-tidb \
--version <chart-version> \
--namespace everest-systemThe chart is self-contained:
- TiDB Operator v2 is bundled as a chart dependency and installed automatically. Disable it
with
--set operator.enabled=falseto run against an externally managed operator. - The TiDB CRDs are installed by a pre-install hook. They are ~15 MB — too large to ship
inside the Helm release — so a
Jobapplies the pinned CRD manifest from the operator's GitHub release before the operator and provider start. This requires cluster egress to that URL; point--set crds.url=...at a mirror for air-gapped clusters, or--set crds.install=falseto manage the CRDs yourself.
Upgrade and uninstall:
helm upgrade provider-tidb oci://ghcr.io/openeverest/charts/provider-tidb
helm uninstall provider-tidb --namespace everest-systemUninstalling the chart does not delete running Instance resources, the TiDB CRDs, or any
data.
Set --set crds.install=false to skip the CRD network pull entirely, then install the CRDs
yourself first (server-side apply is required — the schemas exceed the client-side limit):
# From your own mirror of the pinned manifest:
kubectl apply --server-side -f tidb-operator.crds.yaml
helm install provider-tidb oci://ghcr.io/openeverest/charts/provider-tidb \
--version <chart-version> --namespace everest-system \
--set crds.install=falseAlternatively keep the hook and only repoint it at an internal mirror with
--set crds.url=https://mirror.internal/tidb-operator.crds.yaml.
Verify that the provider registered itself:
kubectl get providers.core.openeverest.io tidbCreate an instance:
apiVersion: core.openeverest.io/v1alpha1
kind: Instance
metadata:
name: my-tidb
spec:
providerRef:
name: tidb
version: "8.5.2"
topology:
type: cluster
components:
pd:
type: pd
replicas: 3
storage:
size: 20Gi
tikv:
type: tikv
replicas: 3
storage:
size: 50Gi
tidb:
type: tidb
replicas: 2spec.version and spec.topology are optional; the provider defaults apply (PD 3, TiKV 3,
TiDB 2, default version bundle). More examples live in examples/.
Watch it come up and read the connection details:
kubectl get instances.core.openeverest.io my-tidb -w
kubectl get instances.core.openeverest.io my-tidb -o jsonpath='{.status.connectionSecretRef.name}'The connection secret holds the MySQL host, port (4000), username (root) and password.
The provider generates a random root password on first bootstrap (via the operator's
bootstrapSQL), stores it in a Secret, and surfaces it here. Connect with any MySQL client:
mysql -h my-tidb-tidb.<namespace> -P 4000 -u root -p"$(kubectl get secret my-tidb-conn -o jsonpath='{.data.password}' | base64 -d)"Note
Each component group expands into one instance resource per replica — e.g. spec.components.tidb.replicas: 2
yields two TiDB resources (kubectl get tidb) and two pods, all belonging to one TiDBGroup
(kubectl get tidbgroup). This mirrors how a Deployment owns its Pods.
The chart ships InstancePresets, so the UI can create a working cluster in one click:
| Preset | PD | TiKV | TiDB | TiFlash | Use it for |
|---|---|---|---|---|---|
tidb-dev |
1 | 1 | 1 | — | A laptop or CI: the smallest cluster that runs (about 4 GiB) |
tidb-standard |
3 | 3 | 2 | — | A highly available transactional database |
tidb-analytics |
3 | 3 | 2 | 2 | Transactions plus analytics on the same data |
tidb-dev has no redundancy and sets PD's max-replicas to 1, so PD stops trying to place
three copies of the data on its single TiKV node. To try TiFlash on it, add a tiflash
component with at least 4 GiB of memory; TiFlash is killed for running out of memory at 3 GiB.
Presets pin the default version bundle; a unit test fails if they fall behind it. Sizes and the
list itself
live under presets: in values.yaml; set enabled: false
to hide one.
| Topology | Default | Description |
|---|---|---|
cluster |
✅ | Standard distributed TiDB: PD + TiKV + TiDB, optionally TiFlash |
| Version bundle | Default | Components |
|---|---|---|
8.5.2 |
✅ | PD / TiKV / TiDB / TiFlash v8.5.2 |
7.5.5 |
PD / TiKV / TiDB / TiFlash v7.5.5 |
Source of truth: definition/versions.yaml. Each bundle pins every
component to the same TiDB release; the user selects one via Instance.spec.version.
- Chart values: charts/provider-tidb/values.yaml — provider
deployment settings and the bundled
operatorsubchart values. - Instance parameters: per-component
config(inline TOML) and per-topology parameters, defined under definition/ and published on theProviderresource (kubectl get provider tidb -o yaml). The API server and the UI validate user input against these schemas.
Requires Go (see go.mod), Docker, Helm, kubectl, and a Kubernetes cluster you can
reach. dev/README.md covers the environment end to end: the recommended
local k3d setup, running against a cluster you already have, and every dev/.env setting.
make dev-up # local cluster + Tilt dev environment (see dev/README.md)
make generate # RBAC, provider spec, Helm chart sync
make run # run the provider locally against the cluster
make test-unit
make test-integration # chainsaw suites under test/integration/
make dev-downmake help lists every target. make verify fails when generated files are stale — run
make generate and commit the result.
The provider contract (Validate / Sync / Status / Cleanup), RBAC markers, watches,
code generation, and the backup/restore interfaces are documented once for all providers in
PROVIDER_DEVELOPMENT.md.
| Path | Purpose |
|---|---|
cmd/provider/ |
Entry point |
internal/provider/ |
ProviderInterface implementation (Sync/Validate/Status/Cleanup), RBAC markers |
internal/common/ |
Component name constants |
definition/ |
Provider identity, component types, versions, topologies |
charts/provider-tidb/ |
Helm chart — bundles the operator subchart and TiDB crds/; generated/ is produced by make generate |
config/rbac/role.yaml |
Generated ClusterRole — do not edit |
test/integration/ |
Chainsaw suites (see its README.md) |
test/vars.sh |
Pinned operator and engine versions used by tests |
examples/ |
Example Instance resources |
dev/ |
Tilt dev environment, .env configuration, k3d cluster config |
.github/workflows/ |
CI: lint, build, unit and integration tests, release |
- Unit tests —
make test-unit(mapping helpers + the provider-runtime conformance suite, which asserts every UI-schema field is reconciled bySync). - Integration tests — chainsaw suites under
test/integration/. Thecore/suite exercises the full lifecycle: create anInstance, assert the provider produces theClusterand component groups, simulate operator readiness, assert theInstancereportsReady, and delete. The operator itself is disabled for these tests (readiness is simulated). See test/integration/README.md. - CI —
.github/workflows/ci.yamlruns lint, build, unit tests, generated-file verification, Helm lint, and each integration suite on every pull request.
kubectl logs -n everest-system deploy/provider-tidb -f| Symptom | Where to look |
|---|---|
Instance stuck in Provisioning |
kubectl describe instances.core.openeverest.io <name> conditions, then the provider logs |
No Provider resource in the cluster |
Is the chart installed? Check the provider deployment logs |
Instance ignored entirely |
spec.providerRef.name must be tidb |
Provider logs no matches for kind "TiKVGroup" |
TiDB CRDs missing. The chart's pre-install hook installs them; check the <release>-crd-installer Job (and its egress to crds.url), or apply them manually: kubectl apply --server-side -f <crds.url> |
Operator pod CrashLoopBackOff at startup |
The TiDB CRDs must exist before the operator starts (see the row above) |
| Groups created but no pods | Inspect the group/instance status (kubectl get tidbgroup,tikvgroup,pdgroup) — the failure is upstream in the operator |
| Storage size raised but PVCs unchanged | The StorageClass must allow volume expansion; check kubectl get pvc and the operator logs |
Issues and pull requests are welcome. See PROVIDER_DEVELOPMENT.md and the OpenEverest Code of Conduct.
Report vulnerabilities per the OpenEverest security policy. Please do not open public issues for security reports.
Apache License 2.0 — see LICENSE for details.