This guide is intended for validators running on bare-metal servers and explains how Union releases work. Check out the NixOS and the Kubernetes guide for more production-ready deployments.
Validators are the backbone of the network. Becoming one requires significant token bonding and delegations, and is not intended for non-power users.
Note Currently, directly downloading the uniond binary requires access to our private GitHub repository. We are not currently providing the general public access to our GitHub repositories. If you don’t have access to our private GitHub repository, you can still run our node using the public Docker image.
You can obtain uniond from a recent release.
Caution Double-check the version and architecture in the link before downloading.
curl --output uniond --location https://github.com/unionlabs/union/releases/download/$UNIOND_VERSION/uniond-release-x86_64-linux
Where UNIOND_VERSION is v0.24.0
Verify that the binary works on your server by running:
./uniond --help
For convenience, we can add the binary to the PATH, to make it callable from anywhere.
mv ./uniond /usr/bin/
We also provide containers in our package registry.
docker pull ghcr.io/unionlabs/uniond-release:$UNIOND_VERSION
Where UNIOND_VERSION is v0.24.0
When running the container, make sure to map a volume to the path passed in --home options to ensure data persistence. From here on the guide assumes the usage of a regular binary. The docker-compose section is more suited for docker users.
Caution uniond is a stateful application and interacts with the file system. Make sure to use volumes.
We’ll need to set up a few configuration files and obtain the genesis.json before we can run the node.
https://docs.union.build/genesis.json
First, set some environment variables, which are used throughout initialization.
export CHAIN_ID=union-testnet-8export MONIKER="Unionized Goblin"export KEY_NAME=aliceexport GENESIS_URL="https://union.build/genesis.json"
Then we’ll have uniond initialize our data and configuration directories. By default, /User/{USER}/.uniond is used.
uniond init $MONIKER --chain-id $CHAIN_ID
Next, edit ~/.union/config/config.toml. We’ll set the seeds to ensure your node can connect to the peer-to-peer network.
For union-testnet-8 replace seeds = "" with:
seeds = "c2bf0d5b2ad3a1df0f4e9cc32debffa239c0af90@testnet.seed.poisonphang.com:26656"
Download the genesis.json and copy it to your uniond home directory.
curl $GENESIS_URL | jq '.result.genesis' > ~/.union/config/genesis.json
To join as a validator, you need to submit a registration transaction. You can do this from the command line on your validator node.
First, add a wallet that holds Union tokens.
uniond keys add $KEY_NAME --recover
Caution For production usage, we recommend not storing the wallet on a server.
To submit the registration transaction and become a validator, you must submit a create-validator transaction:
uniond tx staking create-validator \ --amount 1000000muno \ --pubkey $(uniond tendermint show-validator) \ --moniker $MONIKER \ --chain-id $CHAIN_ID \ --from $KEY_NAME \ --commission-max-change-rate "0.1" \ --commission-max-rate "0.20" \ --commission-rate "0.1" \ --min-self-delegation "1"
Note If your own node isn’t set up to accept RPC request, you can send them to another node via the --node option.
We recommend running uniond as a systemd service. Create a file in /etc/systemd/system called uniond.service. Make sure to replace $USER with your username.
[Unit]Description=uniond[Service]Type=simpleRestart=alwaysRestartSec=1User=$USERExecStart=/usr/bin/uniond start[Install]WantedBy=multi-user.target
You should be able to view the node logs by executing
sudo journalctl -f --user uniond
It’s then recommended to back up these files from ~/.union/config in a secure location:
priv_validator_key.jsonnode_key.json
This guide assumes you are familiar with running a Union validator. If not, start with the validator guide.
docker-compose is a tool for running containers in a declarative manner. This allows for better automation, upgrades, and monitoring.
Our base compose.yml is fairly simple:
services: node: image: "ghcr.io/unionlabs/uniond-release:$UNIOND_VERSION" volumes: - ~/.union:/.union - /tmp:/tmp network_mode: "host" restart: unless-stopped command: start --home /.union
Pay special attention to the volumes key. Here we map an already initialized ~/.uniond directory to the node service. The ~/.uniond directory should contain a config and data directory. To properly set these up, check out the validator guide.
We suggest adding additional monitoring services, such as datadog.
NixOS deployments can use our module to easily manage their validator. It creates a systemd service with a production configuration
Caution The current example does not support remote signers yet. We will expand the guide once horcrux support is implemented.
Below is an example configuration.nix which can be used in production.
Caution The example currently uses git+ssh:// syntax rather than github: syntax because our repository is not public yet. Once it is, this will be simplified. When this happens, GIT_LFS_SKIP_SMUDGE will also no longer be required.
{ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; union.url = "git+ssh://git@github.com/unionlabs/union"; }; outputs = {self,nixpkgs,union, ... }: { nixosConfigurations.testnet-validator = let system = "x86_64-linux"; pkgs = importnixpkgs { inherit system; }; in nixpkgs.lib.nixosSystem { inherit system; modules = [ union.nixosModules.unionvisor { system.stateVersion = "23.11"; # Base configuration for openstack-based VPSs imports = [ "${nixpkgs}/nixos/modules/virtualisation/openstack-config.nix" ]; # Allow other validators to reach you networking.firewall.allowedTCPPorts = [ 80 443 26656 26657 ]; # Unionvisor module configuration services.unionvisor = { enable = true; moniker = "your-testnet-moniker"; }; # OPTIONAL: Some useful inspection tools for when you SSH into your validator environment.systemPackages = with pkgs; [ bat bottom helix jq fastfetch tree ]; } ]; }; };}
You can then deploy the configuration by running
GIT_LFS_SKIP_SMUDGE=1 nixos-rebuild switch --flake .\#testnet-validator --target-host user@validator.domain -L
To upgrade to newer versions, simply run
nix flake updateGIT_LFS_SKIP_SMUDGE=1 nixos-rebuild switch --flake .\#testnet-validator --target-host user@validator.domain -L
This will pull in the latest changes to union configurations and prepare your node for future upgrades.
After successfully running your node with uniond, you can refer to this guide to aid you in configuring your node.
This is not a complete guide to all node configuration options, this is intended to help ensure that your node is fully operational.
This guide will assume that you’re starting at the root of your Union node configuration (located at ~/.union/ by default or ~/.unionvisor/home if you’re using Unionvisor).
Located in config/client.toml, this file is host to client settings.
Update this value to ensure that your client is supplied with the correct chain ID.
For the Union Testnet, this value should be "union-testnet-8".
# The network chain IDchain-id = "union-testnet-8"
This will determine which address your client will listen for Tendermint RPC request on.
This will default to "tcp://localhost:26657", setting this to "tcp://0.0.0.0:26657" will ensure it’s listening on every available network interface.
# <host>:<port> to Tendermint RPC interface for this chainnode = "tcp://0.0.0.0:26657"
Located in config/app.toml, this file is host to app settings.
Located under the “Base Configuration” section of config/app.toml.
While optional, you may wish to ensure your node receives a minimum fee when processing transactions. Whatever you choose for this value, ensure it uses the correct denom. For the Union Testnet, the correct denom is muno.
# The minimum gas prices a validator is willing to accept for processing a# transaction. A transaction's fees must meet the minimum of any denomination# specified in this config (e.g. 0.25token1;0.0001token2).minimum-gas-prices = "0muno"
Located under the “Base Configuration” section of config/app.toml.
Several options are available here, ensure you’ve selected the one that best fits your nodes storage capabilities.
# default: the last 362880 states are kept, pruning at 10 block intervals# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node)# everything: 2 latest states will be kept; pruning at 10 block intervals.# custom: allow pruning options to be manually specified through 'pruning-keep-recent', and 'pruning-interval'pruning = "default"
Located in config/config.toml, this file is host to many settings.
To achieve a low target block time, the Union team asks validators to make the following changes to their configuration.
Located in the consensus TOML table under the “Consensus Configuration Options” section.
# How long we wait for a proposal block before pre-voting niltimeout_propose = "3s"# How much timeout_propose increases with each roundtimeout_propose_delta = "500ms"# How long we wait after receiving +2/3 prevotes for “anything” (ie. not a single block or nil)timeout_prevote = "1s"# How much the timeout_prevote increases with each roundtimeout_prevote_delta = "500ms"# How long we wait after receiving +2/3 precommits for “anything” (ie. not a single block or nil)timeout_precommit = "1s"# How much the timeout_precommit increases with each roundtimeout_precommit_delta = "500ms"# How long we wait after committing a block, before starting on the new# height (this gives us a chance to receive some more precommits, even# though we already have +2/3).timeout_commit = "1s"
Located in the rpc TOML table under the “RPC Server Configuration Options” section.
You’ll want to ensure your node is configured to accept rpc connections. To do so, set this value to the appropriate address.
For example, to listen on every available network interface - set this to "tcp://0.0.0.0:26657".
# TCP or UNIX socket address for the RPC server to listen onladdr = "tcp://0.0.0.0:26657"
Located in the p2p TOML table under the “P2P Configuration Options” section.
You’ll want to ensure your node is configured to accept p2p connections. To do so, set this value to the appropriate address.
For example, to listen on every available network interface - set this to "tcp://0.0.0.0:26656".
# Address to listen for incoming connectionsladdr = "tcp://0.0.0.0:26656"
Located in the p2p TOML table under the “P2P Configuration Options” section.
If you’ve configured a domain name for your node, this is the place to inform your node of it.
# Address to advertise to peers for them to dial# If empty, will use the same port as the laddr,# and will introspect on the listener or use UPnP# to figure out the address. ip and port are required# example: 159.89.10.97:26656external_address = "example.com:26656"
Located in the p2p TOML table under the “P2P Configuration Options” section.
Seed nodes help orchestrate initial connections to the network. For union-testnet-8, the seed nodes are:
seeds = "c2bf0d5b2ad3a1df0f4e9cc32debffa239c0af90@testnet.seed.poisonphang.com:26656"
Located in the p2p TOML table under the “P2P Configuration Options” section.
If you’d like to be a seed node, be sure to set this to true.
Caution If you plan for your node to be a validator, it should not also be a seed node.
# Seed mode, in which node constantly crawls the network and looks for# peers. If another node asks it for addresses, it responds and disconnects.## Does not work if the peer-exchange reactor is disabled.seed_mode = false

