# How to Deploy Applications in Kubernetes using OpenTofu

> How to publish multiple replicas of an application from the Docker registry and create a NodePort or a LoadBalancer in Kubernetes using OpenTofu.

- Source: https://www.itwonderlab.com/kubernetes-with-opentofu/
- Published: 2023-11-06
- Updated: 2026-10-05
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## Publishing Containers in Kubernetes with OpenTofu

A tutorial showing how to deploy an application from a Docker Registry into a local Kubernetes cluster created with k3s.

This tutorial shows how to publish an Application and create a NodePort in Kubernetes using OpenTofu. **It only takes 10 seconds**!

The application will be retrieved from the Docker Public Registry, and OpenTofu will instruct Kubernetes to create 3 replicas and publish its services using a LoadBalancer and/or a NodePort.

### Install a local Kubernetes cluster like K3S

See the [How to Install OpenTofu](https://www.itwonderlab.com/how-to-install-opentofu/) tutorial to install the latest OpenTofu release.

This tutorial is fully compatible with Terraform, if you prefer to use Terraform replace `tofu` with `terraform` in the commands.

If you already have a local or remote Kubernetes cluster ignore the following instructions to install a local Kubernetes cluster.

See the [How to Install K3s](https://www.itwonderlab.com/install-kubernetes-k3s/) tutorial to install K3s, a lightweight Kubernetes distribution.

### Check the Kubernetes Context

Check that the Kubernetes cluster is running by requesting information about the nodes:

```shell
$ kubectl get nodes
NAME    STATUS   ROLES                  AGE     VERSION
xps13   Ready    control-plane,master   8m23s   v1.27.7+k3s1
```

Check the name of the Kubernetes context by requesting information about the kubeconfig file:

```shell
$ kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://127.0.0.1:6443
  name: default
contexts:
- context:
    cluster: default
    user: default
  name: default
current-context: default
kind: Config
preferences: {}
users:
- name: default
  user:
    client-certificate-data: DATA+OMITTED
    client-key-data: DATA+OMITTED
```

In this example the Kubernetes cluster name is **default**.

### Create an OpenTofu plan file

Create a Terraform file named `color_app.tf` that describes the OpenTofu required version, the Kubernetes provider, and its configuration along with the desired state of the application and resources to deploy in Kubernetes using OpenTofu.

Set the config\_path = “/etc/rancher/k3s/k3s.yaml” to the kubeconfig file path if it differs from the standard location and set the config\_context if it is not the default. See [Kubernetes Terraform documentation](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs#file-config) for other provider options.

This example deploys an application named color from the [itwonderlab/color Docker repository](https://hub.docker.com/r/itwonderlab/color). The color app uses the environment variable `COLOR` to change the background color of the web page it shows. Adjust the resource “kubernetes\_deployment” to your needs and the name of the containerized application that will be deployed.

```yaml
# Copyright (C) 2018 - 2023 IT Wonder Lab (https://www.itwonderlab.com)
#
# This software may be modified and distributed under the terms
# of the MIT license.  See the LICENSE file for details.
# -------------------------------- WARNING --------------------------------
# IT Wonder Lab's best practices for infrastructure include modularizing 
# Terraform configuration. 
# In this example, we define everything in a single file. 
# See other tutorials for Terraform best practices for Kubernetes deployments.
# -------------------------------- WARNING --------------------------------
terraform {
  required_version = "> 1.5"
}

#-----------------------------------------
# Default provider: Kubernetes
#-----------------------------------------
provider "kubernetes" {

  #kubeconfig file, if using K3S set the path
  config_path = "/etc/rancher/k3s/k3s.yaml"

  #Context to choose from the config file. Change if not default.
  #config_context = "local-k3s"
}

#-----------------------------------------
# KUBERNETES: Deploy App
#-----------------------------------------
resource "kubernetes_deployment" "color" {
    metadata {
        name = "color-blue-dep"
        labels = {
            app   = "color"
            color = "blue"
        } //labels
    } //metadata
    
    spec {
        selector {
            match_labels = {
                app   = "color"
                color = "blue"
            } //match_labels
        } //selector
        #Number of replicas
        replicas = 3
        #Template for the creation of the pod
        template { 
            metadata {
                labels = {
                    app   = "color"
                    color = "blue"
                } //labels
            } //metadata
            spec {
                container {
                    image = "itwonderlab/color"   #Docker image name
                    name  = "color-blue"          #Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL).
                    
                    #Block of string name and value pairs to set in the container's environment
                    env { 
                        name = "COLOR"
                        value = "blue"
                    } //env
                    
                    #List of ports to expose from the container.
                    port { 
                        container_port = 8080
                    }//port          
                    
                    resources {
                        requests = {
                            cpu    = "250m"
                            memory = "50Mi"
                        } //requests
                    } //resources
                } //container
            } //spec
        } //template
    } //spec
} //resource
```

### Initialize the OpenTofu Plan and Providers

OpenTofu needs to download the providers used by the plan into the .terraform directory. Run `tofu init` to initialize.

```shell
$ tofu init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/kubernetes...
- Installing hashicorp/kubernetes v2.23.0...
- Installed hashicorp/kubernetes v2.23.0 (signed, key ID 0DC64ED093B3E9FF)

Providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://opentofu.org/docs/cli/plugins/signing/

OpenTofu has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that OpenTofu can guarantee to make the same selections by default when
you run "tofu init" in the future.

OpenTofu has been successfully initialized!

You may now begin working with OpenTofu. Try running "tofu plan" to see
any changes that are required for your infrastructure. All OpenTofu commands
should now work.

If you ever set or change modules or backend configuration for OpenTofu,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
```

### Execute OpenTofu Plan and Apply

Run the OpenTofu **plan command** to see what resources will be created, changed, or destroyed:

```shell
$ tofu plan

OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

OpenTofu will perform the following actions:

  # kubernetes_deployment.color will be created
  + resource "kubernetes_deployment" "color" {
      + id               = (known after apply)
      + wait_for_rollout = true

      + metadata {
          + generation       = (known after apply)
          + labels           = {
              + "app"   = "color"
              + "color" = "blue"
            }
          + name             = "color-blue-dep"
          + namespace        = "default"
          + resource_version = (known after apply)
          + uid              = (known after apply)
        }

      + spec {
          + min_ready_seconds         = 0
          + paused                    = false
          + progress_deadline_seconds = 600
          + replicas                  = "3"
          + revision_history_limit    = 10

          + selector {
              + match_labels = {
                  + "app"   = "color"
                  + "color" = "blue"
                }
            }

          + template {
              + metadata {
                  + generation       = (known after apply)
                  + labels           = {
                      + "app"   = "color"
                      + "color" = "blue"
                    }
                  + name             = (known after apply)
                  + resource_version = (known after apply)
                  + uid              = (known after apply)
                }
              + spec {
                  + automount_service_account_token  = true
                  + dns_policy                       = "ClusterFirst"
                  + enable_service_links             = true
                  + host_ipc                         = false
                  + host_network                     = false
                  + host_pid                         = false
                  + hostname                         = (known after apply)
                  + node_name                        = (known after apply)
                  + restart_policy                   = "Always"
                  + scheduler_name                   = (known after apply)
                  + service_account_name             = (known after apply)
                  + share_process_namespace          = false
                  + termination_grace_period_seconds = 30

                  + container {
                      + image                      = "itwonderlab/color"
                      + image_pull_policy          = (known after apply)
                      + name                       = "color-blue"
                      + stdin                      = false
                      + stdin_once                 = false
                      + termination_message_path   = "/dev/termination-log"
                      + termination_message_policy = (known after apply)
                      + tty                        = false

                      + env {
                          + name  = "COLOR"
                          + value = "blue"
                        }

                      + port {
                          + container_port = 8080
                          + protocol       = "TCP"
                        }

                      + resources {
                          + limits   = (known after apply)
                          + requests = {
                              + "cpu"    = "250m"
                              + "memory" = "50Mi"
                            }
                        }
                    }
                }
            }
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so OpenTofu can't guarantee to take exactly these actions if you run "tofu apply" now.
```

Run the OpenTofu **apply command** to deploy the application into Kubernetes:

```shell
$ tofu apply

OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

OpenTofu will perform the following actions:

  # kubernetes_deployment.color will be created
  + resource "kubernetes_deployment" "color" {
      + id               = (known after apply)
      + wait_for_rollout = true

      + metadata {
          + generation       = (known after apply)
          + labels           = {
              + "app"   = "color"
              + "color" = "blue"
            }
          + name             = "color-blue-dep"
          + namespace        = "default"
          + resource_version = (known after apply)
          + uid              = (known after apply)
        }

      + spec {
          + min_ready_seconds         = 0
          + paused                    = false
          + progress_deadline_seconds = 600
          + replicas                  = "3"
          + revision_history_limit    = 10

          + selector {
              + match_labels = {
                  + "app"   = "color"
                  + "color" = "blue"
                }
            }

          + template {
              + metadata {
                  + generation       = (known after apply)
                  + labels           = {
                      + "app"   = "color"
                      + "color" = "blue"
                    }
                  + name             = (known after apply)
                  + resource_version = (known after apply)
                  + uid              = (known after apply)
                }
              + spec {
                  + automount_service_account_token  = true
                  + dns_policy                       = "ClusterFirst"
                  + enable_service_links             = true
                  + host_ipc                         = false
                  + host_network                     = false
                  + host_pid                         = false
                  + hostname                         = (known after apply)
                  + node_name                        = (known after apply)
                  + restart_policy                   = "Always"
                  + scheduler_name                   = (known after apply)
                  + service_account_name             = (known after apply)
                  + share_process_namespace          = false
                  + termination_grace_period_seconds = 30

                  + container {
                      + image                      = "itwonderlab/color"
                      + image_pull_policy          = (known after apply)
                      + name                       = "color-blue"
                      + stdin                      = false
                      + stdin_once                 = false
                      + termination_message_path   = "/dev/termination-log"
                      + termination_message_policy = (known after apply)
                      + tty                        = false

                      + env {
                          + name  = "COLOR"
                          + value = "blue"
                        }

                      + port {
                          + container_port = 8080
                          + protocol       = "TCP"
                        }

                      + resources {
                          + limits   = (known after apply)
                          + requests = {
                              + "cpu"    = "250m"
                              + "memory" = "50Mi"
                            }
                        }
                    }
                }
            }
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  OpenTofu will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

kubernetes_deployment.color: Creating...
kubernetes_deployment.color: Still creating... [10s elapsed]
kubernetes_deployment.color: Creation complete after 15s [id=default/color-blue-dep]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
```

### Check that the Application has been deployed

Check the Kubernetes pods:

```shell
$ kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
color-blue-dep-8656458999-kmfpk   1/1     Running   0          4m10s
color-blue-dep-8656458999-7grbw   1/1     Running   0          4m10s
color-blue-dep-8656458999-9rbjg   1/1     Running   0          4m10s
```

Check an individual pod (deployment). Use the NAME of one of the pods to ask for a detailed description.

```shell
$ kubectl describe pod/color-blue-dep-8656458999-kmfpk
Name:             color-blue-dep-8656458999-kmfpk
Namespace:        default
Priority:         0
Service Account:  default
Node:             xps13/192.168.100.105
Start Time:       Mon, 06 Nov 2023 22:53:51 +0800
Labels:           app=color
                  color=blue
                  pod-template-hash=8656458999
Annotations:      <none>
Status:           Running
IP:               10.42.0.10
IPs:
  IP:           10.42.0.10
Controlled By:  ReplicaSet/color-blue-dep-8656458999
Containers:
  color-blue:
    Container ID:   containerd://8585df64e461979b80886fe73c712d8861c24e11d5c767866592ba9d673669d0
    Image:          itwonderlab/color
    Image ID:       docker.io/itwonderlab/color@sha256:7f17f34b41590a7684b4768b8fc4ea8d3d5f111c37d934c1e5fe5ea3567edbec
    Port:           8080/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Mon, 06 Nov 2023 22:54:00 +0800
    Ready:          True
    Restart Count:  0
    Requests:
      cpu:     250m
      memory:  50Mi
    Environment:
      COLOR:  blue
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-c9df2 (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-c9df2:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   Burstable
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  4m36s  default-scheduler  Successfully assigned default/color-blue-dep-8656458999-kmfpk to xps13
  Normal  Pulling    4m35s  kubelet            Pulling image "itwonderlab/color"
  Normal  Pulled     4m27s  kubelet            Successfully pulled image "itwonderlab/color" in 8.070953604s (8.070960862s including waiting)
  Normal  Created    4m27s  kubelet            Created container color-blue
  Normal  Started    4m27s  kubelet            Started container color-blue
```

### Add a Kubernetes Load Balancer or a NodePort to access the Application

Our *Color* application uses port 8080 to publish its internal web server, Kubernetes exposes that port on each pod (replica) running the application as defined in the containerPort specification for the deployment:

To access the Application from outside the Kubernetes cluster a **Load Balancer** or a **NodePort** service is needed to expose the port. Kubernetes will route service traffic to pods with label keys and values matching this selector specified, in our example the label **app** with a value “**color**” is used as the selector.

```yaml
    ports:
    - containerPort: 8080
      protocol: TCP
```

**Kubernetes Load Balancer Services** are usually cloud infrastructure provider dependant, e.g. AWS or Azure have an external load balancer implementation.

Usually, local Kubernetes implementations don’t have a LoadBalancer service available so instead a NodePort can be used to publish Pods ports, but if using K3s the default Load Balancer implementation is the [Klipper Service Load Balancer](https://github.com/k3s-io/klipper-lb) which can create a local Load Balancer. This tutorial includes both options.

#### Creating a local Kubernetes Load Balancer Service with OpenTofu or Terraform

Using a LoadBalancer is preferred as the Load Balancer gives access to multiple Kubernetes nodes.

K3s provides a load balancer known as **ServiceLB** (formerly Klipper LoadBalancer)

Ports used:

-   **Port 18080**: Port exposed by the LoadBalancer service to receive traffic within the cluster.
-   **Target Port 8080**: Destination port where the traffic received by the Services will be forwarded. Corresponds to the port where the Color application listens for incoming connections.
-   **Node Port 30007**: Port exposed by the LoadBalancer service to receive traffic from outside the cluster. This is the port that is published outside the cluster. Defining the value is optional as Kubernetes will assign a random port.

![Kubernetes tutorial LoadBalancer K8s](https://www.itwonderlab.com/media/tutorials/Kubernetes-OpenTofu-deploy-app/Kubernetes-tutorial-LoadBalancer_K8s.png)

Modify the Terraform file named color\_app.tf that describes the OpenTofu Kubernetes desired state to add a **Load Balancer** service using the above ports.

```hcl
#-------------------------------------------------
# KUBERNETES: Add a LoadBalancer
#-------------------------------------------------

resource "kubernetes_service" "color-service-lb" {
  metadata {
    name = "color-service-lb"
  } //metadata
  spec {
    selector = {
      app = "color"
    } //selector
    session_affinity = "ClientIP"
    port {
        port        = 18080 
        target_port = 8080 
        node_port   = 30007
    } //port
    type = "LoadBalancer"
  } //spec
} //resource
```

#### Creating a local Kubernetes NodePort Service with OpenTofu or Terraform

Use a NodePort when a LoadBalancer is not available.

Ports used in the NodePort:

-   **Port 8080**: Destination port where the traffic received by the Services will be forwarded. Corresponds to the port where the Color application listens for incoming connections.
-   **Node Port 30085**: Port exposed by the NodePort service on each cluster node to receive traffic from outside the cluster. This is the port that is published outside the cluster. Defining the value is optional as Kubernetes will assign a random port.

Modify the Terraform file named color\_app.tf that describes the OpenTofu Kubernetes desired state to add a NodePort service to expose the application web port to the local machine.

```yaml
#-------------------------------------------------
# KUBERNETES: Add a NodePort
#-------------------------------------------------

resource "kubernetes_service" "color-service-np" {
  metadata {
    name = "color-service-np"
  } //metadata
  spec {
    selector = {
      app = "color"
    } //selector
    session_affinity = "ClientIP"
    port {
      port      = 8080 
      node_port = 30085
    } //port
    type = "NodePort"
  } //spec
} //resource
```

Execute again the OpenTofu `plan` and `apply` commands.

Plan:

```shell
$ tofu plan
kubernetes_deployment.color: Refreshing state... [id=default/color-blue-dep]

OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  + create

OpenTofu will perform the following actions:

  # kubernetes_service.color-service-lb will be created
  + resource "kubernetes_service" "color-service-lb" {
      + id                     = (known after apply)
      + status                 = (known after apply)
      + wait_for_load_balancer = true

      + metadata {
          + generation       = (known after apply)
          + name             = "color-service-lb"
          + namespace        = "default"
          + resource_version = (known after apply)
          + uid              = (known after apply)
        }

      + spec {
          + allocate_load_balancer_node_ports = true
          + cluster_ip                        = (known after apply)
          + cluster_ips                       = (known after apply)
          + external_traffic_policy           = (known after apply)
          + health_check_node_port            = (known after apply)
          + internal_traffic_policy           = (known after apply)
          + ip_families                       = (known after apply)
          + ip_family_policy                  = (known after apply)
          + publish_not_ready_addresses       = false
          + selector                          = {
              + "app" = "color"
            }
          + session_affinity                  = "ClientIP"
          + type                              = "LoadBalancer"

          + port {
              + node_port   = 30007
              + port        = 18080
              + protocol    = "TCP"
              + target_port = "8080"
            }
        }
    }

  # kubernetes_service.color-service-np will be created
  + resource "kubernetes_service" "color-service-np" {
      + id                     = (known after apply)
      + status                 = (known after apply)
      + wait_for_load_balancer = true

      + metadata {
          + generation       = (known after apply)
          + name             = "color-service-np"
          + namespace        = "default"
          + resource_version = (known after apply)
          + uid              = (known after apply)
        }

      + spec {
          + allocate_load_balancer_node_ports = true
          + cluster_ip                        = (known after apply)
          + cluster_ips                       = (known after apply)
          + external_traffic_policy           = (known after apply)
          + health_check_node_port            = (known after apply)
          + internal_traffic_policy           = (known after apply)
          + ip_families                       = (known after apply)
          + ip_family_policy                  = (known after apply)
          + publish_not_ready_addresses       = false
          + selector                          = {
              + "app" = "color"
            }
          + session_affinity                  = "ClientIP"
          + type                              = "NodePort"

          + port {
              + node_port   = 30085
              + port        = 8080
              + protocol    = "TCP"
              + target_port = (known after apply)
            }
        }
    }

Plan: 2 to add, 0 to change, 0 to destroy.

─────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so OpenTofu can't guarantee to take exactly these actions if you run "tofu
apply" now.
```

Apply:

```shell
$ tofu apply
...
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
```

### Access the Application with a Web Browser

Check the assigned ports for Load Balancer and/or the NodePort:

```shell
$ kubectl get services
NAME               TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)           AGE
kubernetes         ClusterIP      10.43.0.1       <none>            443/TCP           14h
color-service-lb   LoadBalancer   10.43.233.32    192.168.100.105   18080:30007/TCP   102s
color-service-np   NodePort       10.43.224.107   <none>            8080:30085/TCP    102
```

Open http://192.168.100.105:**30007**/ (for Load Balancer) or http://localhost:**30085**/ (for NodePort) to access the Application.

Review the value of the HostName as it shows which Kubernetes pod (replica) of the Applications is serving the web page.

![Kubernetes Testing Container OpenTofu](https://www.itwonderlab.com/media/tutorials/Kubernetes-OpenTofu-deploy-app/Kubernetes_Testing_Container_OpenTofu.png)

### Destroy the Kubernetes Application using OpenTofu

Run tofu destroy to remove the application replicas and the NodePort.

```shell
$ tofu destroy
kubernetes_service.color-service-np: Refreshing state... [id=default/color-service-np]
kubernetes_service.color-service-lb: Refreshing state... [id=default/color-service-lb]
kubernetes_deployment.color: Refreshing state... [id=default/color-blue-dep]

OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

OpenTofu will perform the following actions:

  # kubernetes_deployment.color will be destroyed
  - resource "kubernetes_deployment" "color" {
      - id               = "default/color-blue-dep" -> null
      - wait_for_rollout = true -> null

      - metadata {
          - annotations      = {} -> null
          - generation       = 1 -> null
          - labels           = {
              - "app"   = "color"
              - "color" = "blue"
            } -> null
          - name             = "color-blue-dep" -> null
          - namespace        = "default" -> null
          - resource_version = "6465" -> null
          - uid              = "74f3f5d6-3a3a-4afc-bc14-1e29d2730e2e" -> null
        }

      - spec {
          - min_ready_seconds         = 0 -> null
          - paused                    = false -> null
          - progress_deadline_seconds = 600 -> null
          - replicas                  = "3" -> null
          - revision_history_limit    = 10 -> null

          - selector {
              - match_labels = {
                  - "app"   = "color"
                  - "color" = "blue"
                } -> null
            }

          - strategy {
              - type = "RollingUpdate" -> null

              - rolling_update {
                  - max_surge       = "25%" -> null
                  - max_unavailable = "25%" -> null
                }
            }

          - template {
              - metadata {
                  - annotations = {} -> null
                  - generation  = 0 -> null
                  - labels      = {
                      - "app"   = "color"
                      - "color" = "blue"
                    } -> null
                }
              - spec {
                  - active_deadline_seconds          = 0 -> null
                  - automount_service_account_token  = true -> null
                  - dns_policy                       = "ClusterFirst" -> null
                  - enable_service_links             = true -> null
                  - host_ipc                         = false -> null
                  - host_network                     = false -> null
                  - host_pid                         = false -> null
                  - node_selector                    = {} -> null
                  - restart_policy                   = "Always" -> null
                  - scheduler_name                   = "default-scheduler" -> null
                  - share_process_namespace          = false -> null
                  - termination_grace_period_seconds = 30 -> null

                  - container {
                      - args                       = [] -> null
                      - command                    = [] -> null
                      - image                      = "itwonderlab/color" -> null
                      - image_pull_policy          = "Always" -> null
                      - name                       = "color-blue" -> null
                      - stdin                      = false -> null
                      - stdin_once                 = false -> null
                      - termination_message_path   = "/dev/termination-log" -> null
                      - termination_message_policy = "File" -> null
                      - tty                        = false -> null

                      - env {
                          - name  = "COLOR" -> null
                          - value = "blue" -> null
                        }

                      - port {
                          - container_port = 8080 -> null
                          - host_port      = 0 -> null
                          - protocol       = "TCP" -> null
                        }

                      - resources {
                          - limits   = {} -> null
                          - requests = {
                              - "cpu"    = "250m"
                              - "memory" = "50Mi"
                            } -> null
                        }
                    }
                }
            }
        }
    }

  # kubernetes_service.color-service-lb will be destroyed
  - resource "kubernetes_service" "color-service-lb" {
      - id                     = "default/color-service-lb" -> null
      - status                 = [
          - {
              - load_balancer = [
                  - {
                      - ingress = [
                          - {
                              - hostname = ""
                              - ip       = "192.168.100.105"
                            },
                        ]
                    },
                ]
            },
        ] -> null
      - wait_for_load_balancer = true -> null

      - metadata {
          - annotations      = {} -> null
          - generation       = 0 -> null
          - labels           = {} -> null
          - name             = "color-service-lb" -> null
          - namespace        = "default" -> null
          - resource_version = "6510" -> null
          - uid              = "2675c3fc-b414-4fe0-9106-f60ca0457619" -> null
        }

      - spec {
          - allocate_load_balancer_node_ports = true -> null
          - cluster_ip                        = "10.43.184.189" -> null
          - cluster_ips                       = [
              - "10.43.184.189",
            ] -> null
          - external_ips                      = [] -> null
          - external_traffic_policy           = "Cluster" -> null
          - health_check_node_port            = 0 -> null
          - internal_traffic_policy           = "Cluster" -> null
          - ip_families                       = [
              - "IPv4",
            ] -> null
          - ip_family_policy                  = "SingleStack" -> null
          - load_balancer_source_ranges       = [] -> null
          - publish_not_ready_addresses       = false -> null
          - selector                          = {
              - "app" = "color"
            } -> null
          - session_affinity                  = "ClientIP" -> null
          - type                              = "LoadBalancer" -> null

          - port {
              - node_port   = 30007 -> null
              - port        = 18080 -> null
              - protocol    = "TCP" -> null
              - target_port = "8080" -> null
            }

          - session_affinity_config {
              - client_ip {
                  - timeout_seconds = 10800 -> null
                }
            }
        }
    }

  # kubernetes_service.color-service-np will be destroyed
  - resource "kubernetes_service" "color-service-np" {
      - id                     = "default/color-service-np" -> null
      - status                 = [
          - {
              - load_balancer = [
                  - {
                      - ingress = []
                    },
                ]
            },
        ] -> null
      - wait_for_load_balancer = true -> null

      - metadata {
          - annotations      = {} -> null
          - generation       = 0 -> null
          - labels           = {} -> null
          - name             = "color-service-np" -> null
          - namespace        = "default" -> null
          - resource_version = "6490" -> null
          - uid              = "0e8670c6-2f9a-47f0-8129-20a048ab18d0" -> null
        }

      - spec {
          - allocate_load_balancer_node_ports = true -> null
          - cluster_ip                        = "10.43.239.131" -> null
          - cluster_ips                       = [
              - "10.43.239.131",
            ] -> null
          - external_ips                      = [] -> null
          - external_traffic_policy           = "Cluster" -> null
          - health_check_node_port            = 0 -> null
          - internal_traffic_policy           = "Cluster" -> null
          - ip_families                       = [
              - "IPv4",
            ] -> null
          - ip_family_policy                  = "SingleStack" -> null
          - load_balancer_source_ranges       = [] -> null
          - publish_not_ready_addresses       = false -> null
          - selector                          = {
              - "app" = "color"
            } -> null
          - session_affinity                  = "ClientIP" -> null
          - type                              = "NodePort" -> null

          - port {
              - node_port   = 30085 -> null
              - port        = 8080 -> null
              - protocol    = "TCP" -> null
              - target_port = "8080" -> null
            }

          - session_affinity_config {
              - client_ip {
                  - timeout_seconds = 10800 -> null
                }
            }
        }
    }

Plan: 0 to add, 0 to change, 3 to destroy.

Do you really want to destroy all resources?
  OpenTofu will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

kubernetes_service.color-service-np: Destroying... [id=default/color-service-np]
kubernetes_service.color-service-lb: Destroying... [id=default/color-service-lb]
kubernetes_deployment.color: Destroying... [id=default/color-blue-dep]
kubernetes_deployment.color: Destruction complete after 0s
kubernetes_service.color-service-np: Destruction complete after 0s
kubernetes_service.color-service-lb: Destruction complete after 0s

Destroy complete! Resources: 3 destroyed.
```
