Migrating from strfry
Move an existing strfry relay's events into nostrfy with one command — preparation, dry run, migration, verification and rollback.
At a glance
nostrfy migrate-strfry reads strfry's
own export format (JSONL, one NIP-01 event per line), so it works across strfry database versions and does
not depend on strfry's internal LMDB schema. It never writes to the strfry database.
| Migrated | Not migrated |
|---|---|
| Every stored event (replaceable/addressable semantics applied) | strfry settings with no nostrfy equivalent (the merge report lists each with a reason) |
| NIP-40 expiry — already-expired events are skipped | Blossom media and its owner mappings (strfry has no Blossom server) |
| NIP-09 deletions, including re-publication blocks for events strfry had already deleted | Access lists (NIP-86 bans, relay pubkey lists, Blossom allowlist) |
NIP-29 9005/9008 moderation side effects | NIP-43 invite codes (issue new ones with createclaim) |
| First-seen timestamps (when the new-pubkey gate is configured) | NIP-62 vanish requests unless --apply-vanish is given |
| NIP-29 groups, NIP-43 roles and their relay-signed metadata, rebuilt on the first start | The relay's own identity/keys (they live in nostrfy.toml) |
The equivalent strfry settings, offered for merge into nostrfy.toml (optional) |
Expected skips in the summary: ephemeral events (kinds 20000-29999, which nostrfy never stores) and already-expired events.
Quick start
# 1. stop the nostrfy relay (the migration needs the database directory)
nostrfy --config /etc/nostrfy/nostrfy.toml stop
# 2. dry run — parses and verifies every event, writes nothing
nostrfy --config /etc/nostrfy/nostrfy.toml migrate-strfry \
--strfry-db /var/lib/strfry-db --dry-run
# 3. import
nostrfy --config /etc/nostrfy/nostrfy.toml migrate-strfry \
--strfry-db /var/lib/strfry-db
# 4. start — NIP-29 groups and NIP-43 roles rebuild from the imported events
nostrfy --config /etc/nostrfy/nostrfy.toml startRequirements
- The
strfrybinary (for--strfry-db), or a JSONL file you exported yourself. - nostrfy v0.1.15 or newer (the
migrate-strfrysubcommand). - The nostrfy config for the target relay, with
database.path,public_urlandprivate_keyset. - Free disk space: roughly the size of the strfry export plus its indexes. The NIP-50 word index adds
some more; on a very tight disk you can disable it (
database.search_index = false), migrate, and re-enable it later (the index is rebuilt at startup). - No running nostrfy instance on the target
database.path.
Prepare the config
[relay]
name = "My Relay"
public_url = "wss://relay.example.com" # needed for NIP-42/62/98 and NIP-29 metadata
private_key = "..." # needed for NIP-29/43 relay-signed metadata
[server]
host = "0.0.0.0"
port = 8080
[database]
path = "/var/lib/nostrfy"
map_size = 1073741824Generate the relay key with nostrfy genkey if you do not have one, then validate:
nostrfy --config /etc/nostrfy/nostrfy.toml checkMerging the strfry settings (optional)
Before the database is opened, migrate-strfry looks for strfry's config
(--strfry-config, then $STRFRY_CONFIG, /etc/strfry.conf, ./strfry.conf), prints the settings that have a nostrfy equivalent and differ from your nostrfy.toml, and asks whether to merge them. Only the listed keys are rewritten — comments
and every other line are preserved, and a value that would make the config invalid is skipped with its
reason while the rest still merge.
--merge-configapplies without asking (for scripts);--no-merge-configskips the step.- With no terminal, proposals are printed and the merge is skipped unless
--merge-configis given. --dry-runprints the proposals but never writes.
Dry run
Always look before you leap — a dry run parses and verifies the whole export without touching the database:
nostrfy migrate-strfry --strfry-db /var/lib/strfry-db --dry-runA non-zero bad signature count means the export contains events strfry accepted without
verification; they will be skipped. If you trust them, pass --no-verify to import them anyway.
Migrate
Pick one of three input modes — all produce the same result:
# Option A — nostrfy runs `strfry export` itself (strfry on PATH)
nostrfy migrate-strfry --strfry-db /var/lib/strfry-db
# Option B — you exported to a file
strfry export > /tmp/strfry-export.jsonl
nostrfy migrate-strfry --input /tmp/strfry-export.jsonl
# Option C — pipe (stdin is the default input)
strfry export | nostrfy migrate-strfry| Flag | Why |
|---|---|
--strfry-bin <PATH> | strfry is not on PATH |
--since <UNIX> | Resume/catch-up: events with this created_at or newer (inclusive) |
--apply-vanish | Honor NIP-62 vanish requests found in the export (off by default) |
--no-verify | Skip signature verification for trusted dumps (faster) |
--batch <N> | Events per database transaction (default 512) |
--dry-run | Parse and verify only |
The migration is safe to re-run: duplicates are skipped and the deletion side effects are
re-applied, so an interrupted run can simply be repeated (or resumed with --since).
Start and verify
The first start rebuilds the NIP-29 group store and the NIP-43 role store from the imported events and
republishes the relay-signed metadata (39000/39001/39002/39005 per group, the 13534 membership list). On a large database this can take a moment; watch the log.
R=wss://relay.example.com # for nak (WebSocket)
H=https://relay.example.com # for curl (HTTP)
nak relay "$R" # relay answers and advertises its NIPs
curl -s "$H/api/v1/query?limit=1" # events are served
nak req -i <deleted-event-id> "$R" # a deleted event stays gone
nak req -k 39000 "$R" # NIP-29 group metadata (if migrated)
nak req --auth --force-pre-auth --sec <nsec> -k 13534 "$R" # NIP-43 membership (AUTH)For an exact count comparison, strfry scan '{}' | wc -l minus the ephemeral/expired events
reported by the migration summary should equal what clients can retrieve.
Resuming an interrupted migration
- Exported to a file / piped: re-run the same command. Duplicates are skipped and the deletion blocks are re-applied.
- Used
--strfry-db: the summary prints a resume hint; re-run with that--since(inclusive, so the boundary second is re-imported and deduplicated). - If the run failed with
database writer unavailable, check free disk space anddatabase.map_size, then re-run.
Rollback
The migration only writes to the nostrfy database. To roll back, stop the relay and restore the pre-migration database or remove it:
nostrfy --config /etc/nostrfy/nostrfy.toml stop
rm -rf /var/lib/nostrfy # or restore the pre-migration backupTroubleshooting
| Message | Cause / fix |
|---|---|
cannot lock the database directory ...; stop the relay before migrating | A nostrfy daemon (or another migration) holds the directory: nostrfy stop first |
strfry database directory ... does not exist | --strfry-db must name the directory that contains data.mdb |
cannot run 'strfry': ... | Install strfry, set --strfry-bin, or use --input |
database writer unavailable; the migration did not complete | The writer thread stopped or the queue is overloaded: check disk/map size, re-run (safe) |
group purge for <id> did not complete | The purge was interrupted: re-run the migration |
High bad signature count | The strfry DB contains unverified events: inspect them; import with --no-verify only if you trust the source |
| NIP-29 metadata missing after the start | No relay.private_key: run nostrfy genkey and restart |
| The settings merge is not offered | strfry's config was not found: pass --strfry-config /etc/strfry.conf |
Checklist
- nostrfy relay stopped
- strfry database and nostrfy config backed up
nostrfy checkpasses- strfry settings merged (or the report reviewed)
- Dry run reviewed (no unexpected bad signatures)
- Migration completed without errors
- Relay starts; group/role rebuild logged
- Event counts match (minus ephemeral/expired)
- Deleted events stay gone (re-publish rejected)
- Private-group visibility checked anonymously and as a member
- Reverse proxy / DNS / client relay lists updated