Skip to main content

TCP

Forward

Synopsis

Creates a target that forwards log data to a remote TCP endpoint. Supports delimiter and octet-count framing, TLS encryption, connection pooling with keep-alive, batch delivery, and retry logic.

Schema

- name: <string>
description: <string>
type: tcp
pipelines: <pipeline[]>
status: <boolean>
properties:
address: <string>
port: <numeric>
framing: <string>
line_delimiter: <string>
timeout: <numeric>
batch_size: <numeric>
reconnect_interval: <numeric>
max_retries: <numeric>
retry_delay: <numeric>
field_format: <string>
tls:
status: <boolean>
verify: <boolean>
server_name: <string>
cert_name: <string>
key_name: <string>
min_tls_version: <string>
max_tls_version: <string>
debug:
status: <boolean>
dont_send_logs: <boolean>

Configuration

FieldRequiredDefaultDescription
nameYTarget name
descriptionN-Optional description
typeYMust be tcp
pipelinesN-Optional post-processor pipelines
statusNtrueEnable/disable the target

Connection

FieldRequiredDefaultDescription
addressYRemote server IP address or hostname
portYRemote server port (1-65535)
timeoutN30Connection timeout in seconds
reconnect_intervalN10Accepted, but has no effect on delivery in the current release. Retries are paced by retry_delay

TCP

FieldRequiredDefaultDescription
framingN"delimiter"Framing mode: delimiter or octet
line_delimiterN"\n"Line separator appended to each message in delimiter framing
batch_sizeN1000Maximum events buffered before flush

Retry

FieldRequiredDefaultDescription
max_retriesN3Retries after a failed batch (total attempts = max_retries + 1). 0 is read as unset and gives the default of 3
retry_delayN1Starting point in seconds for the wait before a retry. 0 is read as unset and gives the default of 1

TLS

FieldRequiredDefaultDescription
tls.statusNfalseEnable TLS encryption

TLS material is resolved through the shared client builder, so these keys mean the same thing on every target that uses it. They are nested under a tls: block.

FieldRequiredDefaultDescription
tls.verifyNtrueVerify the server certificate. On by default — set it to false only to accept an untrusted certificate, and only where you control the network path
tls.server_nameN-SNI hostname override for the handshake. Use it when the certificate's name does not match the address you connect to
tls.ca_nameN-CA bundle used to verify the server certificate. When unset the host trust store is used; when set it replaces the host trust store rather than adding to it
tls.cert_nameN*-Client certificate, for mutual TLS
tls.key_nameN*-Client private key, for mutual TLS
tls.passphraseN-Passphrase for an encrypted client private key
tls.min_tls_versionNtls1.2Lowest protocol version accepted (tls1.0, tls1.1, tls1.2, tls1.3)
tls.max_tls_versionNtls1.3Highest protocol version accepted (tls1.0, tls1.1, tls1.2, tls1.3)

* cert_name and key_name are individually optional but must be supplied together — a certificate without its key, or a key without its certificate, is a configuration error.

warning

When providing tls.cert_name, you must also provide tls.key_name and vice versa.

Normalization

FieldRequiredDefaultDescription
field_formatN-Data normalization format. See applicable Normalization section

Scheduling

See Scheduling and Pool Behavior for interval and cron fields shared by all targets.

Debug Options

FieldRequiredDefaultDescription
debug.statusNfalseEnable debug logging
debug.dont_send_logsNfalseProcess logs but don't send to target (testing)

Details

The TCP target buffers events in memory and delivers them in batches over a persistent TCP connection.

Framing modes determine how individual messages are delimited in the TCP stream:

  • Delimiter (default) appends line_delimiter (default \n) to each message before concatenation. Compatible with most line-based log receivers.
  • Octet prepends the byte length of each message per RFC 5425 (e.g., 42 <message>). Used with syslog-over-TLS receivers that expect length-prefixed framing.

Connection pooling maintains a per-worker TCP connection. Each worker's connection is tested for liveness before each write and automatically recreated on failure. TCP keep-alive is enabled with a 30-second interval.

Batch delivery accumulates events up to batch_size, then concatenates all framed messages into a single write. If delivery fails, the target retries up to max_retries times. Each wait is chosen at random up to a bound that starts from retry_delay, doubles with every attempt, and is capped at 30 seconds. reconnect_interval does not change this in the current release.

Examples

Basic

Forwarding logs to a remote TCP endpoint with default settings...

targets:
- name: tcp_forwarder
type: tcp
properties:
address: "192.168.1.100"
port: 514

With Batching

Configuring batch size, timeout, and retries for high-volume forwarding...

targets:
- name: tcp_high_volume
type: tcp
properties:
address: "logs.example.com"
port: 5000
batch_size: 5000
timeout: 60
max_retries: 5
retry_delay: 2

Octet Framing

Using RFC 5425 octet-count framing for syslog-over-TCP receivers...

targets:
- name: tcp_octet
type: tcp
properties:
address: "syslog.example.com"
port: 601
framing: "octet"

Secure (TLS)

Forwarding logs over TLS with client certificate authentication...

targets:
- name: tcp_tls
type: tcp
properties:
address: "secure-logs.example.com"
port: 6514
framing: "octet"
tls:
status: true
verify: true
server_name: "secure-logs.example.com"
cert_name: "client-cert.pem"
key_name: "client-key.pem"
min_tls_version: "tls1.2"

With Field Normalization

Normalizing fields to Elastic Common Schema before forwarding...

targets:
- name: tcp_ecs
type: tcp
properties:
address: "192.168.1.100"
port: 5000
field_format: "ecs"

Troubleshooting

This section covers the errors you are most likely to see with the tcp target, what causes each one, and how to fix it.

Where to look:

  • Director logs. Target errors are tagged with the target name and carry "Section":"SenderPool". The part after Reason: or after the last colon is the actual cause.
  • The target's connection status in the web interface. It shows the same reason as the log line, prefixed with connection failed for <target name>:.

See Target Delivery Errors for how Director logs and retries target failures.

warning

The receiver is not contacted while the target starts. A closed port, an untrusted certificate, or a client certificate the receiver refuses first appears when a batch is delivered. Until then the connection status shows connected. If data does not arrive, look for execute() failed lines carrying the target name, not only for Failed to reinitialize.

What the receiver needs

PrerequisiteWhen it appliesError when it is missing
A process accepting TCP connections on address and port, reachable from every Director hostAlwaysfailed to connect to ...: connect: connection refused
A network path that passes the connection instead of dropping itAlwaysfailed to connect to ...: i/o timeout
Resolution of address on the Director hostaddress is a hostnameinvalid address: lookup ...: no such host when the configuration is read, lookup ...: no such host later
A receiver certificate that chains to a trusted authority and carries the address you connect totls.status: true with the default tls.verify: truex509: certificate signed by unknown authority, x509: certificate is valid for ..., not ...
A client certificate and key issued by an authority the receiver trustsThe receiver asks for a client certificateremote error: tls: bad certificate
Read access to the tls.ca_name, tls.cert_name and tls.key_name files, kept under the service rootTLS and mutual TLScould not be resolved (env/vault token, inline PEM, or a path under the service root)
Framing the receiver parses the same wayAlwaysNone. The receiver merges or discards records silently

"failed to send after 3 retries" with "connection refused"

[Error] [director] [target-<target id>] [tcp_forwarder] Sender worker 1 execute() failed for tcp_forwarder: target broken: failed to finalize target cache: failed to send after 3 retries: failed to connect to collector.example.com:514: dial tcp <receiver ip>:514: connect: connection refused

Cause: no process accepted the connection on that port. The receiver is stopped, it listens on a different port, or it is bound only to its loopback interface. A firewall that rejects the connection instead of dropping it reads the same way. On Windows the wrapped text comes from the operating system and typically says that the target machine actively refused the connection.

Fix: confirm the receiver listens on port over TCP, and that it is bound to an address the Director host can reach. Open a connection from the Director host to the same address and port to reproduce the failure outside Director. Correct address and port if they name the wrong endpoint.

Retried until fixed, and nothing is dropped. The batch is retried up to max_retries times, then the payload is handed back and redelivered about every 5 seconds until a write succeeds. Data waits in the queue meanwhile.

Each attempt is also logged on its own as [TCP] send-error ... attempt=N err=..., and the last one as [TCP] send-failed ... lastErr=.... The number in after 3 retries is your max_retries.

"i/o timeout" while connecting or while writing

[Error] [director] [target-<target id>] [tcp_high_volume] Sender worker 2 execute() failed for tcp_high_volume: target broken: failed to finalize target cache: failed to send after 5 retries: failed to connect to collector.example.com:5000: dial tcp <receiver ip>:5000: i/o timeout
[Error] [director] [target-<target id>] [tcp_forwarder] Sender worker 4 execute() failed for tcp_forwarder: target broken: failed to finalize target cache: failed to send after 3 retries: failed to write data: write tcp <director ip>:<port>-><receiver ip>:514: i/o timeout

Cause: the first form means the connection attempt got no answer at all. Something drops the packets on the way, usually a network firewall or a security group rule, or the host is off. The second form means the connection was open but the receiver stopped reading, so the batch could not be written in time. A receiver that is out of disk, stalled, or slower than the incoming rate produces it.

timeout bounds both the connection attempt and the write of each batch. With the default of 30 seconds and the default max_retries, a payload can spend about two minutes failing before the error is logged.

Fix: for the connect form, correct the routing and firewall rules between the two hosts. For the write form, check the receiver's health and throughput. Lower timeout when you want failures reported sooner. Raise it only when the receiver is known to be slow rather than broken.

Retried until fixed, and nothing is dropped. A write that stopped part way through is retried on a fresh connection, so the receiver can see the part it already accepted a second time. Expect duplicates at the receiver after this error, not loss.

"connection not alive: EOF" after a quiet period

[Warning] [director] [target-<target id>] [tcp_forwarder] [TCP] send-error file=<payload file> target=tcp_forwarder batch=1000 pending=1000 attempt=0 err=connection not alive: EOF

Cause: the connection was closed while the target had nothing to send, and the close is noticed at the next batch. The receiver restarted, or an idle timeout closed the session on the receiver, on a load balancer, or in a firewall session table. EOF means the other end closed cleanly. read: connection reset by peer means it was reset instead, and Windows typically words the same condition as an existing connection forcibly closed by the remote host.

Fix: none when the next attempt succeeds. The connection is tested before every write, so the batch is still intact and goes out on a new connection. If the warning returns at a regular interval, raise the idle timeout on whatever closes the session, or keep the connection busier by not scheduling long interval values. If it repeats and no attempt ever succeeds, treat it as a refusal by the receiver and read the receiver's own log.

No data is lost while a later attempt succeeds. If every attempt fails, the payload is redelivered as in the entry above.

"x509: certificate signed by unknown authority"

[Error] [director] [target-<target id>] [tcp_tls] Sender worker 1 execute() failed for tcp_tls: target broken: failed to finalize target cache: failed to send after 3 retries: failed to connect to collector.example.com:6514: tls: failed to verify certificate: x509: certificate signed by unknown authority

Cause: the receiver's certificate does not chain to an authority Director trusts. A private or self-signed authority is the usual reason. tls.ca_name replaces the host trust store rather than adding to it, so a bundle that holds only your private authority makes publicly issued certificates fail, and the wrong bundle makes every connection fail.

Fix: set tls.ca_name to the bundle holding the issuing authority, including any intermediates. The file must be PEM, starting with -----BEGIN CERTIFICATE-----, and must sit under the service root. A path outside the service root is refused when the target starts, with could not be resolved (env/vault token, inline PEM, or a path under the service root), and a file in another encoding with contains no valid PEM certificate(s). Use tls.verify: false only on a network you control, and note that it turns off the name check as well.

The related x509: certificate has expired or is not yet valid means the receiver's certificate is out of date, or the clock on the Director host is wrong. Both are retried until fixed, and nothing is dropped.

"x509: certificate is valid for ..., not ..."

[Error] [director] [target-<target id>] [tcp_tls] Sender worker 1 execute() failed for tcp_tls: target broken: failed to finalize target cache: failed to send after 3 retries: failed to connect to <receiver ip>:6514: tls: failed to verify certificate: x509: certificate is valid for collector.example.com, not <receiver ip>

Cause: the address you connect to is not one the certificate carries. Connecting by IP address is the common case, because most receiver certificates list names only. The variant certificate is valid for 1 IP SANs, but none matched ... means the certificate does list addresses, but not this one.

Fix: set tls.server_name to a name the certificate carries. That name is both sent to the receiver and checked against the certificate, so address may stay an IP address. Using the certificate's name as address works too, when it resolves from the Director host. Retried until fixed, and nothing is dropped.

"tls: first record does not look like a TLS handshake"

[Error] [director] [target-<target id>] [tcp_tls] Sender worker 3 execute() failed for tcp_tls: target broken: failed to finalize target cache: failed to send after 3 retries: failed to connect to collector.example.com:514: tls: first record does not look like a TLS handshake

Cause: tls.status is true but the port speaks plain TCP. Port 514 where the receiver expects 6514 is the usual mix-up.

Fix: point port at the encrypted port, or set tls.status: false for a plaintext receiver. Retried until fixed. The opposite mistake is harder to read: plaintext sent to a TLS port is accepted and then closed, which shows up as repeated connection not alive: EOF or failed to write data: ... broken pipe warnings instead.

Errors that start with "remote error: tls:"

[Error] [director] [target-<target id>] [tcp_tls] Sender worker 1 execute() failed for tcp_tls: target broken: failed to finalize target cache: failed to send after 3 retries: failed to connect to collector.example.com:6514: remote error: tls: bad certificate

Cause: the receiver rejected the handshake, so the text after remote error: tls: is the receiver's verdict rather than Director's. bad certificate and unknown certificate authority mean it asked for a client certificate and refused the one it was given, or none was configured. protocol version not supported and handshake failure mean it accepts no version or cipher in the range offered, which runs from tls1.2 to tls1.3 by default.

Fix: for the certificate texts, set tls.cert_name and tls.key_name to a certificate and key issued by an authority the receiver trusts. The two fields are required together. For the version texts, upgrade the receiver, or lower tls.min_tls_version when that is not possible. If a client certificate was replaced in place and the new file is unreadable or not valid PEM, the previous one stays in use with no message, and the error appears only later, when the old certificate expires. Check the new file before replacing the old one.

Retried until fixed, and nothing is dropped while the handshake keeps failing.

Zero does not mean "no limit" or "no retries"

timeout, batch_size, max_retries, retry_delay and reconnect_interval read 0 as "not set" and use their default instead. The value you might expect to switch a limit off does the opposite:

SettingWhat 0 looks like it doesWhat it does
max_retriesDeliver once, never retry3 retries, so 4 attempts
retry_delayRetry with no waitWaits as though set to 1
timeoutWait indefinitely30 seconds
batch_sizeNo batch limit1000 events

Use 1 or more for all of them. max_retries: 0 is accepted when the configuration is read and then behaves as 3, so a single attempt per batch cannot be configured. Negative values are rejected instead, with batch_size must be greater than 0, got -5 or max_retries must be zero or greater, got -1, and the target does not start until they are corrected.

note

reconnect_interval is accepted but does not change delivery in the current release. The wait before a retry comes from retry_delay alone. It is chosen at random up to a bound that doubles with every attempt and is capped at 30 seconds.

The target looks healthy but nothing arrives

Check these in order.

  1. debug.dont_send_logs is enabled. Events are processed and then discarded rather than sent, and the payload is marked delivered. With debug.status also enabled, startup logs Log sending is disabled for this target (tcp_forwarder). Logs will be processed by the pipeline but will not be sent to the target. With debug.status off, nothing is logged at all.
  2. The framing does not match the receiver. framing: delimiter sent to a receiver that expects length-prefixed records, or framing: octet sent to a line-based one, typically leaves the receiver splitting, merging or discarding records on its own. Nothing is reported back. Match framing to what the receiver parses.
  3. line_delimiter was quoted in a way that changed it. A single-quoted newline in YAML is appended as two literal characters, a backslash and an n. Write it double-quoted, as line_delimiter: "\n". An explicit empty value concatenates records with no separator at all. Remove the field to keep the default.
  4. Records carry their own line breaks. With delimiter framing the message is written as it is, so a record holding a carriage return or a line feed typically arrives as several records. Escape or strip line breaks in the pipeline, or use framing: octet.
  5. The port does not speak plain TCP. A receiver that answers with a banner, or an encrypted port addressed without tls.status: true, still looks alive. Writes succeed for a while and then fail with broken pipe or EOF, while the sent counters keep climbing.
  6. The receiver went away mid-write. The connection is tested before each write, but nothing acknowledges the records themselves. Records written into a connection that is closing are counted as sent. Confirm arrival at the receiver rather than relying on the target's counters.