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
- AWS account with some cash on it OR
- any other cloud/VPS provider will do, just change this steps accordingly
- root privileges
- a domain you own and can set DNS records
- in this guide we'll pretend is nostr.domainname.com
Please note: Unless otherwise specified all commands are executed as root.
Ec2 rollout
- Pick your EC2 instance, for example:
- t3micro - 2vCPU - 1GiB Ram - 8Gib HD
- x86
- Linux Ubuntu
- If on AWS:
- generate and store the keypair and store it
- select/create a security group that allows ssh, http, https
- launch the instance
- connect to the instance with ssh (ex:)
ssh -i your-key.pem ubuntu@ec2-X-XXX-XXX-XXX.eu-central-1.compute.amazonaws.com
- otherwise: (DigitalOcean, OVH...)
- get means to access the VPS via ssh
- check with your provider or manually setup the firewall (iptables, ufw...)
- connect to the instance with ssh
Domain DNS configuration
Go to your Domain/DNS provider and use one of the following options:
- create a CNAME record that points to the public DNS of your EC2/VPS or
- 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:
- relay_url : put nostr.domainname.com ( ⚠️ replace with the URL of the domain name you own and that points to the server)
- name : don't be shy ...
- description : ... introduce yourself
- pubkey : your public key
- contact : your email
- tracing : ⚠️ leave commented otherwise it trows errors and won't start
- data_directory : set it to /opt/nostr-data/
- address : set it to 127.0.0.1 as we will later use nginx as a proxy
- remote_ip_header : set it to "x-forwarded-for" for logging real client IP addresses
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
- Do not install rust, cargo etc using apt. Just use rustup