Skip to main content

TCP Services

Overview

Otoroshi is primarily an HTTP reverse proxy and API gateway, but not all traffic in a modern infrastructure speaks HTTP. Databases (PostgreSQL, MySQL, MongoDB), message brokers (MQTT, AMQP), remote shells (SSH), custom binary protocols, and many other services communicate over raw TCP. Without TCP proxying support, you would need a separate tool to manage this non-HTTP traffic, fragmenting your infrastructure and multiplying operational overhead.

TCP services solve this problem by letting you consolidate both HTTP and non-HTTP traffic through a single platform. A TCP service listens on a dedicated port and forwards raw TCP byte streams to one or more backend targets, without any HTTP-level processing. There are no headers, no request/response semantics, no plugins, and no content transformation -- just transparent byte forwarding between the client and the backend.

How TCP services differ from HTTP routes

HTTP routes in Otoroshi operate at the application layer (Layer 7): they parse HTTP requests, apply plugins, transform headers, enforce rate limits, and more. TCP services operate at the transport layer (Layer 4): they accept a TCP connection and pipe bytes to a backend, with no awareness of the application protocol flowing through them. This makes TCP services suitable for any protocol that runs over TCP, but it also means that HTTP-specific features (authentication plugins, header manipulation, traffic policies) are not available on TCP services.

SNI-based routing

A key feature of TCP services is the ability to route connections using TLS Server Name Indication (SNI). During the TLS handshake, the client sends the hostname it is trying to reach. Otoroshi can inspect this hostname and use it to select different backend targets, even when multiple services share the same listening port. This works in two modes:

  • TLS termination (Enabled): Otoroshi terminates TLS, reads the SNI hostname, selects the appropriate certificate from its dynamic certificate store, and opens a new connection (optionally with TLS) to the matched backend.
  • TLS passthrough (PassThrough): Otoroshi extracts the SNI hostname from the initial TLS ClientHello packet without terminating TLS, then forwards the entire encrypted connection to the matched backend. The backend handles TLS termination. This is useful when backends must manage their own certificates or when end-to-end encryption is required.

When SNI is not enabled, each TCP service must bind to a unique port. When SNI is enabled, multiple routing rules can coexist on the same port, each matching a different domain pattern via regex.

Key features

  • Multiple targets with round-robin load balancing: each routing rule can define several backend targets; Otoroshi distributes connections across them.
  • Flexible TLS modes: disabled (plain TCP), enabled (Otoroshi terminates TLS), or passthrough (TLS forwarded to the backend).
  • Mutual TLS (mTLS): optionally require or request client certificates for TCP connections.
  • SNI-based routing: route connections to different backends based on the domain name in the TLS handshake, allowing multiple logical services on a single port.
  • Default forwarding: when SNI is enabled but no rule matches, optionally forward to a default target instead of rejecting the connection.
  • Analytics: TCP connections generate events that flow through the same analytics pipeline as HTTP traffic.

When to use TCP services vs HTTP routes

Use HTTP routes when your traffic is HTTP or HTTPS and you need application-level features such as authentication, rate limiting, header transformation, or plugin processing. Use TCP services when you need to proxy non-HTTP protocols (databases, SSH, MQTT, custom TCP protocols) or when you need transparent TLS passthrough without Otoroshi inspecting the application-layer content.

UI page

You can find all TCP services here

Properties

PropertyTypeDefaultDescription
idstringUnique identifier
namestringDisplay name of the TCP service
descriptionstringDescription
enabledbooleantrueWhether the service is active
portnumberThe listening port for incoming TCP connections
interfacestring0.0.0.0Network interface the service listens on
tagsarray of string[]Tags
metadataobject{}Key/value metadata
tlsstringDisabledTLS mode (see below)
clientAuthstringNonemTLS client authentication mode (see below)
sniobjectSNI configuration (see below)
rulesarray of object[]Routing rules (see below)

TLS modes

ModeDescription
DisabledNo TLS. TCP traffic is proxied as-is
PassThroughThe target exposes TLS. Traffic passes through Otoroshi without termination
EnabledOtoroshi terminates TLS and selects the certificate based on SNI

Client authentication

ModeDescription
NoneNo client certificate required
WantClient certificate is requested but not required
NeedValid client certificate is mandatory

SNI (Server Name Indication)

SNI allows Otoroshi to select different targets based on the hostname in the TLS handshake.

PropertyTypeDefaultDescription
enabledbooleanfalseEnable SNI-based routing
forwardIfNoMatchbooleanfalseForward to a default target if no SNI match is found
forwardsTo.hoststringDefault target hostname (when no SNI match)
forwardsTo.ipstringDefault target IP address
forwardsTo.portnumberDefault target port
forwardsTo.tlsbooleanfalseUse TLS to connect to the default target

Rules

For any listening TCP proxy, you can define multiple routing rules based on SNI or extracted HTTP host (when proxying HTTP).

PropertyTypeDescription
domainstringRegex pattern to match against the domain name
targetsarray of objectList of targets for this rule
targets[].hoststringTarget hostname
targets[].ipstringTarget IP address
targets[].portnumberTarget port
targets[].tlsbooleanUse TLS to connect to this target

JSON example

{
"id": "tcp_service_postgres",
"name": "PostgreSQL proxy",
"description": "TCP proxy for the production PostgreSQL cluster",
"enabled": true,
"port": 5432,
"interface": "0.0.0.0",
"tls": "Enabled",
"clientAuth": "None",
"sni": {
"enabled": true,
"forwardIfNoMatch": true,
"forwardsTo": {
"host": "postgres-primary.internal",
"ip": null,
"port": 5432,
"tls": false
}
},
"rules": [
{
"domain": ".*read.*",
"targets": [
{
"host": "postgres-replica.internal",
"ip": null,
"port": 5432,
"tls": false
}
]
}
],
"tags": ["database", "production"],
"metadata": {}
}

Admin API

GET    /api/tcp/services           # List all TCP services
POST /api/tcp/services # Create a TCP service
GET /api/tcp/services/:id # Get a TCP service
PUT /api/tcp/services/:id # Update a TCP service
DELETE /api/tcp/services/:id # Delete a TCP service
PATCH /api/tcp/services/:id # Partially update a TCP service