NixOS module for strfry β a high-performance Nostr relay.
- π Production-ready NixOS module
- π Hardened systemd service configuration
- π Built-in router support for syncing with remote relays
- π¦ Minimal dependencies (strfry is in nixpkgs)
- π οΈ Development shell for local testing
Test strfry locally without NixOS:
# Clone this repo
git clone https://github.com/orveth/strfry-nix.git
cd strfry-nix
# Enter dev shell
nix develop
# Create test data directory
mkdir -p ./test-data
# Copy example config
cp strfry.conf.example strfry.conf
# Edit config to point to test-data
# (change db = "./test-data")
# Start relay
strfry --config=./strfry.conf relayIn another terminal, test the relay:
# Check relay info (NIP-11)
curl -H "Accept: application/nostr+json" http://localhost:7777
# Connect with websocat
websocat ws://localhost:7777Add to your flake inputs:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
strfry-nix.url = "github:orveth/strfry-nix";
};
outputs = { self, nixpkgs, strfry-nix }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
strfry-nix.nixosModules.default
{
services.strfry = {
enable = true;
bind = "127.0.0.1"; # localhost-only
port = 7777;
info = {
name = "My Relay";
description = "Personal Nostr relay";
};
};
}
];
};
};
}Rebuild and start:
sudo nixos-rebuild switch
systemctl status strfryservices.strfry = {
enable = true;
# Network
bind = "127.0.0.1"; # Use "0.0.0.0" for external access
port = 7777;
# Storage
dataDir = "/var/lib/strfry";
dbSize = 10737418240; # 10GB
# Relay info (NIP-11)
info = {
name = "My Relay";
description = "Personal Nostr relay";
pubkey = "npub1..."; # Optional
contact = "admin@example.com"; # Optional
};
};services.strfry.events = {
maxEventSize = 65536; # 64KB
rejectEventsNewerThanSeconds = 900; # 15 min in future
rejectEventsOlderThanSeconds = 94608000; # 3 years old
maxNumTags = 2000;
maxTagValSize = 1024;
};Sync events from remote relays using the built-in router:
services.strfry.router = {
enable = true;
config = ''
streams {
public {
dir = "down"
urls = [
"wss://relay.damus.io"
"wss://nos.lol"
"wss://relay.nostr.band"
]
}
}
'';
};Or use an external config file:
services.strfry.router = {
enable = true;
configFile = ./strfry-router.conf;
};See strfry-router.conf.example for more options.
For external access with TLS:
services.nginx = {
enable = true;
virtualHosts."relay.example.com" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:7777";
proxyWebsockets = true;
extraConfig = ''
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
'';
};
};
};
# Tell strfry to use the real IP header
services.strfry.realIpHeader = "x-real-ip";# Check service status
systemctl status strfry
systemctl status strfry-router # if router is enabled
# View logs
journalctl -u strfry -f
journalctl -u strfry-router -f
# Export all events
sudo -u strfry strfry export > backup.jsonl
# Import events
cat events.jsonl | sudo -u strfry strfry import
# One-time sync from remote relay
sudo -u strfry strfry sync wss://relay.damus.io --dir down
# Scan events matching filter
sudo -u strfry strfry scan '{"kinds":[1],"limit":10}'- Storage: LMDB (single-file database, no external DB required)
- Config: Generated from Nix options, stored in
/nix/store - Data: Persistent in
/var/lib/strfry(configurable) - User:
strfry(isolated system user) - Hardening: Full systemd security lockdown
The systemd service uses extensive hardening:
NoNewPrivileges,PrivateTmp,ProtectHomeProtectSystem=strictwith explicitReadWritePathsMemoryDenyWriteExecute,RestrictRealtimeRestrictNamespaces,LockPersonality- Limited to
AF_INET,AF_INET6,AF_UNIXaddress families
For localhost-only relays, keep bind = "127.0.0.1" and skip nginx.
Check logs:
journalctl -u strfry -n 50Common issues:
- Port already in use (
ss -tulpn | grep 7777) - Permissions on
dataDir(sudo chown -R strfry:strfry /var/lib/strfry) - Invalid config syntax (check
strfry.conf.example)
journalctl -u strfry-router -fCheck:
- Remote relays are reachable (
websocat wss://relay.damus.io) - Router config syntax (
strfry-router.conf.example) - Main relay is running (
systemctl status strfry)
Monitor size:
du -sh /var/lib/strfryThe dbSize option sets the LMDB maximum. Actual disk usage grows as needed.
MIT
Issues and PRs welcome! This is a personal project but contributions are appreciated.