Run a Nostr Relay as Tor Hidden Service on OpenBSD

Let’s set up and run our own “private Twitter” on Nostr, a simple, open protocol that enables truly censorship-resistant publishing on the web!

Run a Nostr Relay as Tor Hidden Service on OpenBSD

Nostr, or “Notes and Other Stuff Transmitted by Relays”, is an open protocol that is designed for simplicity and censorship resistance, enabling decentralized publishing on the web. Even though the protocol allows publishing various content types, its most prominent use case so far have been social media status posts. Using client applications like Amethyst, Damus, and Iris, people can easily create a Nostr identity and join the flock of predominantly cryptocurrency enthusiasts.

Today we’re going to set up our own, private Nostr relay, as a Tor Hidden Service, on OpenBSD, and connect to it using the Amethyst Android client, allowing us to run our own “private Twitter” for us and our like-minded tinfoil hatter friends.


Usually, when I build an OpenBSD-based infrastructure, I use a Vultr VPS instance. Even though Vultr allows running Tor on their service unless it’s an exit node, for this setup I’d suggest taking a look at a different infrastructure provider that is more focused on privacy and ideally accepts payments via XMR.

Note: I won’t explicitly mention under which user I’m running each command, hence please read carefully. When the prompt shows bsdstr#, I’m acting as root user, otherwise, when it shows bsdstr%, I’m the using the _nostr user.

As soon as the OpenBSD VPS has booted, we can log in via SSH and perform a quick update of the system:

bsdstr# syspatch
bsdstr# pkg_add -u

After that, let’s begin by installing some handy tools:

bsdstr# pkg_add git zsh neovim wget mosh rsync htop

What I like to do is link nvim to vim, because typing vim is in my muscle memory. I also like to use zsh as shell. Since that’s all preference, these steps are optional:

bsdstr# ln -s /usr/local/bin/nvim /usr/local/bin/vim
bsdstr# chsh -s /usr/local/bin/zsh root

Another optional but useful thing that I like to do is to change the SSH port and disable password authentication / enable pubkey authentication. Make sure you have an authorized_keys entry with your pubkey in place before applying this change:

bsdstr# sed -i 's/^#Port 22/Port 31231/g;\
              s/^#PubkeyAuthentication .*/PubkeyAuthentication yes/g;\
              s/^#PasswordAuthentication .*/PasswordAuthentication no/g' \
bsdstr# rcctl restart sshd

Afterwards, disconnect from SSH, and re-connect, ideally using mosh, and launch tmux for the sake of comfort. See this post on how to make the mosh experience even smoother.


We begin by installing and configuring the Tor hidden service:

bsdstr# pkg_add tor
bsdstr# cat /etc/tor/torrc
Log notice syslog
RunAsDaemon 1
DataDirectory /var/tor
HiddenServiceDir /var/tor/hidden_service/
HiddenServicePort 80
User _tor

Next, enable the Tor hidden service:

bsdstr# rcctl enable tor
bsdstr# rcctl start tor

We can now check the hidden service’s Onion address:

bsdstr# cat /var/tor/hidden_service/hostname

This is the address that our hidden service will be available at.

Nostr Relay

For the Nostr Relay we’re going to use the strfry implementation.
Nope, we won’t, because it’s a PITA to compile on OpenBSD, since some dependencies are not easily installable through pkg_add.

For the Nostr Relay we’re going to use the nostr-rs-relay implementation.
Nope, we won’t either, because it won’t build.

For the Nostr Relay we’re going to use the nostream implementation.
Nope, we won’t use that either, because screw Typescript, Node.js and NPM, as those became the soy version of Spring, Java and Maven, and are a PITA to deal with these days; Let alone the security issues of half-baked or intentionally compromised NPM packages.

For the Nostr Relay we’re going to use the rnostr implementation. Mainly because it’s fairly up-to-date and supports a couple more NIPs than my other choice, Nex. #elixir #ftw

First, let’s install the required software to build the relay:

bsdstr# pkg_add git rust

Next, we can git clone and build it:

bsdstr# git clone && cd rnostr
bsdstr# mkdir config
bsdstr# cp ./rnostr.example.toml ./config/rnostr.toml
bsdstr# #
bsdstr# ulimit -n 1024 
bsdstr# cargo build -r

Note: Feel free to adjust ./config/rnostr.toml to your needs. Make sure rnostr listens on, by setting the following configuration:

host = ""
port = 8080

Then we’re going to install the binary and the configuration:

bsdstr# cp ./target/release/rnostr /usr/local/bin/
bsdstr# cp ./config/rnostr.toml /etc

Next we create the _nostr group and user on the system:

bsdstr# groupadd _nostr
bsdstr# useradd -d /home/_nostr -m -c "Nostr" -g _nostr -L daemon -s /sbin/nologin _nostr

Now, create the rc.d file and adjust the permissions:

bsdstr# cat /etc/rc.d/rnostr

daemon="/usr/local/bin/rnostr relay -c /etc/rnostr.toml"

. /etc/rc.d/rc.subr

rc_cmd $1
bsdstr# chmod 555 /etc/rc.d/rnostr

Then make sure to enable and start the service:

bsdstr# rcctl enable rnostr
bsdstr# rcctl start rnostr

Nostr Client

Amethyst displaying rnostr relay

You should now be able to tell your client to connect to your .onion address by entering it as relay into your relays list. Depending on the client, you will probably have to configure Tor first. For my favorite client, Amethyst, you have to first install Orbot, activate it and then select “Tor/Orbot setup” in the hamburger menu to configure access through Orbot.

Note: For Amethyst, make sure to enter the Onion address of the relay with a ws:// prefix (e.g. ws://xyz.onion) until this issue is resolved.

Your own private Nostr relay is now ready to go! At this point you can begin looking into more advanced configuration, e.g. for NIP-42 (auth), rate limiting and metrics. For securing/hardening the setup, I would recommend having a look at the Darknet Opsec Bible.

Enjoyed this? Support me via Monero, Bitcoin, Lightning, or Ethereum!  More info.