Skip to main content

Configuration

NatsConfig holds every connection setting for the NATS client - server addresses, authentication, TLS, reconnect behaviour, and socket tuning. Build it directly in code for static deployments, or let ZIO's built-in config system load it from environment variables or a HOCON file for environment-driven ones. Either way, the same Nats.live layer accepts the result.

Connecting to a server

NatsConfig defaults to localhost:4222 with no authentication and no TLS - the right starting point for local development. For production, pass one or more server URLs. Multiple servers enable automatic failover: if the client loses its connection to one server it tries the next in the list:

import zio.*
import zio.nats.*

// localhost:4222, no auth, no TLS
val defaultLayer = Nats.default

// Single server
val single = ZLayer.succeed(NatsConfig("nats://broker:4222")) >>> Nats.live

// Multiple servers - automatic failover and load distribution
val clustered = ZLayer.succeed(
NatsConfig(servers = List("nats://a:4222", "nats://b:4222", "nats://c:4222"))
) >>> Nats.live

Authentication

NatsAuth is a sealed ADT with mutually exclusive variants - you cannot accidentally combine two auth methods. Set it on NatsConfig via the auth field:

import zio.*
import zio.nats.*
import zio.nats.config.*
import java.nio.file.Paths

// Anonymous (default)
val noAuth = NatsConfig()

// Static token
val token = NatsConfig(auth = NatsAuth.Token("s3cr3t"))

// Username and password
val userPass = NatsConfig(auth = NatsAuth.UserPassword("alice", "p4ssw0rd"))

// NKey/JWT from a .creds file (Synadia Cloud / NGS)
val creds = NatsConfig(auth = NatsAuth.CredentialFile(Paths.get("/run/secrets/nats.creds")))

For dynamic credential rotation or credentials that cannot be expressed as static text - such as NKey signing with a key fetched from Vault - use NatsAuth.Custom(myAuthHandler) and pass a jnats AuthHandler built outside of zio-nats.

TLS

NatsTls is a sealed ADT controlling how the TCP connection is secured. Set it on NatsConfig via the tls field. Auth and TLS operate independently: TLS secures the transport; auth identifies your client to the NATS permission system. Use either, both, or neither:

import zio.*
import zio.nats.*
import zio.nats.config.*
import java.nio.file.Paths

// No TLS (default)
val noTls = NatsConfig()

// TLS using the JVM system trust store - works with public CA certificates
val systemTls = NatsConfig(tls = NatsTls.SystemDefault)

// mTLS with JVM keystore and truststore files
val mtls = NatsConfig(
tls = NatsTls.KeyStore(
keyStorePath = Paths.get("/certs/client.jks"),
keyStorePassword = "changeit",
trustStorePath = Some(Paths.get("/certs/ca.jks")),
trustStorePassword = Some("changeit")
)
)

For certificates loaded at runtime from AWS Secrets Manager, an HSM, or similar, use NatsTls.Custom(mySSLContext) with a pre-built javax.net.ssl.SSLContext.

Loading from the environment

NatsConfig.fromConfig is a ZLayer that reads settings from ZIO's built-in config system - no extra dependency required beyond ZIO itself. By default ZIO reads from environment variables, with keys normalised to UPPER_SNAKE_CASE under the NATS_ prefix.

Use this in container and CI/CD deployments where connection details differ per environment and you want to avoid hardcoding them in source:

import zio.*
import zio.nats.*

val envApp: ZIO[Any, Throwable, Unit] =
ZIO.serviceWithZIO[Nats](_.publish(subject"shop.orders", "order-123"))
.provide(Nats.live, NatsConfig.fromConfig)

All fields have defaults - only the values you want to override need to be set:

SettingEnvironment variable
servers (single)NATS_SERVERS=nats://broker:4222
servers (multiple)NATS_SERVERS=nats://a:4222,nats://b:4222
auth.typeNATS_AUTH_TYPE=token
auth.value (token)NATS_AUTH_VALUE=s3cr3t
auth.usernameNATS_AUTH_USERNAME=alice
auth.passwordNATS_AUTH_PASSWORD=p4ss
tls.typeNATS_TLS_TYPE=system-default
connection-timeoutNATS_CONNECTION_TIMEOUT=PT5S
max-reconnectsNATS_MAX_RECONNECTS=60
socket-read-timeoutNATS_SOCKET_READ_TIMEOUT=PT0.5S

Durations use ISO-8601 format: PT5S = 5 seconds, PT2M = 2 minutes, PT0.1S = 100 ms. Auth type values: no-auth, token, user-password, credential-file. TLS type values: disabled, system-default, key-store.

Loading from HOCON

If you prefer HOCON over environment variables - common for local development with application.conf - add the zio-config-typesafe dependency and install its config provider in bootstrap:

libraryDependencies += "dev.zio" %% "zio-config-typesafe" % "<zio-config-version>"
import zio.*
import zio.nats.*
import zio.config.typesafe.TypesafeConfigProvider

object MyApp extends ZIOAppDefault {
override val bootstrap: ZLayer[Any, Config.Error, Unit] =
Runtime.setConfigProvider(TypesafeConfigProvider.fromResourcePath())

val run = ZIO.unit.provide(Nats.live, NatsConfig.fromConfig)
}

In src/main/resources/application.conf:

nats {
servers = ["nats://broker:4222"]
connection-timeout = "PT5S"
auth {
type = token
value = ${?NATS_TOKEN}
}
}

NatsConfig.fromConfig reads from the root level by default. If your config lives under a different namespace, use .nested to scope it - calls stack from inner to outer:

import zio.*
import zio.nats.*

// Reads from myapp.nats.servers, myapp.nats.auth.type, etc.
val nestedConfig: ZLayer[Any, Config.Error, NatsConfig] =
ZLayer.fromZIO(
ZIO.config(NatsConfig.config.nested("nats").nested("myapp"))
)

Reconnect and timeout tuning

NatsConfig exposes several fields for controlling reconnect and timeout behaviour. The defaults suit most applications, but high-availability or latency-sensitive deployments may need to tune them:

FieldDefaultEffect
maxReconnects60Maximum reconnect attempts before giving up and emitting NatsEvent.Closed
reconnectWait2sPause between reconnect attempts
connectionTimeout2sHow long to wait for the initial TCP connection
drainTimeout30sHow long to wait when draining subscriptions on shutdown
socketWriteTimeout2sWrite deadline per TCP write; prevents silent hangs

Set these directly on NatsConfig when constructing it in code, or via the corresponding environment variables when using NatsConfig.fromConfig. See the NatsConfig reference for the full field list.

Next steps