Vechain
Infrastructure
Public Node

Setup Public Node

Install instructions are for CentOS

sudo yum install epel-release
sudo yum update -y
sudo yum install -y git golang
sudo useradd thor -g users
sudo su - thor
git clone https://github.com/vechain/thor.git
cd thor
make

Setup VechainThor Node

sudo vi /etc/systemd/system/thor.service

Adjust placeholders in <>:

/etc/systemd/system/thor.service
[Unit]
Description=Vechain-Thor-Node

Wants=network.target
After=syslog.target network-online.target

[Service]
Type=simple
User=thor
Group=users
ExecStart=/home/thor/thor/bin/thor --network main --data-dir <DATA_DIR> --api-cors --api-addr 127.0.0.1:8669
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target

Enable and auto start on boot:

sudo chmod 644 /etc/systemd/system/thor.service
sudo systemctl daemon-reload
sudo systemctl enable thor

Test with reboot and status check:

sudo reboot
sudo systemctl status thor

Setup Web-Server

Access via nginx

sudo yum install -y nginx

Example configuration: https://gist.github.com/libotony/851b09f9a1a3da935b419e6fe636f9aa (opens in a new tab)

sudo vi /etc/nginx/conf.d/node.conf
/etc/nginx/conf.d/node.conf
map $http_upgrade $connection_upgrade {
    default upgrade;
    "" close;
}
 
upstream BACKEND {
    server    127.0.0.1:8669;
    keepalive 64;
}
 
server {
    listen 80;
    server_name <DOMAIN>;
 
    add_header "Access-Control-Allow-Origin"    $http_origin always;
    add_header "Access-Control-Allow-Methods"   "GET, POST, OPTIONS, HEAD" always;
    add_header "Access-Control-Allow-Headers"   "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,x-genesis-id" always;
    add_header "Access-Control-Expose-Headers"  "x-genesis-id,x-thorest-ver" always;
    add_header "Access-Control-Max-Age"         86400 always;
 
    if ($request_method = "OPTIONS" ) {
        return 204 no-content;
    }
 
    location ^~ /subscriptions {
        proxy_redirect              off;
        proxy_read_timeout          1m;
        proxy_connect_timeout       1m;
        proxy_pass                  http://BACKEND;
        proxy_http_version          1.1;
        proxy_set_header Host       $host;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Origin     "";
    }
 
    location / {
        proxy_redirect              off;
        proxy_read_timeout          1m;
        proxy_connect_timeout       1m;
        proxy_pass                  http://BACKEND;
        proxy_http_version          1.1;
        proxy_set_header Host       $host;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection "";
    }
}

enable nginx as system service

sudo systemctl enable nginx
sudo systemctl start nginx

Setup SSL encryption

based on https://linuxize.com/post/secure-nginx-with-let-s-encrypt-on-centos-7/ (opens in a new tab)

sudo yum install -y certbot
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
sudo mkdir -p /var/lib/letsencrypt/.well-known
sudo chgrp nginx /var/lib/letsencrypt
sudo chmod g+s /var/lib/letsencrypt
sudo mkdir /etc/nginx/snippets
sudo vi /etc/nginx/snippets/letsencrypt.conf
/etc/nginx/snippets/letsencrypt.conf
location ^~ /.well-known/acme-challenge/ {
  allow all;
  root /var/lib/letsencrypt/;
  default_type "text/plain";
  try_files $uri =404;
}
sudo vi /etc/nginx/snippets/ssl.conf
/etc/nginx/snippets/ssl.conf
ssl_dhparam /etc/ssl/certs/dhparam.pem;
 
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
 
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;
 
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 30s;
 
add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload";
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;

adjust nginx configuration:

sudo vi /etc/nginx/conf.d/node.conf
/etc/nginx/conf.d/node.conf
map $http_upgrade $connection_upgrade {
    default upgrade;
    "" close;
}
 
upstream BACKEND {
    server    127.0.0.1:8669;
    keepalive 64;
}
 
server {
    listen 80;
    server_name <DOMAIN>;
 
    include snippets/letsencrypt.conf;
    return 301 https://$host$request_uri;
}
 
server {
    listen 443 ssl http2;
    server_name <DOMAIN>;
 
    ssl_certificate /etc/letsencrypt/live/<DOMAIN>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<DOMAIN>/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/<DOMAIN>/chain.pem;
    include snippets/ssl.conf;
    include snippets/letsencrypt.conf;
 
    add_header "Access-Control-Allow-Origin"    $http_origin always;
    add_header "Access-Control-Allow-Methods"   "GET, POST, OPTIONS, HEAD" always;
    add_header "Access-Control-Allow-Headers"   "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,x-genesis-id" always;
    add_header "Access-Control-Expose-Headers"  "x-genesis-id,x-thorest-ver" always;
    add_header "Access-Control-Max-Age"         86400 always;
 
    if ($request_method = "OPTIONS" ) {
        return 204 no-content;
    }
 
    location ^~ /subscriptions {
        proxy_redirect              off;
        proxy_read_timeout          1m;
        proxy_connect_timeout       1m;
        proxy_pass                  http://BACKEND;
        proxy_http_version          1.1;
        proxy_set_header Host       $host;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Origin     "";
    }
 
    location / {
        proxy_redirect              off;
        proxy_read_timeout          1m;
        proxy_connect_timeout       1m;
        proxy_pass                  http://BACKEND;
        proxy_http_version          1.1;
        proxy_set_header Host       $host;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection "";
    }
}

request certificate:

sudo certbot certonly --agree-tos --email <YOUR_EMAIL> --webroot -w /var/lib/letsencrypt/ -d <DOMAIN>

auto renew with cronjob:

sudo crontab -e
0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(3600))' && certbot -q renew --renew-hook "systemctl reload nginx"

Health-Check

The node is healthy if it is available via web and synchronized to the latest block. Calling /blocks/best on the node will provide information about the latest block and its unix timestamp.

The timestamp can be used to decide the age of the node and act accordingly:

yum install -y jq curl
#!/usr/bin/env bash
 
maxAge=$((10 * 10))
currentBlockTimestamp=$(curl -s http://localhost/blocks/best | jq .timestamp)
now=$(date +%s)
 
if [ $currentBlockTimestamp -le $((now - maxAge))  ] ; then
    echo "FAIL: older than $maxAge seconds"
    systemctl restart thor
else
    echo "OK"
fi

Update Node

su - thor
cd thor
git pull
make
# confirm version
./bin/thor -v
logout
 
sudo systemctl restart thor

Recommended Hardware

Minimum SpecificationRecommended Specification
CPU8 Core16 Core
RAM16 GB64 GB
Disk500 GB SSD2 TB SSD
Bandwidth10 Mbit20 Mbit
SecurityHardened secure configurationHardened secure configuration DDOS and IDS protection

Links