# SchemaHero: a Kubernetes operator that turns table schemas into cluster resources

> SchemaHero is a Go operator for declarative database schema management, where a table definition is a Kubernetes resource, a schema edit in git is reconciled into an ALTER TABLE, and the database can live inside the cluster or outside it. This looks at how the operator and its plugin split work, and where the model strains.

**schemahero/schemahero** — A Kubernetes operator for declarative database schema management (gitops for database schemas)

- Repository: https://github.com/schemahero/schemahero
- Website: https://schemahero.io
- Stars: 1,271 · Forks: 102
- Language: Go
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/schemahero-schemahero

## Three goals, and the third one is the interesting one

SchemaHero is a Kubernetes Operator for Declarative Schema Management, and the documentation states its goals as three numbered items.

The first is that database table schemas can be expressed as Kubernetes resources that can be deployed to a cluster. The second is that database schemas can be edited and deployed to the cluster, and that SchemaHero will calculate the required change, an `ALTER TABLE` statement, and apply it. The third is that SchemaHero can manage databases that are deployed to the cluster, or external to the cluster, with RDS and Google CloudSQL given as the examples.

That third goal is what turns this from a curiosity into something a platform team can use. A schema tool that only works against a database pod inside the cluster is of limited use to most organisations, because the database is usually a managed service and the thing you want to manage from the cluster is the schema, not the server.

The mechanism follows from the goals. The table definition is a custom resource, the operator reconciles it, the diff between the desired and the actual schema is turned into an `ALTER TABLE`, and the statement is applied through the database's own client. So the gitops part is literal: the schema lives in a repository, the change goes through a pull request, and applying the resource is what changes the database.

The site is schemahero.io, the licence is Apache-2.0, and the default branch is `main`.

## One operator, many database plugins

The repository layout shows a two-part structure that mirrors how the tool has to work.

There is the operator itself, in `cmd/` and `pkg/`, and there is a `plugins/` directory holding the per-database implementations. The Makefile makes the split explicit with a default `PLUGIN ?= postgres` variable and a dedicated target:

```make
.PHONY: plugins
plugins: ## Build all database plugins
	@echo "Building plugins..."
	$(MAKE) -C plugins all
```

The mechanism underneath is `github.com/hashicorp/go-plugin`, which is the standard Go library for building a plugin system where the host and the plugin are separate processes talking over a protocol. That choice is not incidental. A database driver is usually a C library, and running it in a separate process means a crash in a driver cannot take down the operator, and the plugin can be written in any language with a matching go-plugin implementation. The trade is that every call crosses a process boundary, and the host and plugin versions have to agree.

The `plugins/` directory is therefore the compatibility matrix of the project, and the `examples/` directory shows the databases in practice: `postgresql/`, `timescaledb/`, `ssm/`, `vault/` and `doppler/`. Three of those five are not databases at all, and that is the point. `ssm/` is AWS Systems Manager Parameter Store, `vault/` is HashiCorp Vault, and `doppler/` is Doppler, so the examples cover the two ways a Kubernetes workload normally gets a database password, from a cloud secret store and from a secrets manager, both of them wired into a database plugin.

The `features/` directory and the `config/` directory hold the custom resource definitions and the operator's own configuration, and `deploy/` holds the manifests.

## Installation runs through a kubectl plugin

The recommended way to deploy SchemaHero is to use the kubectl plugin, with the full instructions and the other installation methods listed in the documentation under installing with kubectl.

That is a sensible default for an operator, because a kubectl plugin is a single binary the user runs, and the operator itself is then installed into the cluster. The Makefile builds both pieces, the `all` target listing them together:

```make
all: generate fmt vet manifests bin/kubectl-schemahero manager test
```

So a local build produces the kubectl plugin, the manager binary, the generated manifests and a test run, in that order. The `manifests` step is the controller-generator output for the custom resource definitions, which is what makes the schema resources valid in the cluster.

For getting started, the readme points at the tutorial and the full documentation, both on the site. The tutorial is the place to learn the resource format, since the whole model is in the shape of those custom resources: what a table definition looks like, how columns and constraints are expressed, and how the operator names the thing it reconciles.

There is also a `pkg/` and `cmd/` split in the Go code, and the manifest test target runs `go test ./pkg/... ./cmd/...`, so the operator's own logic is covered by unit tests, with `envtest` as a separate target that runs `./hack/envtest.sh`, which is the standard approach for testing controllers against a real API server rather than a fake one.

## What the dependency list says about scope

The `go.mod` is a good map of the project's ambitions, and it is worth reading in detail rather than skipping.

The Kubernetes side is `sigs.k8s.io/controller-runtime` at 0.24.0 with `k8s.io/api`, `apimachinery`, `client-go`, `apiextensions-apiserver` and `cli-runtime` all at 0.36.0, which is the standard controller stack. The module targets Go 1.26.5.

The database side is thin on purpose, with just `github.com/go-sql-driver/mysql` and `github.com/pkg/errors` in the direct requirements, because the real drivers live in the plugin processes rather than in the operator binary.

The AWS SDK v2 is prominent, with the `ssm` service at 1.68.6, plus credentials, config, `sso`, `ssooidc` and `sts`. The `sso` and `ssooidc` packages are the ones worth noting: they are there so the operator can authenticate to AWS with your identity provider rather than with static keys, which is the difference between a schema tool that works in a cluster and one that does not.

Two more dependencies point at design choices. `oras.land/oras-go/v2` is an OCI registry client, which means the artefacts involved, plausibly the plugin binaries and the schema resources, can be distributed through an OCI registry. And `hashicorp/go-hclog` and `go.uber.org/zap` are two logging systems, one from the HashiCorp ecosystem for the plugin protocol and one general purpose, which is the signature of a host and plugin pair that each log in their own idiom.

The CLI is built with `spf13/cobra` and `spf13/viper`, and the YAML dependency is `gopkg.in/yaml.v2`.

## Where declarative schema management strains

Three limits are clear, and they follow from applying a Kubernetes reconciliation loop to a database.

The first is that a schema change becomes a deployment. The whole point of the model is that SchemaHero calculates the required `ALTER TABLE` and applies it, which is a wonderful property for review and a sharp edge operationally. The same declarative loop that makes the schema auditable also means that a merged pull request changes the production database, without a human running the statement and watching it. Teams that want a schema in git usually also want a migration process with a gate, a backup and a plan; SchemaHero does not describe those, and the documentation excerpt does not document a rollback path either.

The second is the scope of the change. SchemaHero calculates an `ALTER TABLE`, which is a DDL statement. A schema change that also needs to move or reshape the data is a different problem, and a `go-plugin` per database cannot bridge it on its own. The `ALTER TABLE` framing is honest about what it covers and should be read as a boundary.

The third is the operational requirement. This is a Kubernetes operator. A team without a cluster, or a team whose database is managed from a different tool entirely, has to run a Kubernetes control plane to use it, which is a large price for a schema tool.

And the maturity point stands: the project is at v0.26.x, so the custom resource format and the operator behaviour are not at a stability guarantee yet. On maintenance, the record is regular, with v0.26.0 published on 2026-08-12, v0.26.1 on 2026-09-03, and v0.26.2 on 2026-09-18, and the last push to the repository on 2026-09-19.

## Conclusion

SchemaHero fits a platform team that wants schema definitions in git, reviewed and applied the same way as application manifests, and that runs Kubernetes workloads against a managed database. It does not fit a team without a cluster, a project that needs data migrations as well as DDL, or anyone who wants a stable API, since the project is still on a 0.x line. Before you adopt it, read the tutorial for the resource format, check that a plugin exists for your database, and decide how you will handle the fact that a schema change is applied automatically rather than by a human at the terminal.

## FAQ

### What does SchemaHero do?

It is a Kubernetes operator for declarative schema management. Table schemas are expressed as Kubernetes resources, an edited schema is deployed to the cluster, and SchemaHero calculates the required ALTER TABLE statement and applies it, to a database inside the cluster or an external one such as RDS or Google CloudSQL.

### How is SchemaHero installed?

The recommended way to deploy it is the kubectl plugin, with the full instructions and other installation methods in the documentation under installing with kubectl. The Makefile builds both the plugin, bin/kubectl-schemahero, and the manager binary, along with the generated manifests.

### How does SchemaHero support different databases?

Through a plugins directory with per-database implementations built using hashicorp/go-plugin, so each driver runs in a separate process from the operator. The examples directory includes postgresql, timescaledb, ssm, vault and doppler, covering both databases and secret sources for credentials.

### Does SchemaHero handle data migrations as well as schema changes?

The documented behaviour is that it calculates the required change as an ALTER TABLE statement and applies it, so it covers declarative DDL. The documentation excerpt does not describe a rollback path or data migration mechanism, which is worth checking against your own requirements.

### What licence is SchemaHero released under?

Apache-2.0, with the licence file at the root of the repository. The project is released under the same terms at v0.26.x, with v0.26.2 published on 2026-09-18.

## Sources

- [License: Apache-2.0](https://github.com/schemahero/schemahero/blob/main/LICENSE)
- [Project website](https://schemahero.io)
- [README](https://github.com/schemahero/schemahero/blob/main/README.md)
- [Releases](https://github.com/schemahero/schemahero/releases)
- [schemahero/schemahero on GitHub](https://github.com/schemahero/schemahero)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/schemahero-schemahero
