Kubernetes
TCP port
nginx-Ingress
cluster networking
Kubernetes ingress

Exposing a TCP port out of cluster in Kubernetes using nginx-Ingress

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Standard Kubernetes Ingress objects are for HTTP and HTTPS routing, not arbitrary TCP streams. If you want the NGINX Ingress Controller to expose a raw TCP service such as PostgreSQL, Redis, or a custom binary protocol, you normally configure a dedicated TCP services mapping and expose the corresponding port on the controller Service.

Understand the difference between HTTP ingress and TCP forwarding

An Ingress resource can route by host and path because HTTP has those concepts. Raw TCP does not. That is why a normal YAML object like this does not solve the problem for non-HTTP traffic:

yaml
kind: Ingress

For TCP, the NGINX controller needs a port-to-service mapping. The controller then listens on a specific external port and forwards the stream to a Kubernetes Service.

Create the internal Service first

Suppose a PostgreSQL instance is already reachable inside the cluster on service postgres port 5432:

yaml
1apiVersion: v1
2kind: Service
3metadata:
4  name: postgres
5  namespace: data
6spec:
7  selector:
8    app: postgres
9  ports:
10    - port: 5432
11      targetPort: 5432

This Service is what the ingress controller will forward traffic to.

Configure the NGINX TCP mapping

The NGINX Ingress Controller typically reads TCP mappings from a ConfigMap. A common example looks like this:

yaml
1apiVersion: v1
2kind: ConfigMap
3metadata:
4  name: tcp-services
5  namespace: ingress-nginx
6data:
7  "5432": "data/postgres:5432"

This means:

  • listen on external port 5432 on the ingress controller
  • forward traffic to service postgres
  • use port 5432 in namespace data

Your controller deployment or chart must also be told to use that ConfigMap. In many installations this is wired through a controller argument or Helm value.

Expose the controller port too

The mapping alone is not enough. The ingress controller Service must also expose the same TCP port:

yaml
1apiVersion: v1
2kind: Service
3metadata:
4  name: ingress-nginx-controller
5  namespace: ingress-nginx
6spec:
7  type: LoadBalancer
8  ports:
9    - name: http
10      port: 80
11      targetPort: 80
12    - name: https
13      port: 443
14      targetPort: 443
15    - name: postgres
16      port: 5432
17      targetPort: 5432

If the Service does not expose 5432, outside clients still cannot reach it even though the controller knows the backend mapping.

Know when not to use NGINX ingress for TCP

Sometimes the simpler solution is to expose the backend service directly with LoadBalancer or NodePort. Using NGINX for TCP is useful when you already rely on the controller as the external entry point and want centralized exposure. It is not automatically the best option for every database or stateful protocol.

A direct Service can be easier to reason about:

yaml
spec:
  type: LoadBalancer

If you only need one TCP service, direct exposure may be cleaner than layering it through the ingress controller.

Common Pitfalls

The most common mistake is trying to use a normal HTTP Ingress resource for a non-HTTP protocol. That resource type does not express raw TCP routing semantics.

Another mistake is creating the TCP ConfigMap entry but forgetting to expose the same port on the ingress controller Service. Both pieces are required.

Namespace mismatches are also common. In the mapping string, the referenced service name, namespace, and port must all match the actual backend Service exactly.

Finally, do not forget cloud firewalls and load balancer rules. Even a correct Kubernetes configuration can still be unreachable if the environment blocks the port externally.

Summary

  • Standard Kubernetes Ingress is for HTTP and HTTPS, not arbitrary TCP streams.
  • With NGINX Ingress Controller, expose TCP by mapping an external port to a backend Service.
  • Add the TCP mapping in the controller's ConfigMap and expose the same port on the controller Service.
  • For simple cases, a direct LoadBalancer Service may be easier than routing TCP through ingress.
  • Verify namespace, service name, port wiring, and external firewall rules together.

Course illustration
Course illustration

All Rights Reserved.