Default Configs
Use whenyou need the exact value the transport starts from before overriding it.
You getevery stream, consumer and connection default, and the exported constant that holds it.
Override any of these through module configuration. Your values merge on top of the defaults, so name only what you change.
Stream Defaults
Every stream is created with storage: File, num_replicas: 1 (see replicas in production), discard: Old, allow_direct: true and compression: S2. What differs per kind:
| Property | Event | Command | Broadcast | Ordered | Schedule | DLQ |
|---|---|---|---|---|---|---|
retention | Workqueue | Workqueue | Limits | Limits | Workqueue | Limits |
allow_rollup_hdrs | true | false | true | false | true | false |
max_consumers | 100 | 50 | 200 | 100 | 100 | 100 |
max_msg_size | 1 MB | 1 MB | 1 MB | 1 MB | 1 MB | 1 MB |
max_msgs_per_subject | 100,000 | 10,000 | 50,000 | 500,000 | unlimited | 50,000 |
max_msgs | 1,000,000 | 100,000 | 500,000 | 5,000,000 | unlimited | 500,000 |
max_bytes | 512 MB | 64 MB | 256 MB | 1 GB | 64 MB | 256 MB |
max_age | 7 days | 3 minutes | 1 hour | 1 day | none | 30 days |
duplicate_window | 2 minutes | 30 seconds | 2 minutes | 2 minutes | 2 minutes | 2 minutes |
max_bytes is what the account is charged for, not what the stream currently holds: JetStream counts it against the account's storage budget the moment the stream is created. A service that provisions events plus a DLQ reserves 768 MB, and one that also uses ordered delivery reserves 1.75 GB, so a 10 GB file store carries roughly a dozen services instead of one.
max_msg_size matches the NATS server's own max_payload default of 1 MB. A larger value here has no effect until the server is reconfigured to accept larger payloads.
Sizes are binary: 1 MB is 1,048,576 bytes, 512 MB is 536,870,912 bytes. Durations are written with toNanos, so max_age: toNanos(7, 'days').
What each kind is for:
- Event carries workqueue events. A message is removed once a consumer acks it.
- Command carries RPC requests in JetStream RPC mode only. It is short-lived: a request nobody answered within three minutes is a request nobody wants answered.
- Broadcast is one stream shared by every service. An hour of
max_agegives a starting pod enough catch-up without letting config updates pile up. - Ordered keeps messages for a day under
Limitsretention, because an ordered consumer replays a subject from the beginning instead of consuming it away. - Schedule holds recurring schedules and their ticks, and exists only when a handler declares
schedule. It also setsallow_msg_schedulesandallow_msg_ttl.max_agestays off because an aged-out holder silently stops its schedule; ticks expire through their own TTL instead. - DLQ is created unless you pass
dlq: false. Thirty days underLimitsretention: reading a dead letter must not remove it. See Dead Letter Queue.
S2 is a Snappy-compatible codec with better ratios. It trades some CPU, depending on payload size and entropy, for less disk I/O and storage. Override per kind:
import { StoreCompression } from '@nats-io/jetstream';
events: {
stream: { compression: StoreCompression.None },
}
To enable message scheduling, add allow_msg_schedules: true to the event stream config. On a server too old for it, startup throws JetstreamCapabilityError; see NATS server versions.
Consumer Defaults
Every durable consumer uses ack_policy: Explicit, deliver_policy: All and replay_policy: Instant. What differs:
| Property | Event | Command | Broadcast | Schedule |
|---|---|---|---|---|
ack_wait | 10 seconds | 5 minutes | 10 seconds | 10 seconds |
max_deliver | 3 | 1 | 3 | 3 |
max_ack_pending | 100 | 100 | 100 | 100 |
An event that fails three times is dead-lettered. Between attempts the transport naks with a delay from the retry curve ([2000, 10000] ms), so the three attempts span roughly twelve seconds instead of burning out in milliseconds.
The delay is applied client-side, which leaves ack_wait at its full value, so a slow handler is not redelivered while it still runs. Tune the curve per kind with retry, or pass false to nak immediately.
A command is delivered once and never retried, so an RPC failure reaches the caller instead of being repeated behind their back.
Ordered consumers have no durable configuration: they are ephemeral and managed by the @nats-io/jetstream client.
Connection Defaults
| Property | Value | Notes |
|---|---|---|
maxReconnectAttempts | -1 | Unlimited reconnection attempts |
reconnectTimeWait | 1000 | 1 second between reconnection attempts |
The transport keeps reconnecting after a network failure. Override either value through connectionOptions.
Timeouts
| Timeout | Default | Constant |
|---|---|---|
| Core RPC (standard request-reply) | 30 seconds | DEFAULT_RPC_TIMEOUT |
| JetStream RPC (persistent) | 3 minutes | DEFAULT_JETSTREAM_RPC_TIMEOUT |
| Graceful shutdown drain | 10 seconds | DEFAULT_SHUTDOWN_TIMEOUT |
The JetStream RPC timeout is longer on purpose: the message is persisted to a stream first, and the consumer may take a while to reach it.
The shutdown value bounds drain() on each NATS connection, which waits for in-flight messages; when it runs out, the transport closes the connection anyway. Raise it only if drains hit the ceiling. See Graceful Shutdown.
Replicas in production
The default num_replicas: 1 suits development and single-node NATS. On a production cluster, set num_replicas: 3 so data survives a node failure:
JetstreamModule.forRoot({
name: 'orders',
servers: ['nats://nats-1:4222', 'nats://nats-2:4222', 'nats://nats-3:4222'],
events: { stream: { num_replicas: 3 } },
broadcast: { stream: { num_replicas: 3 } },
ordered: { stream: { num_replicas: 3 } },
schedule: { stream: { num_replicas: 3 } },
rpc: { mode: 'jetstream', stream: { num_replicas: 3 } },
dlq: { stream: { num_replicas: 3 } },
});
num_replicas is mutable: NATS adds or removes replicas on an existing stream without downtime or recreation.
Immutable vs mutable stream properties
Mutable (can be changed at any time)
num_replicas, max_age, max_bytes, max_msgs, max_msg_size, max_msgs_per_subject, discard, duplicate_window, subjects, compression, description, allow_rollup_hdrs, allow_direct
The transport applies mutable changes on startup: update the value in forRoot() and restart.
Enable-only (can be turned on, but never off)
A normal update enables these on an existing stream, without recreating it or losing messages. Once on, they cannot be turned off.
| Property | Default | Notes |
|---|---|---|
allow_msg_schedules | false | Enables message scheduling. true on the schedule stream |
allow_msg_ttl | false | Enables per-message TTL. true on the schedule stream |
deny_delete | false | Prevents message deletion through the API |
deny_purge | false | Prevents stream purging through the API |
Immutable (locked after creation)
| Property | Default | Migratable | Notes |
|---|---|---|---|
name | derived from service name | No | Cannot be renamed |
retention | Workqueue or Limits | No | Controlled by the transport: a mismatch is always an error |
storage | File | Yes | Migrated with provisioning.allowDestructiveMigration |
The transport migrates storage through blue-green stream recreation. The Stream Migration guide covers how it works, rolling updates and limitations.
retention follows the kind (Workqueue for events, commands and schedules, Limits for broadcast, ordered and DLQ). A running stream with a different policy fails startup, whatever provisioning.allowDestructiveMigration says.
Exported constants
Every default above is exported as a typed constant, for composing overrides or writing tests.
Stream and consumer defaults:
| Constant | Contents |
|---|---|
DEFAULT_EVENT_STREAM_CONFIG | Event (workqueue) stream defaults |
DEFAULT_BROADCAST_STREAM_CONFIG | Broadcast stream defaults (shared broadcast-stream) |
DEFAULT_ORDERED_STREAM_CONFIG | Ordered stream defaults |
DEFAULT_SCHEDULE_STREAM_CONFIG | Schedule stream defaults |
DEFAULT_COMMAND_STREAM_CONFIG | JetStream RPC command stream defaults |
DEFAULT_DLQ_STREAM_CONFIG | Dead Letter Queue stream defaults |
DEFAULT_EVENT_CONSUMER_CONFIG | Event consumer defaults |
DEFAULT_BROADCAST_CONSUMER_CONFIG | Broadcast consumer defaults |
DEFAULT_COMMAND_CONSUMER_CONFIG | JetStream RPC command consumer defaults |
DEFAULT_SCHEDULE_CONSUMER_CONFIG | Schedule tick consumer defaults |
Timeouts and the handler metadata registry:
| Constant | Value | Meaning |
|---|---|---|
DEFAULT_RPC_TIMEOUT | 30_000 | Core RPC timeout, ms |
DEFAULT_JETSTREAM_RPC_TIMEOUT | 180_000 | JetStream RPC timeout, ms |
DEFAULT_SHUTDOWN_TIMEOUT | 10_000 | Graceful shutdown drain, ms |
DEFAULT_METADATA_BUCKET | 'handler_registry' | KV bucket holding handler entries |
DEFAULT_METADATA_REPLICAS | 1 | KV bucket replicas |
DEFAULT_METADATA_HISTORY | 1 | KV history depth: latest only |
DEFAULT_METADATA_TTL | 30_000 | Entry TTL, refreshed by heartbeat, ms |
MIN_METADATA_TTL | 5_000 | Lowest TTL the module accepts, ms |
Other:
RESERVED_HEADERS; theSet<string>of header names blocked byJetstreamRecordBuilder.setHeader(). See Record Builder.
import { DEFAULT_EVENT_STREAM_CONFIG, toNanos } from '@horizon-republic/nestjs-jetstream';
events: {
stream: { ...DEFAULT_EVENT_STREAM_CONFIG, max_age: toNanos(14, 'days') },
}