Configuration

Complete reference for the nostrfy.toml configuration file: every key, its type, its default, and exactly what it does.

Basics

The configuration is a TOML file, by default named nostrfy.toml. Create it with init:

sh
nostrfy --config nostrfy.toml init

Validate it (recommended before every start):

sh
nostrfy --config nostrfy.toml check

Every command takes --config <path> (default nostrfy.toml).

General syntax:

toml
[section]
key = "string"
key = 8080
key = [1, 2]
key = true

Configuration sections

SectionPurpose
[relay]Identity, URLs and NIP toggles
[server]Network binding, API split, metrics
[rpc]NIP-86 management RPC (auth, body limit)
[limits]All limits and overload protections
[database]LMDB storage, search index, queue caps
[daemon]PID, log and stats files and rotation
[access]Initial access control lists (changeable at runtime)
[blossom]Blossom file server (media hosting)

Every key is optional; a missing key uses its default.

Section [relay] — relay identity

KeyTypeDefaultDescription
namestring"nostrfy"Relay name shown to clients via NIP-11
descriptionstring"A minimal and stable Nostr relay"Relay description (NIP-11)
pubkeystring (64 hex)""Administrator public key (NIP-11 pubkey field)
contactstring""Administrator contact URI (mailto: or https://)
iconstring""Relay icon image URL
post_policystring""URL pointing to the relay's posting policy
private_keystring (64 hex)""The relay's own secret key; required for NIP-29 groups
public_urlstring""Public URL, e.g. wss://relay.example.com
livekit_urlstring""LiveKit server URL for NIP-29 audio/video rooms
livekit_api_keystring""LiveKit API key
livekit_api_secretstring""LiveKit API secret (used to sign JWTs)
enabled_nipsarray of integers[]Explicit NIP allowlist
disabled_nipsarray of integers[]NIPs to disable (ignored when enabled_nips is non-empty)
reject_ephemeralbooleanfalseReject NIP-01 ephemeral events (kinds 20000-29999)
enabled_gitbooleanfalseAccept NIP-34 git events (kinds 1617-1633, 30617/30618)
require_powinteger0Required proof-of-work in leading zero bits
new_pubkey_min_age_secsinteger0Refuse events from pubkeys younger than this (seconds; 0 = off)
max_events_per_min_per_pubkeyinteger0Publish rate limit per pubkey (per minute; 0 = no limit)
max_groupsinteger1000Cap on the in-memory NIP-29 group store
require_authbooleanfalseRequire NIP-42 authentication for REQ/EVENT/COUNT/NEG
send_auth_challengebooleantrueSend the AUTH challenge on connect
enabled_nip78_authbooleantrueRequire NIP-42 AUTH before accepting kind 78/30078 events
enabled_command_eventsbooleanfalseExecute kind:1 operator commands authored by the admin pubkey

Key details

  • private_key — the relay's own secret key, used to sign relay-generated events: NIP-29 group metadata (39000-39005) and NIP-43 role/membership events. Generate with nostrfy genkey; keep it secret. It is read once at startup, so changing it requires a restart.
  • public_url — used to validate URL-bearing tags from clients: NIP-42 AUTH, NIP-62 vanish and NIP-98 admin auth. When empty, the relay falls back to host:port, which never matches a real client URL when binding 0.0.0.0 or 127.0.0.1 (a warning is logged). Always set it.
  • enabled_nips vs disabled_nips — the allowlist wins: when enabled_nips is non-empty, only its NIPs are advertised and disabled_nips is ignored. Both require a restart.
  • reject_ephemeral — kinds 20000-29999 are rejected, but the exempt kinds NIPs require to be relayed are still forwarded: 22242, 27235, 28934/28935/28936, 24133, 23194/23195, 24242 and 21059. Applies on SIGHUP.
  • enabled_git — opt-in NIP-34: accepts kinds 1617-1633 and 30617/30618 and advertises NIP-34. Off by default because patch payloads can be large. Applies on SIGHUP.

Section [server] — server settings

KeyTypeDefaultDescription
hoststring"127.0.0.1"Bind address; 0.0.0.0 accepts connections from anywhere
portinteger8080Port (1-65535); port 80 requires root
api_hoststring""Hostname dedicated to the REST API
metrics_enabledbooleantrueServe Prometheus metrics at /metrics
ws_pathsstring"root"WebSocket endpoint paths: root, inbox-outbox or all
inbox_write_policystring"any"Who may write to /inbox: "any" or "relay" (events must still carry a p tag)
outbox_write_policystring"any"Who may write to /outbox: "any" (the NIP-42-authenticated pubkey's own events) or "relay"

Key details

  • host0.0.0.0 binds all IPv4 interfaces; 127.0.0.1 is local only.
  • port — 1-65535; port 80 requires root. This one port serves the WebSocket relay, the NIP-11 document, the REST API and the NIP-86 RPC together.
  • api_host — dedicates the REST API to a single hostname so the API and the relay can share one port behind a reverse proxy. Fixed at startup — requires a restart.
  • ws_pathsroot serves / only, inbox-outbox serves /inbox and /outbox only, all serves both. Fixed at startup — requires a restart.

Section [rpc] — NIP-86 management

KeyTypeDefaultDescription
management_tokenstring""Bearer token for the management APIs
admin_pubkeystring (64 hex)""Administrator pubkey for NIP-98 management auth
max_admin_body_bytesinteger65536Body limit for the NIP-86 management RPC

Section [limits] — limits and protections

Connections and messages

KeyTypeDefaultDescription
max_connectionsinteger10000Maximum concurrent connections
max_connections_per_ipinteger64Max connections per source IP
max_ws_message_bytesinteger1048576Max bytes per WebSocket message/frame
socket_recv_buffer_kbinteger64Per-connection kernel receive buffer (KiB)
max_out_queue_bytesinteger262144Per-connection outgoing queue cap (bytes)
ws_idle_timeout_secsinteger300Close idle connections after this long
http_read_timeout_secsinteger30HTTP request-head timeout (slow-loris defense)
max_connections_per_sec_per_ipinteger0Max new connections per second per source IP

Subscriptions and queries

KeyTypeDefaultDescription
max_filtersinteger20Max filters per REQ
max_subscriptionsinteger20Max subscriptions per connection
max_limitinteger500Ceiling for the REQ limit
max_countinteger2000Ceiling for COUNT results
max_sub_id_leninteger64Max subscription id length
max_sub_bytesinteger1048576Total subscription filter bytes per connection
max_req_response_bytesinteger33554432 (32 MB)Ceiling for the total bytes a single REQ response may send

Events

KeyTypeDefaultDescription
max_content_bytesinteger65536Max event content length in characters
max_tagsinteger2000Max tags per event
max_tag_value_bytesinteger1024Max bytes per tag value
max_created_at_future_secsinteger3600Tolerated future skew of created_at
group_late_publish_secsinteger3600Tolerated lag for NIP-29 group admin events (seconds)
max_neg_itemsinteger100000Max records per NIP-77 negentropy sync

REST API

KeyTypeDefaultDescription
max_api_concurrentinteger8Max concurrent /api/v1 requests
max_api_limitinteger5000Ceiling for the API limit parameter
max_api_offsetinteger50000Ceiling for the API offset parameter
max_api_search_bytesinteger2048Max bytes of the API search parameter

Live fan-out

KeyTypeDefaultDescription
live_batch_interval_msinteger20How often live events are flushed (ms)
live_batch_sizeinteger32Max events per live batch
live_bufferinteger65536Live fan-out queue size

Section [database] — database

KeyTypeDefaultDescription
pathstring"./data"Database directory (LMDB)
max_dbsinteger32LMDB max named databases
max_readersinteger128LMDB max concurrent readers
map_sizeinteger1073741824 (1 GB)Floor for the memory map size (bytes)
max_map_sizeinteger1099511627776 (1 TB)Memory-map ceiling (bytes)
purge_interval_secsinteger300NIP-40 purge interval (seconds)
search_indexbooleantrueEnable the NIP-50 word index
reader_threadsinteger2Dedicated scan threads
max_indexed_wordsinteger32Words of each event's content indexed for search
meta_indexbooleantrueWrite the per-event metadata header used by the scan prefilter
disabled_fsyncbooleanfalseSkip the synchronous disk flush after every write batch
db_buffer_sizeinteger2048Initial per-connection WebSocket buffer (bytes)
db_request_timeout_secsinteger30How long a database request may wait before failing
max_db_queue_msgsinteger4096Max queued pending messages before failing fast
max_db_queue_eventsinteger262144Max events inside queued batches before failing fast

Key details

  • map_size — the floor for the memory map: the map is always opened at least this large.
  • max_map_size — the ceiling, opened as a sparse virtual reservation: physical disk grows only with the data actually written. Raise it when you hit database map is full.
  • search_index = false — search still works (whole-word matching against content) but scans get slower; on a tiny VPS it halves the database. Recommended on small instances.
  • disabled_fsync — trades durability for throughput: writes commit into the OS page cache and a power loss may lose the most recent writes.

Section [daemon] — daemon

KeyTypeDefaultDescription
pid_filestring"./nostrfy.pid"PID file path
log_filestring"./nostrfy.log"Log file path
stats_filestring"./nostrfy.stats.json"Statistics file path
stats_interval_secsinteger5Statistics write interval (seconds)
max_log_size_bytesinteger52428800 (50 MB)Log rotation size (0 = no rotation)
max_log_filesinteger5Rotated log generations to keep

Paths are resolved against the config file's directory, so they stay valid after the daemon changes its working directory.

Section [access] — access control

KeyTypeDefaultDescription
restrict_relaybooleanfalseOnly allow-listed pubkeys may publish
blocked_kindsarray of integers[]Kinds to reject
allowed_kindsarray of integers[]Kind allowlist; only these kinds are accepted when non-empty
blocked_ipsarray of strings[]IP addresses refused at connection time

The pubkey allow/deny lists are not config keys — they live in the relay database (LMDB) and are managed at runtime:

sh
nostrfy relay allow npub1...
nostrfy relay deny npub1...
nostrfy relay list
  • restrict_relay = true — only the allow-listed pubkeys may publish, while reading stays open to everyone (any client can still subscribe and fetch).
  • A denied pubkey is always rejected when publishing and never served when reading.

Section [blossom] — Blossom file server

KeyTypeDefaultDescription
hoststring""Hostname for the Blossom server (empty = disabled)
storagestring"local"Backend: "local" (local_path) or "s3" (S3-compatible bucket)
local_pathstring"/var/lib/nostrfy/images"Local storage root for media files
max_upload_bytesinteger20971520 (20 MB)Max media file size
min_free_bytesinteger33554432 (32 MB)Disk space below which uploads are refused
s3_endpointstring""S3-compatible endpoint (e.g. R2)
s3_regionstring""S3 region (R2 uses "auto")
s3_bucketstring""S3 bucket name
s3_access_keystring""S3 access key
s3_secret_keystring""S3 secret key
restrict_uploadsbooleanfalseOnly allow-listed pubkeys may upload

Reloading at runtime (SIGHUP)

Editing the file and sending kill -HUP $(cat nostrfy.pid) reloads the config without a restart. Most settings take effect immediately; a few are fixed at startup:

Applies on SIGHUPRequires restart
relay.name, description, pubkey, contact, icon, post_policy, public_urlrelay.private_key
reject_ephemeral, enabled_git, enabled_nip78_authrelay.livekit_*, enabled_nips / disabled_nips
most of [limits]api_host, metrics_enabled, ws_paths, database.*, daemon sizes, limit caps, blossom.*

[access] is not applied by a reload — the lists are seeded once at startup and then managed at runtime via NIP-86. The log warns when a restart-required setting changed, and a few startup-captured settings are not checked by the reload.

Common mistakes
MistakeFix
public_url unsetset wss://...
host left 127.0.0.1external clients can't connect
private_key unset with NIP-29run nostrfy genkey + restart
restrict_relay true with empty allowlisteveryone locked out
changing restart-only keys and only SIGHUPinguse nostrfy restart