{"id":7330,"date":"2026-06-02T14:48:00","date_gmt":"2026-06-02T11:48:00","guid":{"rendered":"https:\/\/upcloud.com\/global\/?p=7330"},"modified":"2026-06-04T21:08:20","modified_gmt":"2026-06-04T20:08:20","slug":"when-need-kubernetes-operator-practical-guide","status":"publish","type":"post","link":"https:\/\/upcloud.com\/global\/blog\/when-need-kubernetes-operator-practical-guide\/","title":{"rendered":"When Do You Need a Kubernetes Operator? A Practical Guide"},"content":{"rendered":"\n<p>Kubernetes has become the de facto platform for container orchestration and for organizations building distributed applications. However, operational complexity remains one of its biggest challenges. Nearly <a href=\"https:\/\/platformengineering.org\/blog\/kubernetes-cluster-lifecycle-management-from-manual-operations-to-automated-excellence\" target=\"_blank\" rel=\"noopener\">70% of Kubernetes users point to fragmented tooling<\/a>, cluster management challenges, and difficulties running stateful workloads at scale. Kubernetes operators were introduced to address these challenges and simplify &#8220;Day 2&#8221; operations such as backups, version upgrades, and automated failure recovery.<\/p>\n\n\n\n<p>Contrary to popular opinion, however, operators are not a silver bullet. They carry real engineering costs in implementation, testing, maintenance, and a significantly expanded blast radius when things go wrong.<\/p>\n\n\n\n<p>In this article, we will explore what Kubernetes operators are, examine common use cases in the development workflow, provide a practical decision framework for when to use them, and walk through a practical example using Crossplane on <a href=\"https:\/\/upcloud.com\/global\/products\/managed-kubernetes\/\">UpCloud Kubernetes Service<\/a> (UKS).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What exactly is a Kubernetes Operator?<\/h2>\n\n\n\n<p>A Kubernetes operator is an application-specific controller that extends the Kubernetes API through custom resource definitions (CRDs) to automate the lifecycle management of complex applications on behalf of a Kubernetes user. It encapsulates domain-specific knowledge that enables them to account for the nuances of the applications they manage.<\/p>\n\n\n\n<p>To truly understand what a Kubernetes Operator is, it helps to look at how we traditionally manage applications.<\/p>\n\n\n\n<p>In a native Kubernetes setup without any third-party tools, the DevOps engineer or administrator is the brain of the operation. You write the YAML manifests, deploy the Pods, and manually handle the configuration.<\/p>\n\n\n\n<p>If the application needs an update, a vulnerability patch, or a code deployment, you have to trigger it. If traffic spikes and you aren&#8217;t using automated autoscalers, you have to scale the application manually.<\/p>\n\n\n\n<p>A Kubernetes Operator changes this entirely by packaging human operational knowledge into software. Instead of deploying and managing the application directly, you deploy an operator. It relies on a control loop that continuously compares the system&#8217;s actual state with the desired configuration and automatically reconciles them when a deviation occurs.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The three components:<\/h3>\n\n\n\n<p>An operator consists of three primary components working together to achieve this automation:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>A Custom Resource Definition (CRD)<\/strong>: CRD is a blueprint that lets you define your own custom Kubernetes object and tells the Kubernetes API, &#8220;Look out for this new type of resource.&#8221; Instead of thinking just in terms of Pods and Services, you can work with higher-level concepts. For instance, imagine you want to deploy a PostgreSQL database. Instead of managing dozens of individual Pods and ConfigMaps, you define a CRD called <code>PostgresDatabase<\/code>. Now, you can write a simple YAML file like this:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">apiVersion: database.example.com\/v1\nkind: PostgresDatabase\nmetadata:\n  name: my-app-prod-database\nspec:\n  version: '15'\n  replicas: 3\n  storage: 50Gi<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A <strong>Controller<\/strong>: This controller is a process running in the cluster that watches for instances of its associated CRD. It continuously runs a reconciliation loop. In this loop, the controller observes the system&#8217;s current state, compares it to the desired state specified in the custom resource, and then acts to align the current state with that desired state.<\/li>\n<\/ul>\n\n\n\n<p>When you apply the PostgresDatabase as shown in the YAML configuration above, the Custom Controller intercepts it. It shows that you want 3 Postgres 15 replicas with 50 Gi of storage<em>.<\/em> The controller then automatically provisions the underlying Kubernetes stateful sets, sets up replication, configures the storage volumes, and establishes network security.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A <strong>Custom Resource (CR)<\/strong>: The <strong>Custom Resource<\/strong> is the actual instance of your CRD that is deployed into the cluster. It is the specific configuration where you declare your desired state. In this Postgres scenario, the blueprint is the CRD (<code>PostgresDatabase<\/code>). When you apply your specific configuration file (<code>my-app-prod-database<\/code>), that running, living instance is the custom resource. If you need to scale your database from 3 replicas to 5, you don&#8217;t manually create new pods. You simply update the <code>replicas: 5<\/code> fields in your Custom Resource.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Use Cases of Kubernetes Operator<\/h2>\n\n\n\n<p>Kubernetes Operators are often mistaken for just another Kubernetes resource, but in reality, they capture operational knowledge in code and are well-suited for several scenarios, like:<\/p>\n\n\n\n<p><strong>1) Stateful Applications and Databases:<\/strong><\/p>\n\n\n\n<p>Managing stateful systems and databases is one of the primary reasons teams reach for an operator. Databases such as PostgreSQL, MySQL, Cassandra, and MongoDB, as well as distributed systems like Kafka, have operational requirements that Kubernetes does not natively support. These systems require tasks such as replication management, failover handling, backup scheduling, and version upgrades.<\/p>\n\n\n\n<p>An operator embeds this operational expertise directly into Kubernetes. Instead of manually executing runbooks, teams can define the desired state and let the operator automatically handle the complexity. If a PostgreSQL primary node fails in an application, the Kubernetes Operator can automatically promote a replica, update connections, and restore service without requiring manual intervention.<\/p>\n\n\n\n<p><strong>b) Infrastructure provisioning<\/strong><\/p>\n\n\n\n<p>Another use case of the Kubernetes Operator is treating cloud infrastructure as native Kubernetes resources. Operators unify this entire workflow by treating cloud infrastructure such as AWS S3 buckets, GCP Cloud SQL instances, VPCs, and DNS records directly as Kubernetes objects. By deploying infrastructure operators like Crossplane, developers can manage their entire stack through a single control plane using unified Kubernetes manifests.<\/p>\n\n\n\n<p>So if a developer creates a custom resource that requests a managed database, the operator will automatically provision it on AWS, Azure, or <a href=\"https:\/\/upcloud.com\/global\/\">UpCloud<\/a> and expose the connection details to the application.<\/p>\n\n\n\n<p>While this creates a powerful self-service experience for developers, it also adds another layer of operational complexity. If the cluster hosting the operator becomes unavailable or misconfigured, infrastructure provisioning workflows may also be affected. Teams should weigh these operational costs against the convenience of a unified control plane.<\/p>\n\n\n\n<p><strong>c) Platform engineering \/ internal developer platforms<\/strong><\/p>\n\n\n\n<p>Operators are particularly valuable when building a platform or an internal service for other teams. They allow platform teams to provide simplified, declarative internal APIs.<\/p>\n\n\n\n<p>A developer only needs to submit a minimalist custom manifest, such as a <code>WebApp<\/code> or <code>MicroserviceDeployment<\/code> CRD, specifying only their container image and resource tier. Behind the scenes, the Kubernetes Operator intercepts this simple request and automatically implements the desired state of the infrastructure. It generates the deployment, configures the HPA, provisions the <code>cert-manager<\/code> TLS certificate, and encodes all platform policies, so developers don&#8217;t have to deal with Ingress, <code>PodDisruptionBudgets<\/code>, or <code>NetworkPolicies<\/code>.<\/p>\n\n\n\n<p><strong>d) Automated Backup, Recovery, and Disaster Recovery<\/strong><\/p>\n\n\n\n<p>Business-critical applications require reliable backup and recovery mechanisms. Instead of maintaining separate backup scripts and cron jobs, organizations can define backup policies directly in Kubernetes. So the Kubernetes Operator can then continuously manage these workflows. In the event of a disaster, it can also restore data declaratively from backups using custom resources.<\/p>\n\n\n\n<p>For instance, a database operator can automatically create backups every six hours, store them in <a href=\"https:\/\/upcloud.com\/global\/products\/object-storage\/\">UpCloud&#8217;s managed object storage<\/a>, and recover to a specific point in time after an outage with minimal data loss.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Trade-offs of Kubernetes Operators<\/h2>\n\n\n\n<p>Kubernetes operators may provide additional functionality to Kubernetes when managing certain applications; however, not every problem in Kubernetes needs an operator. Hence, when building an Operator, you need to acknowledge the high, ongoing engineering and operational costs that come with building Kubernetes Operator patterns, like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Day-2 <\/strong><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\"><strong>operations and maintenance:<\/strong>\u00a0Writing a controller using frameworks like Kubebuilder or the\u00a0<\/span>Operator SDK is relatively straightforward on day one. However, you have to consider the ongoing maintenance of a distributed system component. Every time the Kubernetes API graduates a resource version or deprecates an old endpoint, you have to patch, test, and update your controller code.<\/li>\n\n\n\n<li><strong>Security surface:<\/strong> By definition, an operator needs to act on your behalf, which means it requires broad Role-Based Access Control (RBAC) permissions. It often needs cluster-wide rights to create, delete, and modify core resources. Introducing a custom operator means adding a highly privileged, long-running binary inside your cluster, making it a high-value target in your security model.<\/li>\n\n\n\n<li><strong>Testing complexity<\/strong>: Unlike stateless services, operators are difficult to test in isolation fully. Since operators interact directly with the Kubernetes API machinery, testing them requires running clusters (such as Minikube or KIND) or building massive, intricate mock setups.<\/li>\n\n\n\n<li><strong>The exploded blast radius:<\/strong> Standard application bugs crash a single pod or drop a few web requests. However, since an operator has write access to your cluster&#8217;s state, a bug in an operator can cause significant issues. Simple logic errors, a misread status field that falls through to the wrong branch, can trigger deletions, restarts, or misconfigurations at scale, automatically, before anyone notices.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">The Decision Framework: To Build or Not to Build?<\/h2>\n\n\n\n<p>When you decide to build a Kubernetes operator for your application, there&#8217;s one question worth asking: Does your situation require stateful, ongoing reconciliation logic that Kubernetes doesn&#8217;t handle natively?<br>If the answer is yes, then a Kubernetes operator might be justified. If not, you might just be introducing added complexity to your workflow.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">So, you need a Kubernetes operator when:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Your app has domain-specific operational logic that can&#8217;t be expressed with deployments, jobs, and health checks alone.<\/li>\n\n\n\n<li>You need to manage the full lifecycle of a stateful service, not just deploy it, but upgrade, back up, recover, and scale it with awareness of internal state.<\/li>\n\n\n\n<li>You are building a platform abstraction layer for other developers, since Kubernetes is too low-level, and you want a clean API rather than exposing raw manifests.<\/li>\n\n\n\n<li>Your system must react to real-world conditions, not just configuration, such as scaling based on queue depth, lag, or external signals, rather than CPU or memory alone.<\/li>\n\n\n\n<li>The operational process is already a long, fragile runbook that developers execute inconsistently, such that automation reduces real risk, not just convenience.<\/li>\n\n\n\n<li>Before building a Kubernetes operator, check for <a href=\"https:\/\/operatorhub.io\/\" target=\"_blank\" rel=\"noopener\">mature existing Operators<\/a> like CloudNativePG, Etcd, Vault, cert-manager, or Prometheus Operator that are already available to you.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Consider alternatives when:<\/h3>\n\n\n\n<p>As discussed earlier, not all cases or applications require Kubernetes Operators. If you are just trying to template Kubernetes manifests, Helm or Kustomize is your option. Most teams start down the operator path because they want parameterized manifests with some conditional logic. Helm is explicitly designed for this. It&#8217;s well-understood and does not introduce runtime complexity into your cluster.<\/p>\n\n\n\n<p>If you are orchestrating a one-off or sequential workflow (like database migrations or initialization steps), use CI\/CD pipelines, jobs, or init containers instead of a full reconciliation loop.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">In Practice: Managing Cloud Infrastructure with the Crossplane Operator on UKS<\/h2>\n\n\n\n<p>So far, we have discussed Kubernetes Operators from a conceptual perspective: how they extend Kubernetes, encode operational knowledge, and automate complex workflows. Now let&#8217;s look at a practical example of how this works.<\/p>\n\n\n\n<p>In this section, we will use <a href=\"https:\/\/www.crossplane.io\/\" target=\"_blank\" rel=\"noopener\">Crossplane<\/a> running on UpCloud Kubernetes Service (UKS) to demonstrate how Operators can bridge the gap between your Kubernetes cluster and external cloud platforms.<\/p>\n\n\n\n<p>Rather than building a traditional application-focused Operator from scratch, we will use Crossplane because it demonstrates a core Operator pattern used heavily in production today.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Prerequisites<\/h3>\n\n\n\n<p>Before we begin, make sure you have a few basics ready:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A running <a href=\"https:\/\/upcloud.com\/global\/docs\/guides\/get-started-managed-kubernetes\/\"><strong>UKS<\/strong> (UpCloud Kubernetes Service) cluster<\/a>.<\/li>\n\n\n\n<li>kubectl is installed locally and configured to talk to your UKS cluster.<\/li>\n\n\n\n<li><a href=\"https:\/\/helm.sh\/\" target=\"_blank\" rel=\"noopener\">Helm is<\/a> installed for a quick operator setup.<\/li>\n\n\n\n<li>An UpCloud API username and password (with API access enabled in your UpCloud account permissions).<\/li>\n\n\n\n<li>Cluster admin access<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Step 1: Install the Crossplane Operator Engine<\/h3>\n\n\n\n<p>Now that you have confirmed your cluster is ready, we need to inject the Crossplane Operator into your UKS cluster so it can start its background control loop.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>To achieve that, create an isolated namespace (a secure folder) inside your cluster for the operator using the following commands:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kubectl create namespace crossplane-system<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Tell Helm where to find the official Crossplane installation files:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">helm repo add crossplane-stable https:\/\/charts.crossplane.io\/stable  helm repo update<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Install Crossplane into that namespace<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">helm install crossplane \\--namespace crossplane-system crossplane-stable\/crossplane<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/help-repo-crossplane-stable.png\" alt=\"-\" class=\"wp-image-82939\" \/><\/figure>\n\n\n\n<p>Once you have confirmed that the crossplane status is deployed, the next step is to confirm that the operator has successfully moved in and started its background loops.<\/p>\n\n\n\n<p>Let&#8217;s ask your cluster to list its running applications with the command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kubectl get pods \\-n crossplane-system<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/kubectl-get-cross-plane-1024x131.png\" alt=\"-\" class=\"wp-image-82941\" \/><\/figure>\n\n\n\n<p>You should see Crossplane pods running successfully. This tells us that the cluster has gained new infrastructure management capabilities.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Install the UpCloud Provider Package<\/h3>\n\n\n\n<p>At this point, the Crossplane operator is running in your cluster; however, Crossplane uses providers that contain the cloud-specific logic required to communicate with external platforms such as AWS, Azure, GCP, or, in this case, UpCloud.<\/p>\n\n\n\n<p>So we need to install the UpCloud provider.<\/p>\n\n\n\n<p>This process registers <strong>Custom Resource Definitions (CRDs)<\/strong> into your cluster, effectively teaching Kubernetes the vocabulary of UpCloud infrastructure.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Create a folder in your local company and within that directory, create a manifest file called <code>provider-upcloud.yaml<\/code> with the following content<\/li>\n\n\n\n<li>apiVersion: pkg.crossplane.io\/v1<\/li>\n\n\n\n<li>kind: Provider<\/li>\n\n\n\n<li>metadata:<\/li>\n\n\n\n<li>name: provider-upcloud<\/li>\n\n\n\n<li>spec:<\/li>\n\n\n\n<li>package: xpkg.upbound.io\/upcloud\/provider-upcloud:v0.1.1<\/li>\n\n\n\n<li>Use the following command to send this file to your UKS cluster:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kubectl apply \\-f provider-config.yaml<\/code><\/pre>\n\n\n\n<p>Behind the scenes, the main Crossplane operator reads this file and contacts the UpCloud repository to download the UpCloud package. Now the Operator pattern becomes visible in practice. The Provider acts as a specialized controller that monitors Kubernetes resources and translates them into cloud infrastructure operations.<\/p>\n\n\n\n<p><strong>Note:<\/strong> The package version must be present in the Upbound registry. As of writing, v0.1.1 is the latest\/stable release. Do not guess version numbers; always verify at <a href=\"http:\/\/marketplace.upbound.io\" target=\"_blank\" rel=\"noopener\">marketplace.upbound.io<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Securely Share Your Cloud Credentials<\/h3>\n\n\n\n<p>Even though the operator is active, Crossplane still needs credentials to provision resources in your UpCloud account. So we need credentials for our Upcloud API Subaccounts.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>First, create a text file on your local machine named <code>upcloud-credentials.json<\/code>. Replace the placeholder text with your actual UpCloud API details in:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">{\n    \"username\": \"YOUR\\_UPCLOUD\\_API\\_SUBACCOUNT\\_USERNAME\",\n    \"password\": \"YOUR\\_UPCLOUD\\_API\\_SUBACCOUNT\\_PASSWORD\"\n}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Now, upload that file into an encrypted vault inside your UKS cluster called a <strong>Secret<\/strong>:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kubectl create secret generic upcloud-creds \\-n crossplane-system \\--from-file=creds=.\/upcloud-credentials.json<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Verify:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kubectl get secret upcloud-credentials \\-n crossplane-system<\/code><\/pre>\n\n\n\n<p><strong>Note:<\/strong> Now that the credentials are securely encrypted in the clutter, you can safely delete the local text file from your desktop.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Next, you need to create a setup resource called a <code>ProviderConfig<\/code> that tells our UpCloud operator exactly which secret vault to use when it needs to authenticate with the cloud.<\/li>\n\n\n\n<li>Create a file named <code>provider-config.yaml<\/code><\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">apiVersion: provider.upcloud.com\/v1beta1\nkind: ProviderConfig\nmetadata:\n  name: default\nspec:\n  credentials:\n    source: Secret\n    secretRef:\n      namespace: crossplane-system\n      name: upcloud-credentials\n      key: credentials<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Apply this using the following command:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kubectl apply \\-f provider-config.yaml<\/code><\/pre>\n\n\n\n<p>At this point, Crossplane has everything required to provision real infrastructure on UpCloud.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 4: Provision Real Infrastructure via YAML<\/h3>\n\n\n\n<p>Now we are ready to see the operator pattern complete its loop.<\/p>\n\n\n\n<p>Instead of opening the UpCloud dashboard and manually creating resources, we will define infrastructure using Kubernetes manifests.<\/p>\n\n\n\n<p>Let\u2019s provision a simple, isolated network router using nothing but a standard Kubernetes configuration file:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Create a final text file named <code>upcloud-network.yaml<\/code>:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">apiVersion: network.upcloud.com\/v1alpha1\nkind: Network\nmetadata:\n  name: uks-operator-demo-network\nspec:\n  forProvider:\n    name: uks-demo-network\n    zone: de-fra1\n    ipNetwork:\n      - address: 10.0.1.0\/24\n        dhcp: true\n        family: IPv4\n  providerConfigRef:\n    name: default<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Deploy it to your cluster:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kubectl apply \\-f upcloud-network.yaml<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You can now successfully track the Operator&#8217;s actions. and query the operator to watch it complete its reconciliation loop and talk to the UpCloud backend APIs:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kubectl get networks.network.upcloud.com \\-w<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Expected output once provisioned:<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/kubectl-get-networks.png\" alt=\"-\" class=\"wp-image-82949\" \/><\/figure>\n\n\n\n<p>The EXTERNAL-NAME field is the actual UpCloud network UUID, confirming that the Operator has successfully made an API call to UpCloud and that the network now exists in your cloud account.<\/p>\n\n\n\n<p>What is happening here is the following:<\/p>\n\n\n\n<p>You applied a YAML manifest<br>\u2193<br>Crossplane&#8217;s controller (Operator) detected the new Network resource<br>\u2193<br>It&#8217;s called the UpCloud API: POST \/1.3\/network<br>\u2193<br>UpCloud created the network and returned the UUID (03a9a60d\u2026)<br>\u2193<br>The Operator wrote that UUID back to EXTERNAL-NAME<br>\u2193<br>Status updated: SYNCED: True, READY: True<\/p>\n\n\n\n<p><strong>This is the reconciliation loop, where<\/strong> the operator continuously monitors the desired state (your YAML) and ensures the actual state (UpCloud infrastructure) matches it. If someone deletes the network directly in UpCloud, Crossplane will recreate it. If you delete the Kubernetes resource, Crossplane will delete it from UpCloud.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 5: Test the Self-Healing<\/h3>\n\n\n\n<p>To understand why operators are highly valued compared to native Kubernetes deployment scripts, let&#8217;s test the self-healing capabilities of this operator.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Open two terminal windows. In both, run this to point kubectl at the right cluster:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">KUBECONFIG=\/Users\/anitaihuman\/Downloads\/dev-md-de-fra1\\_kubeconfig.yaml<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Start a live watcher in Terminal A using the following command. Leave the window running. This will show status changes in real time as Crossplane reacts:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kubectl get networks.network.upcloud.com \\-w<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Set your UpCloud credentials in Terminal B. Run these two commands one at a time:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">UPCLOUD\\_USER=$(python3 \\-c \"import json;print(json.load(open('upcloud-credentials.json'))\\['username'\\])\")   UPCLOUD\\_PASS=$(python3 \\-c \"import json; print(json.load(open('upcloud-credentials.json'))\\['password'\\])\")<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Delete the real network directly in UpCloud from Terminal B, bypassing Kubernetes entirely, as if someone went into the cloud console and deleted it manually:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">curl \\-s \\-o \/dev\/null \\-w \"%{http\\_code}\" \\-u \"$UPCLOUD\\_USER:$UPCLOUD\\_PASS\" \\-X DELETE https:\/\/api.upcloud.com\/1.3\/network\/03a9a60d-82bc-4448-8448-d58c306fb70b<\/code><\/pre>\n\n\n\n<p>You should receive a 204 response immediately, confirming the network has been removed from UpCloud.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/kubernetes-operator-upcloud-api-1024x255.png\" alt=\"-\" class=\"wp-image-82944\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You can confirm that the network was truly deleted and recreated by running this command in Terminal B:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kubectl get networks.network.upcloud.com uks-operator-demo-network<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/kubernetes-operator-kubectl-get-networks.png\" alt=\"-\" class=\"wp-image-82943\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Now in Terminal A, you will notice that the status flips from SYNCED: True<strong>,<\/strong> READY: True \u2192 SYNCED: False \u2192 back to SYNCED: True<strong>,<\/strong> READY: True with an EXTERNAL-NAME populated.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/kubernetes-operator-config.png\" alt=\"-\" class=\"wp-image-82946\" \/><\/figure>\n\n\n\n<p>The Crossplane detected a drift and is automatically rebuilding the network in UpCloud without any manual intervention.<\/p>\n\n\n\n<p>You will notice that the EXTERNAL-NAME field will also contain a new UpCloud network UUID, proving that the operator actually made a real API call to UpCloud and rebuilt the infrastructure from your Kubernetes manifest alone. If we did not have the Kubernetes Operator, that resource would be lost completely until a developer notices and manually fixes it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>Kubernetes Operators have fundamentally changed how we think about automation by shifting the operational burden from developers to software. They enhance the Kubernetes API by embedding domain expertise into specialized controllers and integrating with Kubernetes resources such as jobs, stateful sets, and RBAC operators. Evidently, Operators make application management simpler by automating the application lifecycle for engineering teams. However, the decision to use an operator boils down to a fundamental trade-off between operational strength and development complexity.<\/p>\n\n\n\n<p>If you are ready to explore the capabilities of Kubernetes Operators without the added complexity of managing control plane infrastructure, a managed platform provides the perfect sandbox. With a service like <a href=\"https:\/\/upcloud.com\/global\/products\/managed-kubernetes\/\">UpCloud Kubernetes Service (UKS),<\/a> you can quickly spin up a production-ready cluster and start experimenting with Operators such as Crossplane, CloudNativePG, and other cloud-native automation tools.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Kubernetes has become the de facto platform for container orchestration and for organizations building distributed applications. However, operational complexity remains one of its biggest challenges. [&hellip;]<\/p>\n","protected":false},"author":83,"featured_media":82961,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_relevanssi_hide_post":"","_relevanssi_hide_content":"","_relevanssi_pin_for_all":"","_relevanssi_pin_keywords":"","_relevanssi_unpin_keywords":"","_relevanssi_related_keywords":"","_relevanssi_related_include_ids":"","_relevanssi_related_exclude_ids":"","_relevanssi_related_no_append":"","_relevanssi_related_not_related":"","_relevanssi_related_posts":"3924,535,3694,499,880,112","_relevanssi_noindex_reason":"Blocked by a filter function","footnotes":""},"categories":[64,22,28],"tags":[],"class_list":["post-7330","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-kubernetes","category-cloud-infrastructure","category-long-reads"],"acf":[],"_links":{"self":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/posts\/7330","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/users\/83"}],"replies":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/comments?post=7330"}],"version-history":[{"count":1,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/posts\/7330\/revisions"}],"predecessor-version":[{"id":7332,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/posts\/7330\/revisions\/7332"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/"}],"wp:attachment":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/media?parent=7330"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/categories?post=7330"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tags?post=7330"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}