kubectl expose

Expose a resource as a new Kubernetes Service. Supports pods, deployments, replica sets, and replication controllers.

kubectl expose [TYPE] [NAME] --port=PORT [flags]

Common Flags

FlagShortDescription
--portThe port that the service should serve on
--target-portThe port on the container that the service should direct traffic to
--typeType of service: ClusterIP (default), NodePort, LoadBalancer, ExternalName
--protocolThe network protocol for the service: TCP (default), UDP, SCTP
--nameThe name of the newly created service
--selectorA label selector to use for this service, overriding the one from the source resource

Examples

Expose a deployment as a ClusterIP service

kubectl expose deployment my-app --port=80 --target-port=8080

Expose a deployment as a NodePort service

kubectl expose deployment my-app --port=80 --type=NodePort

Expose a deployment as a LoadBalancer

kubectl expose deployment my-app --port=443 --target-port=8443 --type=LoadBalancer

Expose a pod directly

kubectl expose pod my-pod --port=80 --name=my-pod-service

Expose with a custom service name

kubectl expose deployment my-app --port=80 --name=my-app-public --type=LoadBalancer

Generate service YAML without creating it

kubectl expose deployment my-app --port=80 --dry-run=client -o yaml

When to Use kubectl expose

kubectl expose is the quickest way to make a workload accessible over the network. It creates a Service that routes traffic to pods matching a label selector derived from the source resource. For production services, you will typically define Services in YAML manifests, but kubectl expose is invaluable for quick testing and generating templates.

Service Types

Kubernetes supports four service types, each building on the previous:

# ClusterIP (default) — internal access only
kubectl expose deployment my-app --port=80 --target-port=8080

# NodePort — accessible on every node at a static port
kubectl expose deployment my-app --port=80 --target-port=8080 --type=NodePort

# LoadBalancer — provisions an external load balancer
kubectl expose deployment my-app --port=80 --target-port=8080 --type=LoadBalancer

# You can also create ExternalName services, but not through expose

ClusterIP assigns an internal IP that is reachable only from within the cluster. This is appropriate for backend services that do not need external access.

NodePort opens a port (30000-32767) on every node in the cluster. Traffic to any node on that port is forwarded to the service. This works without a cloud provider but exposes a high port number.

LoadBalancer requests an external load balancer from the cloud provider. It provisions a public IP and routes traffic through the cloud provider's infrastructure.

Port Mapping

Understanding port mapping is critical for correct service configuration:

# Container listens on 8080, service exposes on 80
kubectl expose deployment my-app --port=80 --target-port=8080

# Same port for both service and container
kubectl expose deployment my-app --port=3000

# Multiple ports require YAML manifests (expose only supports single port)

When --target-port is omitted, it defaults to the value of --port. This is correct when the container listens on the same port you want the service to expose.

Verifying the Service

After creating a service, verify it is routing correctly:

# Check the service was created
kubectl get svc my-app

# Verify endpoints exist (pods are backing the service)
kubectl get endpoints my-app

# Describe for full details
kubectl describe svc my-app

# Test connectivity from within the cluster
kubectl run test --image=curlimages/curl -it --rm --restart=Never -- \
  curl http://my-app.default.svc.cluster.local

If the Endpoints list is empty, the service selector does not match any running pods. Check labels on both the service and the pods.

Generating Service Manifests

Use expose with dry-run to generate YAML that you can customize:

# Generate a service manifest
kubectl expose deployment my-app --port=80 --target-port=8080 \
  --type=ClusterIP --dry-run=client -o yaml > service.yaml

You can then edit the generated manifest to add multiple ports, session affinity, annotations for cloud provider configuration, or other settings not available through command-line flags.

Service DNS

Every service gets a DNS entry in the cluster. The format is <service-name>.<namespace>.svc.cluster.local:

# From a pod in any namespace, access the service
curl http://my-app.default.svc.cluster.local

# From the same namespace, short name works
curl http://my-app

# SRV records for named ports
dig _http._tcp.my-app.default.svc.cluster.local SRV

Common Patterns

# Expose a pod for quick testing
kubectl run web --image=nginx --port=80
kubectl expose pod web --port=80

# Expose and immediately get the NodePort
kubectl expose deployment my-app --port=80 --type=NodePort
kubectl get svc my-app -o jsonpath='{.spec.ports[0].nodePort}'

# Expose a deployment in a specific namespace
kubectl expose deployment my-app --port=80 -n production

Limitations

kubectl expose creates single-port services. For multi-port services, headless services, or services with advanced configuration like session affinity, external traffic policy, or topology-aware routing, define the service in a YAML manifest. The expose command is a convenience tool for common cases, not a replacement for declarative service definitions.

Interview Questions About This Command

What are the differences between ClusterIP, NodePort, and LoadBalancer service types?
ClusterIP is internal-only (default). NodePort exposes on a static port on every node (30000-32767). LoadBalancer provisions an external load balancer (cloud provider required). Each type builds on the previous.
How does kubectl expose determine which pods to route traffic to?
It copies the label selector from the source resource (deployment, pod, etc.) and uses it as the service selector. You can override this with the --selector flag.
What is the difference between port and targetPort?
port is what the service listens on. targetPort is the port on the container receiving the traffic. They can be different, allowing the service to abstract the container's actual listening port.

Common Mistakes

  • Forgetting to specify --target-port when the container listens on a different port than the service port.
  • Expecting NodePort or LoadBalancer services to work in local clusters (like minikube) without additional configuration like minikube tunnel.
  • Not checking that the label selector on the service matches the pods, resulting in no endpoints.

Related Commands