The healthcheck runs INSIDE the container, so it must target the container port (7777), not the host mapping (8090) — and 127.0.0.1 explicitly, because `localhost` resolves to ::1 in the image while strfry binds IPv4 0.0.0.0 only. Both bugs verified empirically on .228 (container was Up but "unhealthy"; 127.0.0.1:7777/health returns 200). Ships at the next catalog regen/re-sign (catalog overlay supremacy); with the runtime manifest-reload now in place it will apply fleet-wide on the next catalog refresh, no service restarts needed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
214 lines
7.3 KiB
YAML
214 lines
7.3 KiB
YAML
app:
|
|
id: strfry
|
|
name: Strfry Nostr Relay
|
|
version: 0.9.0
|
|
description: Lightweight Nostr relay written in C++. Alternative to nostr-rs-relay with lower resource usage.
|
|
|
|
container:
|
|
image: dockurr/strfry:1.0.4
|
|
image_signature: cosign://...
|
|
pull_policy: verify-signature
|
|
|
|
dependencies:
|
|
- storage: 5Gi
|
|
|
|
resources:
|
|
cpu_limit: 1
|
|
memory_limit: 512Mi
|
|
disk_limit: 5Gi
|
|
|
|
security:
|
|
capabilities: []
|
|
readonly_root: true
|
|
no_new_privileges: true
|
|
seccomp_profile: default
|
|
network_policy: isolated
|
|
apparmor_profile: nostr-relay
|
|
|
|
ports:
|
|
- host: 8090
|
|
container: 7777
|
|
protocol: tcp # HTTP/WebSocket (strfry listens on 7777)
|
|
|
|
volumes:
|
|
- type: bind
|
|
source: /var/lib/archipelago/strfry
|
|
target: /app/strfry-db
|
|
options: [rw]
|
|
# Image default config demands a 1M NOFILES rlimit, above the rootless
|
|
# user-manager hard cap (524288) — ship the config with nofiles = 0.
|
|
# Mounting it also skips the entrypoint's copy into /etc, which a
|
|
# readonly_root container cannot do.
|
|
- type: bind
|
|
source: /var/lib/archipelago/strfry-config/strfry.conf
|
|
target: /etc/strfry.conf
|
|
options: [ro]
|
|
|
|
files:
|
|
- path: /var/lib/archipelago/strfry-config/strfry.conf
|
|
overwrite: true
|
|
content: |
|
|
##
|
|
## Default strfry config
|
|
##
|
|
|
|
# Directory that contains the strfry LMDB database (restart required)
|
|
db = "./strfry-db/"
|
|
|
|
dbParams {
|
|
# Maximum number of threads/processes that can simultaneously have LMDB transactions open (restart required)
|
|
maxreaders = 256
|
|
|
|
# Size of mmap() to use when loading LMDB (default is 10TB, does *not* correspond to disk-space used) (restart required)
|
|
mapsize = 10995116277760
|
|
|
|
# Disables read-ahead when accessing the LMDB mapping. Reduces IO activity when DB size is larger than RAM. (restart required)
|
|
noReadAhead = false
|
|
}
|
|
|
|
events {
|
|
# Maximum size of normalised JSON, in bytes
|
|
maxEventSize = 65536
|
|
|
|
# Events newer than this will be rejected
|
|
rejectEventsNewerThanSeconds = 900
|
|
|
|
# Events older than this will be rejected
|
|
rejectEventsOlderThanSeconds = 94608000
|
|
|
|
# Ephemeral events older than this will be rejected
|
|
rejectEphemeralEventsOlderThanSeconds = 60
|
|
|
|
# Ephemeral events will be deleted from the DB when older than this
|
|
ephemeralEventsLifetimeSeconds = 300
|
|
|
|
# Maximum number of tags allowed
|
|
maxNumTags = 2000
|
|
|
|
# Maximum size for tag values, in bytes
|
|
maxTagValSize = 1024
|
|
}
|
|
|
|
relay {
|
|
# Interface to listen on. Use 0.0.0.0 to listen on all interfaces (restart required)
|
|
bind = "0.0.0.0"
|
|
|
|
# Port to open for the nostr websocket protocol (restart required)
|
|
port = 7777
|
|
|
|
# Set OS-limit on maximum number of open files/sockets (if 0, don't attempt to set) (restart required)
|
|
nofiles = 0
|
|
|
|
# HTTP header that contains the client's real IP, before reverse proxying (ie x-real-ip) (MUST be all lower-case)
|
|
realIpHeader = ""
|
|
|
|
info {
|
|
# NIP-11: Name of this server. Short/descriptive (< 30 characters)
|
|
name = "Archipelago Strfry Relay"
|
|
|
|
# NIP-11: Detailed information about relay, free-form
|
|
description = "Self-hosted strfry Nostr relay on Archipelago."
|
|
|
|
# NIP-11: Administrative nostr pubkey, for contact purposes
|
|
pubkey = ""
|
|
|
|
# NIP-11: Alternative administrative contact (email, website, etc)
|
|
contact = ""
|
|
|
|
# NIP-11: URL pointing to an image to be used as an icon for the relay
|
|
icon = ""
|
|
|
|
# List of supported lists as JSON array, or empty string to use default. Example: "[1,2]"
|
|
nips = ""
|
|
}
|
|
|
|
# Maximum accepted incoming websocket frame size (should be larger than max event) (restart required)
|
|
maxWebsocketPayloadSize = 131072
|
|
|
|
# Maximum number of filters allowed in a REQ
|
|
maxReqFilterSize = 200
|
|
|
|
# Websocket-level PING message frequency (should be less than any reverse proxy idle timeouts) (restart required)
|
|
autoPingSeconds = 55
|
|
|
|
# If TCP keep-alive should be enabled (detect dropped connections to upstream reverse proxy)
|
|
enableTcpKeepalive = false
|
|
|
|
# How much uninterrupted CPU time a REQ query should get during its DB scan
|
|
queryTimesliceBudgetMicroseconds = 10000
|
|
|
|
# Maximum records that can be returned per filter
|
|
maxFilterLimit = 500
|
|
|
|
# Maximum number of subscriptions (concurrent REQs) a connection can have open at any time
|
|
maxSubsPerConnection = 20
|
|
|
|
writePolicy {
|
|
# If non-empty, path to an executable script that implements the writePolicy plugin logic
|
|
plugin = "/app/write-policy.py"
|
|
}
|
|
|
|
compression {
|
|
# Use permessage-deflate compression if supported by client. Reduces bandwidth, but slight increase in CPU (restart required)
|
|
enabled = true
|
|
|
|
# Maintain a sliding window buffer for each connection. Improves compression, but uses more memory (restart required)
|
|
slidingWindow = true
|
|
}
|
|
|
|
logging {
|
|
# Dump all incoming messages
|
|
dumpInAll = false
|
|
|
|
# Dump all incoming EVENT messages
|
|
dumpInEvents = false
|
|
|
|
# Dump all incoming REQ/CLOSE messages
|
|
dumpInReqs = false
|
|
|
|
# Log performance metrics for initial REQ database scans
|
|
dbScanPerf = false
|
|
|
|
# Log reason for invalid event rejection? Can be disabled to silence excessive logging
|
|
invalidEvents = true
|
|
}
|
|
|
|
numThreads {
|
|
# Ingester threads: route incoming requests, validate events/sigs (restart required)
|
|
ingester = 3
|
|
|
|
# reqWorker threads: Handle initial DB scan for events (restart required)
|
|
reqWorker = 3
|
|
|
|
# reqMonitor threads: Handle filtering of new events (restart required)
|
|
reqMonitor = 3
|
|
|
|
# negentropy threads: Handle negentropy protocol messages (restart required)
|
|
negentropy = 2
|
|
}
|
|
|
|
negentropy {
|
|
# Support negentropy protocol messages
|
|
enabled = true
|
|
|
|
# Maximum records that sync will process before returning an error
|
|
maxSyncEvents = 1000000
|
|
}
|
|
}
|
|
|
|
health_check:
|
|
type: http
|
|
# In-container probe: must target the CONTAINER port (7777), not the host
|
|
# mapping (8090), and 127.0.0.1 explicitly — `localhost` resolves to ::1
|
|
# inside the image while strfry binds IPv4 0.0.0.0 only (verified on .228:
|
|
# localhost:7777 refused, 127.0.0.1:7777/health = 200).
|
|
endpoint: http://127.0.0.1:7777
|
|
path: /health
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
nostr_integration:
|
|
relay_type: public
|
|
monetization_enabled: true
|