Kubernetes Native Development: From Java Operators to Golang Controllers
Kubernetes is built for extensibility, and one of its most powerful extension points is the Operator pattern—allowing you to codify operational knowledge directly into the cluster. While Golang is the native choice for Kubernetes development, frameworks like Fabric8 and Quarkus have brought first-class Kubernetes operator support to Java developers as well.
This article walks you through the core concepts of Custom Resource Definitions (CRDs), building custom operators in both Java and Go, and choosing the right approach based on your team’s skills, ecosystem, and performance needs.
What Are CRDs and Operators?
- CRDs (Custom Resource Definitions) let you define your own Kubernetes resources (like
KafkaCluster,BackupJob, etc.). - Operators are controllers that watch these custom resources and act accordingly.
Goal Example:
Let’s say we want to build an operator that provisions a database backup job whenever a BackupRequest resource is created.
Option 1: Writing a Kubernetes Operator in Go (Native)
Go is the official language of Kubernetes, and the Kubebuilder and Operator SDK provide robust tooling.
Setup (with Operator SDK)
operator-sdk init --domain example.com --repo github.com/example/db-backup-operator operator-sdk create api --group apps --version v1alpha1 --kind BackupRequest --resource --controller
This scaffolds:
- The CRD definition (
BackupRequest) - Controller logic to reconcile resources
Sample CRD Spec (in Go)
type BackupRequestSpec struct {
DatabaseName string `json:"databaseName"`
Schedule string `json:"schedule"` // e.g., cron
}
Sample Reconcile Logic
func (r *BackupRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var backup BackupRequest
if err := r.Get(ctx, req.NamespacedName, amp;backup); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
job := createK8sJob(backup.Spec.DatabaseName)
err := r.Create(ctx, amp;job)
return ctrl.Result{}, err
}
✅ Pros:
- Full performance and API parity with Kubernetes internals
- Native support, battle-tested tooling
❌ Cons:
- Requires learning Go
- Boilerplate-heavy for Java-native teams
Option 2: Writing a Kubernetes Operator in Java (with Fabric8 or Quarkus)
🔹 Using Fabric8 Kubernetes Client
Fabric8 is the go-to Java client for Kubernetes. It also supports building operators using informers and controllers.
Maven Setup
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<version>6.x.x</version>
</dependency>
Define the CRD POJO
@Group("apps.example.com")
@Version("v1alpha1")
@Kind("BackupRequest")
@Plural("backuprequests")
public class BackupRequest extends CustomResource<BackupRequestSpec, Void> {}
public class BackupRequestSpec {
private String databaseName;
private String schedule;
// Getters/Setters
}
Reconciler Logic
public class BackupRequestController implements ResourceEventHandler<BackupRequest> {
private final KubernetesClient client;
public void onAdd(BackupRequest backupRequest) {
Job job = createBackupJob(backupRequest.getSpec().getDatabaseName());
client.batch().v1().jobs().inNamespace("default").create(job);
}
}
Using Quarkus Extension
With Quarkus, operator development is even easier thanks to the quarkus-operator-sdk extension.
mvn io.quarkus.platform:quarkus-maven-plugin:2.x.x:create \ -Dextensions="operator-sdk"
You can then annotate your reconciler like:
@Controller(crdName = "backuprequests.apps.example.com")
public class BackupRequestReconciler implements Reconciler<BackupRequest> {
public UpdateControl<BackupRequest> reconcile(BackupRequest resource, Context context) {
// Logic to create Kubernetes Job
return UpdateControl.noUpdate();
}
}
✅ Pros:
- Java ecosystem + Spring/Quarkus support
- Easier onboarding for Java teams
- Supports unit testing with JUnit/mock frameworks
❌ Cons:
- Not as performant as Go
- Slightly behind in ecosystem maturity
Testing Your Operator (Both Go & Java)
- Use kind or minikube to spin up a dev cluster.
- Use controller-runtime’s envtest (Go) or Testcontainers (Java) for integration testing.
- Always define status fields in CRDs for observability.
Java vs Go for Kubernetes Operators
| Feature | Go (Kubebuilder) | Java (Fabric8 / Quarkus) |
|---|---|---|
| Performance | ✅ Native, fastest | ⚠️ Slight overhead |
| Language Ecosystem | ✅ Designed for K8s | ✅ Leverage existing Java libs |
| Learning Curve | ⚠️ Need to learn Go | ✅ Java-friendly |
| Tooling | ✅ Rich (Kubebuilder, SDK) | ⚠️ Less standardization |
| Testing | ✅ envtest, fake client | ✅ JUnit, Mockito, Testcontainers |
Final Thoughts
If you’re building a production-grade operator with high performance requirements, Golang is still the gold standard. But for Java-centric teams who want to extend Kubernetes without leaving their comfort zone, frameworks like Fabric8 and Quarkus Operator SDK offer powerful alternatives.
Ultimately, choose the language that aligns best with your team’s skills, and leverage the right tools to keep your operator robust and testable.

