# Connecting

Every resilient connection starts the same way: a client opens it. So far `order-svc` has connected with nothing but a server URL, a bare default connection. That works on a laptop, but it's the wrong starting point for production. The very first thing a connection does is also the part most likely to fail, since it has to find a server, agree on terms, and prove who it is.

This page opens `order-svc`'s connection deliberately. It adds two things to the bare default: a small set of connection options that name the connection and point it at more than one server, and an understanding of the connect handshake, the short conversation the client and server have before the first message moves. Once these are correct, every later mechanism in this chapter has a working connection to build on.

## Connection options

A **connection option** is a setting you pass at connect time. It's fixed for the life of the connection: you choose it when you open the connection, not while messages flow. Three of them matter before anything else.

The first is the **connection name**. By default a connection is anonymous: the server sees a client, but can't tell which application it is. Naming the connection `order-svc` makes it identifiable in `nats server report connections` and in the server logs, so when something goes wrong you can find the right connection instead of guessing.

The second is the **server pool**: the list of server URLs the client may connect to. With one URL, a client has one place to go, and if that server is unreachable the connect fails. With several URLs the client has choices. It tries them until one answers, which is failover at connect time, before a single message is sent.

Here's `order-svc` opening a named connection to a single server, the laptop setup from Core NATS:

#### CLI

```
#!/bin/bash

# Open order-svc's connection to a single server and give it a name.

#

# The --connection-name flag labels the connection so it is identifiable

# in `nats server report connections`. Without it, the server only sees an

# anonymous client and you cannot tell order-svc apart from anything else.

#

# This publishes the canonical order event once the connection is up.



nats pub orders.created \

  '{"order_id":"ord_8w2k","customer":"acme-co","total_cents":4200,"ts":"2026-05-22T10:14:22Z"}' \

  --server nats://n1:4222 \

  --connection-name order-svc
```

Every example on this page publishes the same canonical order event:

```
{"order_id":"ord_8w2k","customer":"acme-co","total_cents":4200,"ts":"2026-05-22T10:14:22Z"}
```

## The server pool is randomized

Once `order-svc` runs against more than one server, the pool's *order* matters. By default the client **randomizes** the pool before it dials. Give it `n1`, `n2`, and `n3`, and one client may try `n2` first while another tries `n1`. That randomization spreads connections evenly across the servers instead of concentrating every client on whichever URL happens to be first in the list.

You can turn randomization off with a single option (every client calls it some variant of `NoRandomize`) when you deliberately want a preferred-server order. Most applications should leave it on so that a restart of every `order-svc` instance doesn't overload one server.

`order-svc` opens against the `n1`/`n2`/`n3` cluster, used here only as a pool of three URLs the client can reach, not as a thing this chapter explains:

#### CLI

```
#!/bin/bash

# Open order-svc against a server pool instead of a single server.

#

# Pass every server URL the client may connect to, comma-separated. The

# client picks one at random by default; if the first dial fails it tries

# the next. That is failover at connect time, before a single message is

# sent.

#

# --connection-name labels the connection. The CLI does not expose the

# connect-timeout knob (the client libraries do) — see the other tabs for

# setting the timeout that bounds how long each dial may block.



nats pub orders.created \

  '{"order_id":"ord_8w2k","customer":"acme-co","total_cents":4200,"ts":"2026-05-22T10:14:22Z"}' \

  --server "nats://n1:4222,nats://n2:4222,nats://n3:4222" \

  --connection-name order-svc
```

Why the cluster exists, and how those three servers coordinate behind the pool, belongs to [Topologies → Your first cluster](/learn/topologies/your-first-cluster.md). This chapter treats the pool as a fact: three URLs the client may dial.

## The connect timeout bounds the dial

Dialing a server isn't instant. The client resolves the hostname, opens a TCP socket, and waits for the server to answer. Any of those steps can hang: a wrong DNS entry, a firewall that drops packets, a server that's up but overloaded. Without a bound, the client waits indefinitely.

The **connect timeout** is that bound. It caps how long a single dial may block before the client gives up and tries the next URL in the pool. The default is short (two seconds in most clients), which is enough for a healthy network and quick to move past a dead server. Set it deliberately when your network is slower than that, or leave the default when it isn't.

The timeout and a server pool complement each other. A pool gives the client other URLs to try, and the timeout decides how long it waits on one URL before trying the next. One unreachable server in the pool costs you one timeout, then the client moves on to the next URL.

The full set of connection options is documented in [Reference](/reference/.md); here we cover only the ones that change how a connection behaves under fault.

## The connect handshake

Naming a connection and pointing it at a pool decides which server the client goes to. The **connect handshake** is the exchange that happens once it gets there: the short conversation that turns a TCP socket into a working NATS connection.

It runs in four steps, in order:

1. **TCP dial.** The client opens a socket to the chosen server. The connect timeout bounds this step.
2. **Server INFO.** The server immediately sends an `INFO` message describing itself: its `server_id`, its `max_payload`, whether authentication is required (`auth_required`), whether TLS is required (`tls_required`), and more.
3. **Client CONNECT.** The client replies with a `CONNECT` message carrying its name, any credentials, and the protocol features it supports.
4. **`+OK` or `-ERR`.** The server accepts with `+OK` or rejects with `-ERR`. After `+OK`, the connection is **CONNECTED** and messages may flow.

Picture the handshake and the two end states it can reach, CONNECTED or rejected:

**Message flow — connectHandshakeAnimated:** Interactive NATS flow diagram.

* order-svc → server (subject: TCP connect)
* server → order-svc (subject: INFO)
* order-svc → server (subject: CONNECT + creds)
* server → order-svc (subject: -ERR auth violation)
* server → order-svc (subject: +OK)

Two fields in that `INFO` message change how the client behaves, so they're worth naming.

`auth_required` tells the client whether the server demands credentials. If it's true and the client has none, the handshake ends in `-ERR` and the connection never reaches CONNECTED. Supplying those credentials is the job of [TLS & Auth](/learn/resilient-clients/tls-and-auth.md) later in this chapter; here you only need to know the server announces the requirement up front, in the handshake.

`max_payload` is the largest single message the server will accept, one megabyte by default. The client reads it from `INFO` and enforces it locally: a publish larger than `max_payload` fails *before* it leaves the client, rather than being sent and rejected. That's why an oversized `orders.created` event fails fast — the client already knows the limit.

## Pitfalls

Connecting fails in a few predictable ways. Each one happens before a single order moves, so catching it at connect time saves a confusing debugging session later.

**One URL is a single point of failure at connect time.** A connection opened against a single server has nowhere to go if that server is unreachable, and the connect fails. Don't hardcode one URL for a production client. Pass the whole pool (several URLs, or several IPs behind one name) so the client can fail over while it's still connecting.

**A blocked dial with no timeout hangs the startup.** If DNS resolution or the TCP dial stalls and no connect timeout is set short, the client waits far longer than you expect, and a service that hangs on startup looks dead rather than slow. Do set a deliberate connect timeout, and pair it with a pool so a single slow server costs one timeout, not the whole startup.

**An oversized message fails the publish, not the connect.** A message larger than the server's `max_payload` (one megabyte by default) is refused by the client itself, before it's sent. The connection is fine; the publish is the thing that fails. Don't treat that error as a connection problem. Keep messages under `max_payload`, and for large bodies store the blob elsewhere and publish a reference.

Handle the connect failures at the boundary instead of letting them surface deep in the application. The handler branches on whether the client could reach any server in the pool at all:

#### CLI

```
#!/bin/bash

# Connecting can fail before any message moves. Two failures matter here:

# the credentials are the wrong type for the server (authorization error),

# and the message is larger than the server's max_payload (the publish is

# refused before it leaves the client).

#

# With the CLI, a failed connect prints the error and exits non-zero, so

# you can branch on it. The client libraries surface the same failures as

# an error you catch at connect time — see the other tabs.



# A pool whose first URL is unreachable: the client tries the next one.

# If every dial fails, the command exits non-zero and prints the reason.

if nats pub orders.created \

  '{"order_id":"ord_8w2k","customer":"acme-co","total_cents":4200,"ts":"2026-05-22T10:14:22Z"}' \

  --server "nats://n1:4222,nats://n2:4222,nats://n3:4222" \

  --connection-name order-svc; then

  echo "connected and published"

else

  echo "could not connect to any server in the pool" >&2

  exit 1

fi
```

## Where you are

`order-svc` now opens its connection deliberately:

* It carries a name (`order-svc`), so the server can identify it.
* It points at a server pool (`n1`/`n2`/`n3`), randomized by default, so one unreachable server isn't fatal at connect time.
* It bounds each dial with a connect timeout, so a blocked server costs one timeout and no more.

And you can read the connect handshake: TCP, the server's `INFO`, the client's `CONNECT`, and the `+OK` that means CONNECTED, plus what `auth_required` and `max_payload` in `INFO` mean for the client.

## What's next

The connection is open. The next fault to survive is the server going away *after* it's open. [Reconnection](/learn/resilient-clients/reconnection.md) makes `order-svc` cycle the same pool with backoff and jitter, and buffer its publishes until it rejoins.

Continue to [Reconnection](/learn/resilient-clients/reconnection.md).

## See also

* [Topologies → Your first cluster](/learn/topologies/your-first-cluster.md) — what the `n1`/`n2`/`n3` pool actually is on the server side.
* [TLS & Auth](/learn/resilient-clients/tls-and-auth.md) — supplying the credentials the handshake may demand when `auth_required` is set.
