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
| Flag | Short | Description |
|---|---|---|
| --port | — | The port that the service should serve on |
| --target-port | — | The port on the container that the service should direct traffic to |
| --type | — | Type of service: ClusterIP (default), NodePort, LoadBalancer, ExternalName |
| --protocol | — | The network protocol for the service: TCP (default), UDP, SCTP |
| --name | — | The name of the newly created service |
| --selector | — | A 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=8080Expose a deployment as a NodePort service
kubectl expose deployment my-app --port=80 --type=NodePortExpose a deployment as a LoadBalancer
kubectl expose deployment my-app --port=443 --target-port=8443 --type=LoadBalancerExpose a pod directly
kubectl expose pod my-pod --port=80 --name=my-pod-serviceExpose with a custom service name
kubectl expose deployment my-app --port=80 --name=my-app-public --type=LoadBalancerGenerate service YAML without creating it
kubectl expose deployment my-app --port=80 --dry-run=client -o yamlWhen 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
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.