How to self host strfy Nostr relay with Docker Compose and Nginx Proxy Manager

How to self host strfy Nostr relay with Docker Compose and Nginx Proxy Manager

I love testing every new self hosted app and I can say that Nostr "world" is really good regarding self hosting stuff.

Today I tested a Nostr relay named Strfry.

Strfry is really simple to setup and support a lot's of Nostr NIPs.

Here is the list of what it is able to do :

  • Supports most applicable NIPs: 1, 2, 4, 9, 11, 12, 15, 16, 20, 22, 28, 33, 40
  • No external database required: All data is stored locally on the filesystem in LMDB
  • Hot reloading of config file: No server restart needed for many config param changes
  • Zero downtime restarts, for upgrading binary without impacting users
  • Websocket compression: permessage-deflate with optional sliding window, when supported by clients
  • Built-in support for real-time streaming (up/down/both) events from remote relays, and bulk import/export of events from/to jsonl files
  • negentropy-based set reconcilliation for efficient syncing with remote relays

Installation with docker compose (v2)

Spoiler : you need to have a computer with more than 1 (v)Core / 2GB of RAM to build the docker image locally. If not, this below might crash your computer during docker image build. You may need to use a prebuilt strfry docker image.

I assume you've read my first article on Managing domain with Nginx Proxy Manager because I will use the NPM docker compose stack to publish strfry Nostr relay. Without the initial NPM configuration done, it may not work as expected. I'll use the same docker-compose.yml file and folder.

Get back in the "npm-stack" folder :

cd npm-stack

We'll create a root strfry folder :

mkdir strfry

Cloning the strfry github repo locally :

git clone https://github.com/hoytech/strfry.git strfry/strfry-build

Modify the docker-compose file to locate the strfry configuration data outside of the folder repo directory to avoid mistake during futures upgrades (CTRL + X, S & ENTER to quit and save modifications) :

nano docker-compose.yml

You don't have to insert the Nginx Proxy Manager part, you should already have it into the file. If not, check here. You should only have to add the strfry part.

version: '3.8'
services:
  # should already be present into the docker-compose.yml
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    restart: unless-stopped
    ports:
      # These ports are in format <host-port>:<container-port>
      - '80:80' # Public HTTP Port
      - '443:443' # Public HTTPS Port
      - '81:81' # Admin Web Port
      # Add any other Stream port you want to expose
      # - '21:21' # FTP

    # Uncomment the next line if you uncomment anything in the section
    # environment:
      # Uncomment this if you want to change the location of
      # the SQLite DB file within the container
      # DB_SQLITE_FILE: "/data/database.sqlite"

      # Uncomment this if IPv6 is not enabled on your host
      # DISABLE_IPV6: 'true'

    volumes:
      - ./nginxproxymanager/data:/data
      - ./nginxproxymanager/letsencrypt:/etc/letsencrypt

  strfry-nostr-relay:
    container_name: strfry
    build: ./strfry/strfry-build
    volumes:
      - ./strfry/strfry-data/strfry.conf:/etc/strfry.conf
      - ./strfry/strfry-data/strfry-db:/app/strfry-db
# ports is commented by NPM will access through docker internal network
# no need to expose strfry port directly to the internet
#    ports:
#      - "7777:7777"

Before starting the container, we need to customize the strfry configuration file "strfry.conf". We'll copy the strfry configuration file and place it into the "strfry-data" folder to modify it with our own settings :

mkdir strfry/strfry-data
cp strfry/strfry-build/strfry.conf strfry/strfry-data/

And modify the strfry.conf file with your own settings :

nano strfry/strfry-data/strfry.conf

You can modify all the settings you need but the basics settings are :

  • bind = "127.0.0.1" --> bind = "0.0.0.0" --> otherwise NPM won't be able to contact the strfry service
  • name = "strfry default" --> name of your nostr relay
  • description = "This is a strfry instance." --> your nostr relay description
  • pubkey = "" --> your pubkey in hex format. You can use the Damu's tool to generate your hex key from your npub key : https://damus.io/key/
  • contact = "" --> your email

relay {
    # Interface to listen on. Use 0.0.0.0 to listen on all interfaces (restart required)
    bind = "127.0.0.1"

    # 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 = 1000000

    # 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 = "strfry default"

        # NIP-11: Detailed information about relay, free-form
        description = "This is a strfry instance."

        # NIP-11: Administrative nostr pubkey, for contact purposes
        pubkey = ""

        # NIP-11: Alternative administrative contact (email, website, etc)
        contact = ""
    }

You can now start the docker strfry docker container :

docker compose up -d

This command will take a bit of time because it will build the strfry docker image locally before starting the container. If your VPS doesn't have lot's of (v)CPU/RAM, it could fail (nothing happening during the docker image build). My VPS has 1 vCore / 2GB of RAM and died few seconds after the build beginning.

If it's the case, you can use prebuilt strfry docker image available on the Docker hub : https://hub.docker.com/search?q=strfry&sort=updated_at&order=desc

That said, otherwise, you should see this :

user@vps:~/npm-stack$ docker compose up -d
[+] Building 202.4s (15/15) FINISHED                                                                                                                                                                                      
 => [internal] load build definition from Dockerfile                                                                                                                                                                 0.2s
 => => transferring dockerfile: 724B                                                                                                                                                                                 0.0s
 => [internal] load .dockerignore                                                                                                                                                                                    0.3s
 => => transferring context: 2B                                                                                                                                                                                      0.0s
 => [internal] load metadata for docker.io/library/ubuntu:jammy                                                                                                                                                      0.0s
 => [build 1/7] FROM docker.io/library/ubuntu:jammy                                                                                                                                                                  0.4s
 => [internal] load build context                                                                                                                                                                                    0.9s
 => => transferring context: 825.64kB                                                                                                                                                                                0.2s
 => [runner 2/4] WORKDIR /app                                                                                                                                                                                        1.3s
 => [build 2/7] WORKDIR /build                                                                                                                                                                                       1.5s
 => [runner 3/4] RUN apt update && apt install -y --no-install-recommends     liblmdb0 libflatbuffers1 libsecp256k1-0 libb2-1 libzstd1     && rm -rf /var/lib/apt/lists/*                                           12.4s
 => [build 3/7] RUN apt update && apt install -y --no-install-recommends     git g++ make pkg-config libtool ca-certificates     libyaml-perl libtemplate-perl libregexp-grammars-perl libssl-dev zlib1g-dev     l  55.5s
 => [build 4/7] COPY . .                                                                                                                                                                                             0.9s 
 => [build 5/7] RUN git submodule update --init                                                                                                                                                                      2.6s 
 => [build 6/7] RUN make setup-golpe                                                                                                                                                                                10.8s 
 => [build 7/7] RUN make -j4                                                                                                                                                                                       126.8s 
 => [runner 4/4] COPY --from=build /build/strfry strfry                                                                                                                                                              1.3s 
 => exporting to image                                                                                                                                                                                               0.8s 
 => => exporting layers                                                                                                                                                                                              0.8s 
 => => writing image sha256:1d346bf343e3bb63da2e4c70521a8350b35a02742dd52b12b131557e96ca7d05                                                                                                                         0.0s 
 => => naming to docker.io/library/docker-compose_strfry-nostr-relay                                                                                                                                                 0.0s 
                                                                                                                                                                                                                          
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them                                                                                                                      
[+] Running 02/02                                                                                                                                                                                                         
 ⠿ Container strfry                        Started                                                                                                                                                                  11.0s
 ⠿ Container npm-stack-app-1               Running

You can check if everything is OK with strfry container by checking the container logs :

p@vps:~/npm-stack$ docker logs strfry
date       time         ( uptime  ) [ thread name/id ]   v| 
2023-07-21 19:26:58.514 (   0.039s) [main thread     ]INFO| arguments: /app/strfry relay
2023-07-21 19:26:58.514 (   0.039s) [main thread     ]INFO| Current dir: /app
2023-07-21 19:26:58.514 (   0.039s) [main thread     ]INFO| stderr verbosity: 0
2023-07-21 19:26:58.514 (   0.039s) [main thread     ]INFO| -----------------------------------
2023-07-21 19:26:58.514 (   0.039s) [main thread     ]INFO| CONFIG: Loading config from file: /etc/strfry.conf
2023-07-21 19:26:58.529 (   0.054s) [main thread     ]INFO| CONFIG: successfully installed
2023-07-21 19:26:58.533 (   0.058s) [Websocket       ]INFO| Started websocket server on 0.0.0.0:7777

Now, we have to create the subdomain where strfry Nostr relay will be accessible.
You need to connect to your Nginx Proxy Manager admin UI and create a new proxy host with these settings :

"Details" tab (Websockets support is mandatory!, you can replace "strfry" by whatever you like, for instance : mybeautifulrelay.yourdomain.tld)

"Details" tab:

"SSL" tab:

And click on "Save"

If everything is OK, when you go to https://strfry.yourdomain.tld you should see :

To verify if strfry is working properly, you can test it with the (really useful!) website https://nostr.watch. You have to insert your relay URL into the nostr.watch URL like this : https://nostr.watch/relay/strfry.yourdomain.tld

You should see this :

If you are seeing your server as online, readable and writable, you made it !
You can add your Nostr strfry server to your Nostr prefered relay and begin to publish notes ! 🎇

Future work:

Once done, strfry will work like a charm but you may need to have more work to update strfry in the near future. I'm currently working on a bash script that will :

  • Update the "strfry" folder,
  • Backup the "strfry.conf" file,
  • Download the latest "strfry.conf" from strfry github repo,
  • Inject old configuration settings into the new "strfry.conf" file,
  • Compose again the stack (rebuilding the image to get the latest code updates),
  • etc.

Tell me if you need the script!

Voilààààà

See you soon in another Fractalized story!