Kubernetes
Go
API
Operator SDK
DevOps

Setup Kubernetes Pods via API Call using Go and Operator SDK

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

If you want Go code to create Pods in Kubernetes, you can talk to the Kubernetes API directly through a client library. If you want that behavior to be long-lived, declarative, and driven by a custom resource, Operator SDK is the better fit because it wraps the client logic in a reconcile loop instead of a one-time script.

Know the Difference Between client-go and an Operator

Use plain API calls when you need an imperative tool or service action:

  • create one Pod now
  • inspect cluster state
  • run an administrative task

Use Operator SDK when you want:

  • a custom resource such as ExampleApp
  • a controller that keeps actual state aligned with desired state
  • repeated reconciliation after restarts, deletes, or spec changes

Operators do not replace the Kubernetes API. They structure how you keep using it.

Scaffold an Operator Project

The typical Operator SDK flow starts with a Go-based operator project and a custom resource definition.

Example high-level commands:

bash
operator-sdk init --domain example.com --repo example.com/pod-operator
operator-sdk create api --group apps --version v1alpha1 --kind ExampleApp --resource --controller

That creates the API types and a controller skeleton where your Pod-management logic will live.

Define Desired Pod State in the Custom Resource

A small spec keeps the controller flexible.

go
1type ExampleAppSpec struct {
2    Image    string `json:"image,omitempty"`
3    Replicas int32  `json:"replicas,omitempty"`
4}

This lets a user declare intent through YAML instead of calling Pod creation functions manually every time.

Create Pods in the Reconcile Loop

Inside the controller, use the controller-runtime client to read the custom resource and create dependent Pods.

go
1package controllers
2
3import (
4    "context"
5
6    appsv1alpha1 "example.com/pod-operator/api/v1alpha1"
7    corev1 "k8s.io/api/core/v1"
8    apierrors "k8s.io/apimachinery/pkg/api/errors"
9    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10    ctrl "sigs.k8s.io/controller-runtime"
11    "sigs.k8s.io/controller-runtime/pkg/client"
12)
13
14func (r *ExampleAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
15    var app appsv1alpha1.ExampleApp
16    if err := r.Get(ctx, req.NamespacedName, &app); err != nil {
17        return ctrl.Result{}, client.IgnoreNotFound(err)
18    }
19
20    pod := corev1.Pod{
21        ObjectMeta: metav1.ObjectMeta{
22            Name:      app.Name + "-pod",
23            Namespace: app.Namespace,
24        },
25        Spec: corev1.PodSpec{
26            Containers: []corev1.Container{
27                {
28                    Name:  "app",
29                    Image: app.Spec.Image,
30                },
31            },
32        },
33    }
34
35    if err := ctrl.SetControllerReference(&app, &pod, r.Scheme); err != nil {
36        return ctrl.Result{}, err
37    }
38
39    var existing corev1.Pod
40    err := r.Get(ctx, client.ObjectKey{Name: pod.Name, Namespace: pod.Namespace}, &existing)
41    if apierrors.IsNotFound(err) {
42        if err := r.Create(ctx, &pod); err != nil {
43            return ctrl.Result{}, err
44        }
45    } else if err != nil {
46        return ctrl.Result{}, err
47    }
48
49    return ctrl.Result{}, nil
50}

This is the core operator pattern: read desired state, compare against actual state, then create or repair resources.

Why Owner References Matter

ctrl.SetControllerReference is not optional boilerplate. It tells Kubernetes that the Pod belongs to the custom resource. That gives you:

  • garbage collection when the custom resource is deleted
  • correct watch relationships
  • clearer cluster ownership semantics

Without owner references, your controller may create orphaned Pods that outlive the thing that requested them.

RBAC and Deployment

Your operator needs permission to watch custom resources and manage Pods. Operator SDK usually scaffolds RBAC markers, but you still need to verify them.

Typical capability requirements:

  • get, list, watch on the custom resource
  • create, get, list, watch, update, delete on Pods

After generating manifests, deploy the operator and apply a custom resource instance.

yaml
1apiVersion: apps.example.com/v1alpha1
2kind: ExampleApp
3metadata:
4  name: demo
5spec:
6  image: nginx:1.27
7  replicas: 1

Then the reconcile loop should create the managed Pod.

When Not to Manage Bare Pods

In many real applications, you should manage a Deployment instead of a bare Pod, because Deployments handle restart behavior and rolling updates more naturally. A Pod example is fine for understanding API calls, but production operators often reconcile higher-level workloads.

That design decision is worth mentioning because it shows operational awareness, not just API familiarity.

Common Pitfalls

The most common mistake is treating an operator like a one-shot Pod creation script and ignoring reconciliation. Another is creating child resources without owner references, which leads to orphaned Pods. Teams also often start by reconciling bare Pods when a Deployment would better represent the desired workload. Finally, RBAC is frequently under-specified, so the controller compiles and starts but fails at runtime with authorization errors.

Summary

  • Use direct Kubernetes API calls for imperative one-off actions and Operator SDK for declarative reconciliation.
  • In an operator, create resources from the reconcile loop rather than from ad hoc endpoints.
  • Set owner references so managed Pods are tracked and garbage-collected correctly.
  • Verify RBAC before debugging controller logic.
  • For real applications, consider reconciling a Deployment instead of a bare Pod.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.