Skip to main content

Run a Non-Block-Producing Node

This guide covers steady-state nodes on a Telos network where Savanna instant finality is already active. Use it for vote relays, read-only APIs, and State History (SHiP) services. A backup producer is a producer-capable host and must instead follow the BP setup guide and finalizer safety rules.

Choose one role

RoleFinalizer votesSigning keysIntended workload
Vote relay / sentryvote-threads = 4NoneCarry P2P blocks, transactions, and finalizer votes between private producers and public peers
Read-only APIvote-threads = 0NoneServe chain API calls behind a TLS reverse proxy
SHiPvote-threads = 0NoneStream traces, chain state, and finality data to trusted indexers

Only relays on a finalizer's P2P path need vote processing. Pure API and SHiP nodes should not load a BLS key, producer-name, producer_plugin, or a finalizers-dir.

1. Install TelosZero Core

Follow Install TelosZero Core and verify the approved package checksum and full version. The current templates target TelosZero Core 1.2.2.

Do not install Leap 4/5 for a new Telos node, and do not carry forward removed options such as producer-threads, chain-id, wasm-runtime = wabt, or the legacy history_plugin.

2. Prepare an instance

The maintained Telos node-template repository contains separate profiles for vote relays, APIs, and SHiP. Clone it and create a service account:

git clone https://github.com/telosnetwork/node-template.git
cd node-template

id -u telos >/dev/null 2>&1 \
|| sudo useradd --system --home-dir /var/lib/telos --shell /usr/sbin/nologin telos

Choose an instance name prefixed by its network, such as mainnet-relay-a, mainnet-api, or mainnet-ship. Create root-owned configuration and writable instance data directories:

export INSTANCE=mainnet-api

sudo install -d -o root -g telos -m 0750 "/etc/telos/$INSTANCE"
sudo install -d -o telos -g telos -m 0750 "/var/lib/telos/$INSTANCE"

3. Render and review the role profile

Render exactly one network and role:

./scripts/render-config.sh mainnet api "$INSTANCE" /tmp/telos-config.ini

Valid roles are vote-relay, api, and ship. The renderer appends endpoints currently marked verified by the Telos validator service. Treat that list as a starting point: curate for operator and geographic diversity and monitor every peer.

Review the rendered configuration before installation. Every role uses:

chain-state-db-size-mb = 32768
protocol-features-dir = /var/lib/telos/<INSTANCE>/protocol_features
plugin = eosio::chain_plugin
plugin = eosio::net_plugin

The 32768 MiB chain-state capacity is a starting point for a host with 64 GB RAM. Keep it below usable physical memory and increase it deliberately as measured state grows.

Vote relay

A vote relay must accept and forward finalizer votes but has no signing keys:

http-server-address = 127.0.0.1:8888
p2p-listen-endpoint = 0.0.0.0:9876
vote-threads = 4

plugin = eosio::http_plugin
plugin = eosio::chain_plugin
plugin = eosio::net_plugin

Place public peers only on relays and other non-producing nodes. A private producer should connect only to BP-controlled relays.

Read-only API

Bind nodeos to loopback and expose it through a hardened TLS reverse proxy:

http-server-address = 127.0.0.1:8888
p2p-listen-endpoint = 0.0.0.0:9876
vote-threads = 0
api-accept-transactions = false

plugin = eosio::http_plugin
plugin = eosio::chain_plugin
plugin = eosio::chain_api_plugin
plugin = eosio::net_plugin

Do not enable wildcard CORS on a public RPC. Configure exact trusted origins at the reverse proxy only when a browser application requires them.

State History (SHiP)

Use SHiP only on a dedicated indexing node and keep its WebSocket endpoint on loopback or a private interface:

vote-threads = 0
api-accept-transactions = false

plugin = eosio::http_plugin
plugin = eosio::chain_plugin
plugin = eosio::chain_api_plugin
plugin = eosio::net_plugin
plugin = eosio::state_history_plugin

state-history-endpoint = 127.0.0.1:8080
trace-history = true
chain-state-history = true
finality-data-history = true
state-history-dir = state-history

finality-data-history = true makes Savanna finality data available to SHiP consumers. Plan storage and retention for the full history workload.

Install the reviewed profile read-only to the service account:

sudo install -o root -g telos -m 0640 /tmp/telos-config.ini \
"/etc/telos/$INSTANCE/config.ini"
sudo install -o root -g telos -m 0640 logging.json \
"/etc/telos/$INSTANCE/logging.json"

4. Bootstrap from trusted chain state

chain-id is not a valid nodeos option. The genesis or snapshot state selects the network. For a new mainnet or testnet data directory, obtain a recent snapshot from a trusted provider, verify its published checksum, and start it once in the foreground:

sudo -u telos /usr/bin/nodeos \
--config-dir "/etc/telos/$INSTANCE" \
--data-dir "/var/lib/telos/$INSTANCE" \
--snapshot "/var/lib/telos/$INSTANCE/snapshots/<snapshot-file>.bin"

Stop cleanly after the snapshot loads. Do not retain --snapshot for normal restarts.

5. Run the instance with systemd

Install the instance service supplied by node-template:

sudo install -o root -g root -m 0644 systemd/telos-nodeos@.service \
/etc/systemd/system/telos-nodeos@.service
sudo systemctl daemon-reload
sudo systemctl enable --now "telos-nodeos@$INSTANCE"
sudo systemctl status "telos-nodeos@$INSTANCE"
sudo journalctl -u "telos-nodeos@$INSTANCE" -f

The instance service uses /etc/telos/%i and /var/lib/telos/%i, preventing a mainnet, testnet, API, relay, or SHiP process from accidentally sharing runtime state.

6. Verify network, sync, and finality

Query the loopback API:

curl -s http://127.0.0.1:8888/v1/chain/get_info | jq '{
chain_id,
server_full_version_string,
head_block_num,
last_irreversible_block_num,
head_block_time,
lib_lag: (.head_block_num - .last_irreversible_block_num)
}'

Expected chain IDs:

Mainnet: 4667b205c6838ef70ff7988f6e8257e8be0e1284a2f59699054a018f743b1d11
Testnet: 1eaa0824707c8c16bd25145493bf062aecddfeb56c736f6ba6397f3195f33c9f

Confirm that:

  • the chain ID and approved full version are exact;
  • head and irreversible blocks advance and the node reaches network head;
  • P2P peers remain diverse and stable;
  • a vote relay carries live vote traffic in both directions;
  • API nodes reject transaction submission as intended;
  • SHiP consumers receive traces, chain state, and finality data;
  • logs contain no persistent fork, database, vote, or peer errors.

Alert on sustained deviations from the network's normal head-to-LIB lag rather than one hard-coded threshold.

Ongoing operations

  • Refresh and review public peers through node-template's peer updater.
  • Keep TelosZero upgrades, snapshot procedures, and storage growth under change control.
  • Back up configuration and indexing data according to role, but never introduce a BLS key or safety.dat on these non-producing profiles.
  • Keep public RPC, P2P, and SHiP firewall policies separate and least-privileged.