Official Go client library for Kubernetes API - typed clients, controllers, and cluster interaction tools
Server-Side Apply (SSA) is a declarative object management mechanism that enables multiple controllers to manage different fields of the same resource without conflicts. Apply configurations provide a builder pattern for constructing objects for SSA.
k8s.io/client-go/applyconfigurationsk8s.io/client-go/applyconfigurations/meta/v1import (
corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
appsv1ac "k8s.io/client-go/applyconfigurations/apps/v1"
metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)Server-Side Apply differs from traditional Update operations:
Traditional Update:
Server-Side Apply:
// Traditional Update (NOT recommended for shared objects)
deployment, _ := clientset.AppsV1().Deployments("default").Get(ctx, "myapp", metav1.GetOptions{})
deployment.Spec.Replicas = pointer.Int32(3)
_, err := clientset.AppsV1().Deployments("default").Update(ctx, deployment, metav1.UpdateOptions{})
// Server-Side Apply (recommended)
deploymentApply := appsv1ac.Deployment("myapp", "default").
WithSpec(appsv1ac.DeploymentSpec().
WithReplicas(3))
_, err := clientset.AppsV1().Deployments("default").Apply(
ctx,
deploymentApply,
metav1.ApplyOptions{
FieldManager: "my-controller",
})import (
corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Create pod apply configuration
podApply := corev1ac.Pod("my-pod", "default").
WithLabels(map[string]string{"app": "myapp"}).
WithSpec(corev1ac.PodSpec().
WithContainers(
corev1ac.Container().
WithName("nginx").
WithImage("nginx:1.21").
WithPorts(corev1ac.ContainerPort().
WithContainerPort(80).
WithProtocol(corev1.ProtocolTCP))))
// Apply to cluster
result, err := clientset.CoreV1().Pods("default").Apply(
context.TODO(),
podApply,
metav1.ApplyOptions{
FieldManager: "my-controller",
Force: false,
})import (
appsv1ac "k8s.io/client-go/applyconfigurations/apps/v1"
corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
)
deploymentApply := appsv1ac.Deployment("myapp", "default").
WithLabels(map[string]string{"app": "myapp"}).
WithSpec(appsv1ac.DeploymentSpec().
WithReplicas(3).
WithSelector(metav1ac.LabelSelector().
WithMatchLabels(map[string]string{"app": "myapp"})).
WithTemplate(corev1ac.PodTemplateSpec().
WithLabels(map[string]string{"app": "myapp"}).
WithSpec(corev1ac.PodSpec().
WithContainers(corev1ac.Container().
WithName("myapp").
WithImage("myapp:v1.0.0").
WithPorts(corev1ac.ContainerPort().
WithContainerPort(8080)).
WithEnv(
corev1ac.EnvVar().
WithName("LOG_LEVEL").
WithValue("info"),
corev1ac.EnvVar().
WithName("PORT").
WithValue("8080")).
WithResources(corev1ac.ResourceRequirements().
WithRequests(corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("128Mi"),
}).
WithLimits(corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("500m"),
corev1.ResourceMemory: resource.MustParse("512Mi"),
}))))))
result, err := clientset.AppsV1().Deployments("default").Apply(
context.TODO(),
deploymentApply,
metav1.ApplyOptions{
FieldManager: "my-controller",
})serviceApply := corev1ac.Service("myapp", "default").
WithLabels(map[string]string{"app": "myapp"}).
WithSpec(corev1ac.ServiceSpec().
WithSelector(map[string]string{"app": "myapp"}).
WithType(corev1.ServiceTypeLoadBalancer).
WithPorts(
corev1ac.ServicePort().
WithName("http").
WithPort(80).
WithTargetPort(intstr.FromInt(8080)).
WithProtocol(corev1.ProtocolTCP)))
result, err := clientset.CoreV1().Services("default").Apply(
context.TODO(),
serviceApply,
metav1.ApplyOptions{
FieldManager: "my-controller",
})type ApplyOptions struct {
// TypeMeta allows you to set the APIVersion and Kind
TypeMeta
// DryRun controls whether the request is executed
DryRun []string
// Force overrides conflicts with other field managers
Force bool
// FieldManager is the name of the actor applying the configuration
FieldManager string
// FieldValidation determines server-side validation
FieldValidation string
}
// Apply with options
result, err := clientset.CoreV1().Pods("default").Apply(
context.TODO(),
podApply,
metav1.ApplyOptions{
FieldManager: "my-controller",
Force: true, // Force take ownership of conflicting fields
DryRun: []string{metav1.DryRunAll}, // Dry run only
})Each field in an object can have one or more field managers:
// Controller A applies replicas
deploymentApply := appsv1ac.Deployment("myapp", "default").
WithSpec(appsv1ac.DeploymentSpec().
WithReplicas(3))
_, err := clientset.AppsV1().Deployments("default").Apply(
ctx, deploymentApply,
metav1.ApplyOptions{FieldManager: "controller-a"})
// Controller B applies image (no conflict)
deploymentApply := appsv1ac.Deployment("myapp", "default").
WithSpec(appsv1ac.DeploymentSpec().
WithTemplate(corev1ac.PodTemplateSpec().
WithSpec(corev1ac.PodSpec().
WithContainers(corev1ac.Container().
WithName("myapp").
WithImage("myapp:v2.0.0")))))
_, err := clientset.AppsV1().Deployments("default").Apply(
ctx, deploymentApply,
metav1.ApplyOptions{FieldManager: "controller-b"})
// Success - different fields, no conflict// This will conflict if another manager owns the replicas field
deploymentApply := appsv1ac.Deployment("myapp", "default").
WithSpec(appsv1ac.DeploymentSpec().
WithReplicas(5))
_, err := clientset.AppsV1().Deployments("default").Apply(
ctx, deploymentApply,
metav1.ApplyOptions{FieldManager: "controller-c"})
if apierrors.IsConflict(err) {
// Conflict detected - another manager owns this field
fmt.Println("Conflict:", err)
// Option 1: Force take ownership
_, err = clientset.AppsV1().Deployments("default").Apply(
ctx, deploymentApply,
metav1.ApplyOptions{
FieldManager: "controller-c",
Force: true, // Force take ownership
})
// Option 2: Don't apply this field, let other manager keep it
// Just remove it from your apply configuration
}// Apply status separately
podApply := corev1ac.Pod("my-pod", "default").
WithStatus(corev1ac.PodStatus().
WithPhase(corev1.PodRunning).
WithConditions(corev1ac.PodCondition().
WithType(corev1.PodReady).
WithStatus(corev1.ConditionTrue)))
result, err := clientset.CoreV1().Pods("default").ApplyStatus(
context.TODO(),
podApply,
metav1.ApplyOptions{
FieldManager: "my-controller",
})package main
import (
"context"
"fmt"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
appsv1ac "k8s.io/client-go/applyconfigurations/apps/v1"
corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
const fieldManager = "example-controller"
func reconcileDeployment(ctx context.Context, clientset kubernetes.Interface) error {
// Build desired state declaratively
deploymentApply := appsv1ac.Deployment("myapp", "default").
WithLabels(map[string]string{
"app": "myapp",
"managed-by": "example-controller",
}).
WithSpec(appsv1ac.DeploymentSpec().
WithReplicas(3).
WithSelector(metav1ac.LabelSelector().
WithMatchLabels(map[string]string{"app": "myapp"})).
WithTemplate(corev1ac.PodTemplateSpec().
WithLabels(map[string]string{"app": "myapp"}).
WithSpec(corev1ac.PodSpec().
WithContainers(corev1ac.Container().
WithName("myapp").
WithImage("myapp:v1.0.0").
WithPorts(corev1ac.ContainerPort().
WithContainerPort(8080))))))
// Apply to cluster
result, err := clientset.AppsV1().Deployments("default").Apply(
ctx,
deploymentApply,
metav1.ApplyOptions{
FieldManager: fieldManager,
})
if err != nil {
return fmt.Errorf("failed to apply deployment: %w", err)
}
fmt.Printf("Applied deployment: %s (replicas: %d)\n",
result.Name, *result.Spec.Replicas)
return nil
}
func main() {
config, _ := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
clientset, _ := kubernetes.NewForConfig(config)
ctx := context.Background()
if err := reconcileDeployment(ctx, clientset); err != nil {
panic(err)
}
}import (
appsv1 "k8s.io/api/apps/v1"
appsv1ac "k8s.io/client-go/applyconfigurations/apps/v1"
)
// Get existing deployment
deployment, err := clientset.AppsV1().Deployments("default").Get(
ctx, "myapp", metav1.GetOptions{})
// Extract apply configuration
extractedApply, err := appsv1ac.ExtractDeployment(deployment, fieldManager)
// Modify and reapply
extractedApply.Spec.Replicas = pointer.Int32(5)
result, err := clientset.AppsV1().Deployments("default").Apply(
ctx,
extractedApply,
metav1.ApplyOptions{
FieldManager: fieldManager,
})// Only update replicas, leave everything else alone
deploymentApply := appsv1ac.Deployment("myapp", "default").
WithSpec(appsv1ac.DeploymentSpec().
WithReplicas(5))
clientset.AppsV1().Deployments("default").Apply(
ctx, deploymentApply,
metav1.ApplyOptions{FieldManager: "autoscaler"})// Add labels without affecting other fields
podApply := corev1ac.Pod("my-pod", "default").
WithLabels(map[string]string{
"environment": "production",
"team": "platform",
})
clientset.CoreV1().Pods("default").Apply(
ctx, podApply,
metav1.ApplyOptions{FieldManager: "labeler"})// Only apply if certain conditions are met
deployment, err := clientset.AppsV1().Deployments("default").Get(
ctx, "myapp", metav1.GetOptions{})
if deployment.Status.AvailableReplicas == *deployment.Spec.Replicas {
// Only update image if all replicas are available
deploymentApply := appsv1ac.Deployment("myapp", "default").
WithSpec(appsv1ac.DeploymentSpec().
WithTemplate(corev1ac.PodTemplateSpec().
WithSpec(corev1ac.PodSpec().
WithContainers(corev1ac.Container().
WithName("myapp").
WithImage("myapp:v2.0.0")))))
clientset.AppsV1().Deployments("default").Apply(
ctx, deploymentApply,
metav1.ApplyOptions{FieldManager: "roller"})
}| Method | Use Case | Pros | Cons |
|---|---|---|---|
| Create | New resources | Simple | Fails if exists |
| Update | Full replacement | Complete control | Overwrites others |
| Patch (JSON) | Specific changes | Precise | Complex syntax |
| Patch (Merge) | Partial updates | Simpler than JSON | Can't remove fields |
| Patch (Strategic) | K8s-aware merge | Smart merging | K8s types only |
| Apply | Declarative mgmt | Multi-actor safe | Requires SSA support |
Install with Tessl CLI
npx tessl i tessl/golang-k8s-io--client-go@0.35.0