nats-io/nats-server · error

config reload not supported for decreasing jetstream max mem

Error message

config reload not supported for decreasing jetstream max memory and store

What it means

JetStream max_memory/max_store can only be increased via config reload, never decreased. When both old and new values are explicitly set and the new limit is lower than the running limit (the default branch of the switch), the server rejects the reload because shrinking live resource ceilings could strand in-use memory/storage.

Source

Thrown at server/reload.go:1862

					}
					if optName == "jetstreammaxmemory" {
						jsLimitsUpdate.newMaxMemory = new
					} else {
						jsLimitsUpdate.newMaxStore = new
					}
				case fromSet && toUnset:
					// Limits changed but it may mean that JS is being disabled,
					// keep track of the change and error in case it is not.
					if optName == "jetstreammaxmemory" {
						jsMemLimitsChanged = true
					} else {
						jsFileLimitsChanged = true
					}
				case fromUnset && toSet:
					// Prevent changing from dynamic max memory / file at runtime.
					return nil, fmt.Errorf("config reload not supported for jetstream dynamic max memory and store")
				default:
					return nil, fmt.Errorf("config reload not supported for decreasing jetstream max memory and store")
				}
			}
		case "jetstreammetacompact", "jetstreammetacompactsize", "jetstreammetacompactsync":
			// Allowed at runtime but monitorCluster looks at s.opts directly, so no further work needed here.
		case "jetstreamconcurrentios":
			// Not reloadable at runtime; preserve the current value while JetStream is disabled,
			// e.g. the entire jetstream{} block was deleted.
			if newOpts.JetStream {
				return nil, fmt.Errorf("config reload not supported for %s: old=%v, new=%v",
					field.Name, oldValue, newValue)
			} else {
				newOpts.JetStreamConcurrentIOs = oldValue.(int)
			}
		case "websocket":
			// Similar to gateways
			tmpOld := oldValue.(WebsocketOpts)
			tmpNew := newValue.(WebsocketOpts)
			tmpOld.TLSConfig, tmpOld.tlsConfigOpts = nil, nil

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Restart the server to apply a decreased limit
  2. Increase the value instead of decreasing (increases are allowed at runtime)
  3. Verify units in the config (e.g. "2GB" vs "200MB") — accidental unit mistakes trigger this error

Example fix

// before
jetstream { max_memory: 2GB }  # was 4GB, + reload signal
// after
server # systemctl restart nats-server  (decreases require restart)
// or increase instead:
jetstream { max_memory: 8GB }  # reload succeeds
Defensive patterns

Strategy: validation

Validate before calling

// Only allow increases on reload:
func limitDecreased(old, new *Options) bool {
	return (new.MaxMemory > 0 && old.MaxMemory > 0 && new.MaxMemory < old.MaxMemory) ||
		(new.MaxStore > 0 && old.MaxStore > 0 && new.MaxStore < old.MaxStore)
}
// if limitDecreased(old, new) { scheduleRestart() }

Prevention

When it happens

Trigger: SIGHUP reload after lowering `jetstream.max_memory` or `jetstream.max_store` (e.g. from 4GB to 2GB) while JetStream is enabled and both old and new values are explicit.

Common situations: Freeing capacity on a busy node by editing the limits down and reloading; fleet-wide config pushes that reduce limits on some servers; mistaken unit edits (GB vs MB) that silently lower the value.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/ef0bbaab64e9f14d. Report an issue: GitHub.