---
title: "Exposing StatefulSets in Kubernetes"
source: "https://www.tigera.io/blog/exposing-statefulsets-in-kubernetes/"
description: "Learn about Kubernetes StatefulSets and How you can properly expose a StatefulSet externally."
---

[Technical Blog](https://www.tigera.io/category/technical-blog/)

# Exposing StatefulSets in Kubernetes

By [Bob Killen](https://www.tigera.io/blog/author/bob-killen/) on Jan 04, 2019 • 4 min read

By far one of the most common questions we receive in the monthly Kubernetes Office Hours is: *“How do you properly expose a StatefulSet externally?”*Usually this question is asked in the form of: *“How do I allow connections to a particular instance of Mongo or Postgres from outside the cluster?”*

Fear not! This isn’t as challenging as it seems. If you’d like the [TL;DR answer](http://medium.com/p/698730fb92a1#6fc0), just skip down to the bottom of this post. If you’d like a little bit more of an explanation, continue reading.

Following the *normal* practice of having a single Service point to all instances of your application doesn’t work when you want to query a specific instance directly. This is already handled internally with the[Headless Service](http://kubernetes.io/docs/concepts/services-networking/service/#headless-services) you create alongside the StatefulSet. The created ervice will **not**be given a `clusterIP`, but will instead simply include a list of `Endpoints`. These `Endpoints` are then used to generate instance-specific DNS records in the form of: `<StatefulSet>-<Ordinal>.<Service>.<Namespace>.svc.cluster.local`e.g., `app-0.myapp.default.svc.cluster.local`.

Now, how do you replicate that when you want to expose it externally as a `LoadBalancer` Service? The Kubernetes developers have thought of this already. 😉 Each Pod in the StatefulSet has it’s own label that includes its [generated Pod identity](http://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-identity): `statefulset.kubernetes.io/pod-name`. You know what that means? It means we can use it in a Service Selector!

```
`apiVersion: v1 kind: Service metadata: name: app-0 spec: type: LoadBalancer selector: statefulset.kubernetes.io/pod-name: app-0 ports: - protocol: TCP port: 80 targetPort: 80`
```

While the headless service might not take care of all our needs, we can create additional Services that point to the individual Pods of the StatefulSet. Generally speaking, StatefulSets aren’t being scaled up or down without some careful planning, so it’s worth the small amount of extra effort to create some additional Services.

With that, we will be able to expose our instances individually, but we can actually improve on this a bit.

When an external Kubernetes Service (`NodePort` and `LoadBalancer`) is created, it will open a port on **all**nodes in the cluster that can be used for reaching our exposed Service. This is great in a general sense, when you spread traffic across many instances of the same Pod, but not so much when you want to direct traffic to a singular one. It’d be better if we could direct traffic directly to the node running the specific instance of our StatefulSet. Thankfully, the devs thought of this scenario too!

You can route traffic directly to the node by setting the Service attribute [externalTrafficPolicy](http://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-type-loadbalancer)[to](http://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-type-loadbalancer)[Local](http://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-type-loadbalancer). This forces the Service to only proxy traffic for local endpoints. Meaning, any node in the cluster that is **not** running the specific instance of the Pod will **not** proxy traffic, and will therefore fail any external healthcheck.

```
`apiVersion: v1 kind: Service metadata: name: app-0 spec: type: LoadBalancer externalTrafficPolicy: Local selector: statefulset.kubernetes.io/pod-name: app-0 ports: - protocol: TCP port: 80 targetPort: 80`
```

This may seem a little odd, but it’s a very useful and generic method that can play nice with the various cloud providers and their load balancers. By not proxying the traffic, the nodes will automatically be removed from the load balancer pool for that external Service forcing traffic to only go to the node running that specific instance.

With that, you should be able to expose StatefulSets externally and remove the unnecessary extra hop when routing traffic to a single instance.

## Bonus:

If you don’t want to have to manage creating an additional Service per Pod for your StatefulSet, it can be automated using a powerful cluster automation tool called [metacontroller](http://metacontroller.app/). It allows you to create your own simple controllers where you can trigger additional scripts or actions. One of their examples automates the Service creation for StatefulSet instances using their[DecoratorController](http://github.com/GoogleCloudPlatform/metacontroller/tree/master/examples/service-per-pod).

## TL;DR:

- StatefulSet Pods have the label: `statefulset.kubernetes.io/pod-name`which contains their generated name (`<StatefulSet Name>-<Ordinal>`). You can create individual Services for each instance that use that label as their selector to expose the individual instances of the StatefulSet.

- To remove a potential extra hop, create the Services with the attribute `externalTrafficPolicy` and set it to `Local`.

- You can automate this process with a nifty cluster automation tool [metacontroller](http://metacontroller.app/).

This article originated from [http://itnext.io/exposing-statefulsets-in-kubernetes-698730fb92a1](http://itnext.io/exposing-statefulsets-in-kubernetes-698730fb92a1)

Bob Killen is a Tigera guest blogger. He is Research Cloud Administrator at University of Michigan, CNCF Ambassador and Kubernetes & OSS Advocate.

————————————————-

[**Free Online Training**](https://www.tigera.io/events/)

Access Live and On-Demand Kubernetes Tutorials

[**Calico Enterprise – Free Trial**](https://www.calicocloud.io/home)

Solve Common Kubernetes Roadblocks and Advance Your Enterprise Adoption

[How-To](https://www.tigera.io/tags/how-to/)[Open Source](https://www.tigera.io/tags/open-source/)

## Related posts

[![From Merged PR to Governed Pod: Automating AI Agent Deployment on Kubernetes](https://www.tigera.io/app/uploads/2026/10/From-Merged-PR-to-Governed-Pod-featured.png)](https://www.tigera.io/blog/from-merged-pr-to-governed-pod-automating-ai-agent-deployment-on-kubernetes/)

#### [From Merged PR to Governed Pod: Automating AI Agent Deployment on Kubernetes](https://www.tigera.io/blog/from-merged-pr-to-governed-pod-automating-ai-agent-deployment-on-kubernetes/)

By [Alister Baroi](https://www.tigera.io/blog/author/alister-baroi/)
on Oct 13, 2026

In short, automating the AI agent lifecycle is this year’s pitch: Google Cloud’s walkthrough of 30th July 2026 turns the whole thing into a conversation with a coding agent. On Kubernetes the deploy half of...

[Read more](https://www.tigera.io/blog/from-merged-pr-to-governed-pod-automating-ai-agent-deployment-on-kubernetes/)

[![Save the Address, Save the Cloud: A Hands-on KubeVirt Live Migration Workshop](https://www.tigera.io/app/uploads/2026/07/Save-the-Address-Save-the-Cloud-A-Hands-on-KubeVirt-Live-Migration-Workshop.png)](https://www.tigera.io/blog/save-the-address-save-the-cloud-a-hands-on-kubevirt-live-migration-workshop/)

#### [Save the Address, Save the Cloud: A Hands-on KubeVirt Live Migration Workshop](https://www.tigera.io/blog/save-the-address-save-the-cloud-a-hands-on-kubevirt-live-migration-workshop/)

By [Reza Ramezanpour](https://www.tigera.io/blog/author/rezar/)
on Jul 9, 2026

In the previous post in this series, we covered why Virtual Machine (VM) Live Migration in Kubernetes is difficult: a VM’s IP is its identity, and the “new” VM on the destination node has to...

[Read more](https://www.tigera.io/blog/save-the-address-save-the-cloud-a-hands-on-kubevirt-live-migration-workshop/)

[![What’s new in Calico: Spring 2026 Release](https://www.tigera.io/app/uploads/2026/06/Whats-New-in-Calico-NEW-TEMPLATE-2026.png)](https://www.tigera.io/blog/whats-new-in-calico-spring-2026-release/)

[Company Blog](https://www.tigera.io/category/company-blog/)

#### [What’s new in Calico: Spring 2026 Release](https://www.tigera.io/blog/whats-new-in-calico-spring-2026-release/)

By [Veronika Smolik](https://www.tigera.io/blog/author/veronika-smolik/)
on Jun 2, 2026

Kubernetes has come a long way since its debut in 2014. It’s gone from running a couple of containerized microservices to orchestrating fleets of production workloads spanning everything from AI agents to full scale VMs...

[Read more](https://www.tigera.io/blog/whats-new-in-calico-spring-2026-release/)

<!-- plugin=object-cache-pro client=phpredis metric#hits=6138 metric#misses=17 metric#hit-ratio=99.7 metric#bytes=2258754 metric#prefetches=0 metric#store-reads=410 metric#store-writes=35 metric#store-hits=423 metric#store-misses=6 metric#sql-queries=37 metric#ms-total=969.68 metric#ms-cache=55.26 metric#ms-cache-avg=0.1245 metric#ms-cache-ratio=5.7 sample#redis-hits=93731101 sample#redis-misses=9720719 sample#redis-hit-ratio=90.6 sample#redis-ops-per-sec=233 sample#redis-evicted-keys=0 sample#redis-used-memory=81199032 sample#redis-used-memory-rss=79773696 sample#redis-memory-fragmentation-ratio=1.0 sample#redis-connected-clients=2 sample#redis-tracking-clients=0 sample#redis-rejected-connections=0 sample#redis-keys=21831 -->
