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
| Role | Finalizer votes | Signing keys | Intended workload |
|---|---|---|---|
| Vote relay / sentry | vote-threads = 4 | None | Carry P2P blocks, transactions, and finalizer votes between private producers and public peers |
| Read-only API | vote-threads = 0 | None | Serve chain API calls behind a TLS reverse proxy |
| SHiP | vote-threads = 0 | None | Stream 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.daton these non-producing profiles. - Keep public RPC, P2P, and SHiP firewall policies separate and least-privileged.