Home

Awesome

Nostr relay Setup on AWS

Intro

This is a step-by-step, complete guide on how to set up a Nostr relay with Nginx reverse proxy, SSL/TSL with some extra info on how to control and administer it. And no, there is no way you can do that properly in "5 minutes" The steps are based mostly on Amazon AWS EC2 instance but it can be easily applied to any other VPS provider like DigitalOcean, OVH, Linode etc... It will use nostr-rs-relay, a Rust implementation that uses SQLite database.

Requirements

Please note: Unless otherwise specified all commands are executed as root.

Ec2 rollout

<!-- end list --> <!-- end list -->
ssh -i your-key.pem ubuntu@ec2-X-XXX-XXX-XXX.eu-central-1.compute.amazonaws.com

Domain DNS configuration

Go to your Domain/DNS provider and use one of the following options:

  1. create a CNAME record that points to the public DNS of your EC2/VPS or
  2. create a A record entry that points to the public IP of your VPS

Installation Steps

Perform the updates


apt-get update
apt-get upgrade -y

Install rust tools (press 1 when asked)

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source /root/.cargo/env

Check that rust is installed and in your path

rustc --version
cargo --version

Install dependencies

apt-get install certbot build-essential sqlite3 libsqlite3-dev libssl-dev pkg-config nginx git -y
apt-get install net-tools whois -y

Compile nostr-rs

cd /opt
mkdir nostr-data
git clone https://github.com/scsibug/nostr-rs-relay.git
cd nostr-rs-relay
cargo build --release

Compilation can take about 10 minutes or so and produces a binary of about 19mb

Install nostr-rs

install target/release/nostr-rs-relay /usr/local/bin

Clean compilation artifacts

Since the instance you are using has little disk space (mine has 8Gb total) you don't want to waste valuable disk space and save it for nostr messages. Once successfully compiled the compilation artifacts can use up to 2Gb of space.

In order to reclaim that space, once the compiled binaries are installed and moved under /usr/local/bin, you can clean them up with

cargo clean

or (last ditch)

rm -Rf /opt/nostr-rs-relay/target

Uninstall compiler tools

Another surce of wasted space (other 2Gb) is in the /root/.rustup folder.

You want to remove/disinstall that too

rm -Rf /root/.rustup

Nostr Configuration

Create a nostr user to run the service and turn into that user

adduser --disabled-login nostr  # keep pressing enter until it ends 
su - nostr

Download the sample configuration file

cd /opt/nostr-rs-relay
wget https://raw.githubusercontent.com/scsibug/nostr-rs-relay/master/config.toml

Update / change the /opt/nostr-rs-relay/config.toml configuration file with your favourite editor (ex vim o nano) Parameters you might want to change:

Go back to user root

exit

First Test / Dry-run

Start nostr-relay as a stand alone foreground process and check the logs

RUST_LOG=warn,nostr_rs_relay=info /usr/local/bin/nostr-rs-relay

Check the INFO messages, then check that the DB and other files are there

ls -al /opt/nostr-data
total 112
drwxr-xr-x 2 root root  4096 Jan  9 07:54 ./
drwxr-xr-x 4 root root  4096 Jan  8 19:53 ../
-rw-r--r-- 1 root root 73728 Jan  9 07:55 nostr.db
-rw-r--r-- 1 root root 32768 Jan  9 07:55 nostr.db-shm
-rw-r--r-- 1 root root     0 Jan  9 07:55 nostr.db-wal

Give the permissions to the user nostr

chown -R nostr:nostr /opt/nostr-data

Service Setup

Systemd run script

Create a file /etc/systemd/system/nostr-relay.service with the following content

[Unit]
Description=Nostr Relay
After=network.target

[Service]
Type=simple
User=nostr
WorkingDirectory=/home/nostr
Environment=RUST_LOG=info,nostr_rs_relay=info
ExecStart=/usr/local/bin/nostr-rs-relay
Restart=on-failure

[Install]
WantedBy=multi-user.target

Then enable and start the nostr relay service

systemctl daemon-reload
systemctl enable nostr-relay.service
systemctl start nostr-relay.service

Check that the service is running

systemctl status nostr-relay.service
netstat -tnap | grep nostr
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      37860/nostr-rs-rela 

Setup NginX proxy

Site configuration

Create a nginx configuration file for the proxy and folders for HTTPS/TLS certificate creation

mkdir -p /var/www/nostr/.well-known/acme-challenge/
chown -R 33:33 /var/www/nostr
cd /etc/nginx/sites-available
vim nostr-relay.conf

Paste this as the content of the configuration file. ⚠️ Remember to use the domain you picked

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream websocket {
    server 127.0.0.1:8080;
}

server {
    listen 80;
    server_name nostr.domainname.com;  ## <<=== CHANGE THIS

    location /.well-known/acme-challenge/ {
    root /var/www/nostr;
    allow all;
    }

    location / {
        proxy_pass http://websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}

Enable and start the site

<!-- end list -->
ln -s /etc/nginx/sites-available/nostr-relay.conf /etc/nginx/sites-enabled/.
rm -f /etc/nginx/sites-enabled/default
nginx -t # must say "ok... test successful"
nginx -s reload

Run an external check

From another pc/server/vps (your laptop) check that you can connect to nostr through nginx

wget nostr.domainname.com  ## <<=== CHANGE THIS

this should download an index.html file. Check the content

cat index.html 
Please use a Nostr client to connect

Add HTTPS/TLS

Prepare dhparams

Create dhparams 4096 bit prime number(this will require a minute or so)

mkdir /etc/nginx/ssl
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096

Let the screen fill up with "....+..." etc

Certificate request

Try certificate request in dry-run mode

cd /var/www/nostr
certbot certonly --webroot -w . -d nostr.domainname.com --dry-run --agree-tos ## <<== Change with your domain

If all goes well do the real certificate request

cd /var/www/nostr
certbot certonly --webroot -w . -d nostr.domainname.com ## <<== Change with your domain

The issued certificates will be found in letsencrypt folder

ll /etc/letsencrypt/live/nostr.domainname.com/
total 12
drwxr-xr-x 2 root root 4096 Jan  9 09:04 ./
drwx------ 3 root root 4096 Jan  9 09:04 ../
-rw-r--r-- 1 root root  692 Jan  9 09:04 README
lrwxrwxrwx 1 root root   48 Jan  9 09:04 cert.pem -> ../../archive/nostr.domainname.com/cert1.pem
lrwxrwxrwx 1 root root   49 Jan  9 09:04 chain.pem -> ../../archive/nostr.domainname.com/chain1.pem
lrwxrwxrwx 1 root root   53 Jan  9 09:04 fullchain.pem -> ../../archive/nostr.domainname.com/fullchain1.pem
lrwxrwxrwx 1 root root   51 Jan  9 09:04 privkey.pem -> ../../archive/nostr.domainname.com/privkey1.pem

Now that you have a valid certificate set, replace the nginx configuration file to use it

cd /etc/nginx/sites-available
vim nostr-relay.conf

Update Nginx site file

Update the content as follows:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream websocket {
    server 127.0.0.1:8080;
}

server {
    listen 80;
    server_name nostr.domainname.com;

   location /.well-known/acme-challenge/ {
        root /var/www/nostr;
        allow all;
    }

    location / {
        return 301 https://nostr.domainname.com;
    }
}

server  {
    listen          443 ssl;
    server_name nostr.domainname.com;

    location / {
        proxy_pass http://websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
    }

    #### SSL ####

    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_dhparam ssl/dhparam.pem;
    ssl_ecdh_curve secp384r1;

    add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header Referrer-Policy same-origin;
    add_header Feature-Policy "geolocation none;midi none;notifications none;push none;sync-xhr none;microphone none;camera none;magnetometer none;gyroscope none;speaker self;vibrate none;fullscreen self;payment none;";


    ssl_certificate /etc/letsencrypt/live/nostr.domainname.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/nostr.domainname.com/privkey.pem; 
}

NOTE : remember to change all the occurrences of nostr.domainname.com with your domain

Check the new configuration and reload it into nginx

nginx -t
nginx -s reload

Now check that nginx + http + nostr are all working

netstat -tnap | grep 'nginx\|nostr'
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      37860/nostr-rs-rela 
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      8545/nginx: master  
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      8545/nginx: master 

Now check that they are reachable from the outside. Like before go to another pc/server and

wget nostr.domainname.com
--2023-01-09 10:22:05--  http://nostr.domainname.com/
Resolving nostr.domainname.com (nostr.domainname.com)... 3.123.142.158
Connecting to nostr.domainname.com (nostr.domainname.com)|3.123.142.158|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://nostr.domainname.com [following]
--2023-01-09 10:22:05--  https://nostr.domainname.com/
Connecting to nostr.domainname.com (nostr.domainname.com)|3.123.142.158|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 37 [text/plain]
Saving to: ‘index.html.1’

index.html.1                     100%[=============================>]      37  --.-KB/s    in 0s      

NOTE : the test is like the previous one, just this time it tries to connect to http first and get redirected (301) to https, then in https the file is download and its content are the same as before

cat index.html 
Please use a Nostr client to connect

See it at work

This section is mostly sysadmin related but can be very useful when trying to get "why it's not working"

Logs

Nostr relay logs

To keep an eye on what's goin on simply ask the logs

journalctl -f | grep --line-buffered nostr_rs_relay | cut -d' ' -f 10,12-100 

... you might want to set an alias for this. This will show the logs of nostr relay in real time. Press CTRL+C to stop it

Nginx logs

tail -f /var/log/nginx/*.log

You can fire up a tmux session if you don't want to start multiple consoles

Inspect Database

NOTE : ⚠️ carefull with this one as you might damage the db

Try some sqlite commands like:

cd /opt/nostr-data
sqlite3 nostr.db
...
sqlite> .databases
sqlite> .tables
sqlite> select * from event;
sqlite> select count(*) from event;
...

Connect your client

If you made it so far, congrats !!

Now is time to connect your client and see it at work !!

Just connect your client to wss://nostr.domainname.com. Send some messages, see the log get them in (almost) real time and find them in the database.

Pitfalls

Links