Skip to main content

How To Host A Staking Voter

· 18 min read


Atto relies on voter nodes across the world to participate in ORV and help keep transaction confirmation responsive and decentralized. A separate distribution policy may reward eligible voter operators.

Voter rewards are separate from ORV consensus. Operating or registering a node does not by itself establish reward eligibility; current scoring, sharing, and payment rules determine whether a payout address may receive variable rewards.

Thanks to its lightweight design, a voter can run on modest hardware. When the node and MySQL share a host, use at least 2 GB of physical RAM for both bootstrap and continued operation. A synchronized installation may sometimes run with 1 GB, but that is not recommended. Higher-end hardware can improve network performance. A planned scoring model may later take performance into account, but current reward eligibility follows the published rules.

This guide explains the process of setting up a voter. Readers that have a fresh, working installation of Linux can skip the next section to Securing the Server, while readers who have already set up secure remote login and a user account can skip to Running the Node.

Obtaining a Private Server

While the node can be run on almost any computer, this guide targets computers and servers running Ubuntu Linux.

If you have a computer lying around, then simply install Ubuntu Desktop on it and skip to Securing the Server. If you're not paying for a static IP address, your home network is likely behind NAT, so you will also need to set up a reverse tunnel (for example, install Tailscale and enable Funnel).

If you don't have a spare computer, you can rent a private server, with some being priced as low as $5/month with no commitment while still meeting the minimum requirements. For less hassle and a positive impact on network performance, however, it's best to follow the recommended requirements outlined below.

If you're going for a private server, you need to select a private server provider.

Choosing a Private Server Provider

Different providers have servers in different locations, although some may have servers within the same city or datacenter. To promote provider and geographic diversity, the following providers are listed in a random order:

    info

    These providers are examples, not a compatibility list. You can use another provider if it offers an AMD64 (x86_64) Linux VPS that can run Docker, meets the requirements below, and permits this type of node under its terms of service.

    Choose a provider and plan that meets these requirements. The next section will guide your decision.

    Choosing a Plan

    Most providers offer shared CPU plans (virtual private servers, or VPSes) and dedicated CPU plans (dedicated servers). VPSes tend to be cheaper and are adequate for hosting a node.

    Among VPSes, some providers offer both KVM (virtualized) and LXC (containerized) VPSes. LXC servers are unsupported because the Atto node is released as a Docker container, which cannot run in an already containerized environment.

    You will be given several options for the allocated hardware. Choose a plan that meets the following requirements and recommendations:

    • Architecture: AMD64 (x86_64)
    • Storage: 20 GB or greater SSD or NVMe SSD
    • RAM: 2 GB or greater

    When asked for the operating system, picking the latest LTS version of Ubuntu will enable you to smoothly follow along with this guide.

    Follow the provider's instructions to pick the right plan, create an account and pay your first payment. After this, you should be greeted with your new server's management interface.

    Logging Into Your Server

    Now that you have a server, navigate to its console/dashboard (if you're not already there) and start your server.

    Every provider listed above offers console access: noez.de and Vultr provide browser-based noVNC consoles, DigitalOcean provides browser and recovery consoles, Linode provides its browser-based Lish console, and Hostinger provides a browser terminal. Contabo provides VNC access, but requires a separate VNC client. You can use these options for the initial setup and recovery.

    SSH is more convenient for regular administration, but making it reachable from the public internet exposes another login service that you must protect. If you enable it, prefer key-based authentication and restrict access to your IP address using the provider's firewall. The provider's instructions will explain how to configure it safely.

    Open the provider's console and authenticate if prompted. You should see a prompt ending with # (for the root user) or $ (for a regular user) on the last line. Your next step is to complete the basic server setup.

    Securing the Server

    Security and firewall controls vary by provider. Follow your provider's guidance before storing a voter private key on the server.

    Creating a Non-Root User

    At this point, you should be logged into the computer/server as root. If you're logged in as another user or created a new user while installing Ubuntu, you can skip this section.

    Decide on a username, then run the following two commands to create a new user and allow it to use sudo. Replace <username> with your chosen username.

    adduser <username>
    usermod -aG sudo <username>

    For example, adduser waldo will create a new user named "waldo" and ask you to create a new password.

    adduser will ask you a few questions, but you can leave all of them blank. When it asks for a password, enter a strong, randomly generated password. A sequence of four or more randomly generated words is recommended, as this is hard to guess but easy to remember.

    Before ending the root session, switch to the new user and test that sudo works:

    su - <username>
    sudo echo 'sudo works!'

    Enter the new user's password if prompted. If you see sudo works!, the account is ready and you should stop running routine commands as root. If the provider's console automatically logs in as root, run su - <username> before continuing.

    Updating the System

    Updating the system ensures that all of the latest security patches are installed, fixing well-known bugs that could leave your server vulnerable.

    Run sudo apt update to tell the package manager apt to find updates to packages. Then run sudo apt upgrade to download and install the updates.

    When faced with the following dialog, choose "keep the local version currently installed".

    ┌───────────────────────┤ Configuring openssh-server ├───────────────────────┐
    │ A new version (/tmp/tmp.XXXXXXXXXX) of configuration file │
    │ /path/to/file is available, but the version installed currently has been │
    │ locally modified. │
    │ │
    │ What do you want to do about modified configuration file? │
    │ │
    │ install the package maintainer's version │
    │ keep the local version currently installed │
    │ show the differences between the versions │
    │ show a side-by-side difference between the versions │
    │ show a 3-way difference between available versions │
    │ do a 3-way merge between available versions │
    │ start a new shell to examine the situation │
    │ │
    │ <Ok> │
    │ │
    └────────────────────────────────────────────────────────────────────────────┘

    Your system should be updated on a regular basis and as soon as you hear about a security vulnerability that affects Linux.

    Now that your server is secure, you can set up the node.

    Running the Node

    Setting up the node involves setting up a project directory, creating the .env and compose.yaml files, installing Docker and Docker Compose, and starting the containers.

    When creating the configuration files, you will need to configure the private key that your node will use to sign its votes and derive its address.

    Generating a Private Key

    Generate a random byte sequence (which qualifies as a private key) by running openssl rand -hex 32 and copying the result. Generate a visibly different MySQL password with printf 'mysql_%s\n' "$(openssl rand -hex 24)".

    danger

    Store this key safely and do not share it with anyone, as it can be used to impersonate your node.

    Now that you have a private key and database password, you can create the files that tell Docker Compose what containers to spawn.

    Setting Up the Project Directory

    Run the following commands on your server from a terminal:

    cd ~/ # Navigate to your user's home directory. Your working directory is now ~/.
    mkdir atto-voter-node # Make a new directory inside your working directory.
    cd atto-voter-node/ # Navigate to the new directory. Your working directory is now ~/atto-voter-node/.
    touch .env compose.yaml
    printf '.env\n' > .gitignore
    chmod 600 .env

    Follow the Docker example for a voter node with a direct private key. Put the generated database password, private key, and public URI in .env; keep the image, memory limits, ports, and other settings in compose.yaml. Replace the .invalid hostname with your server's public hostname, or use ws://<IP address>:8082 when the gossip port is not behind TLS termination.

    These files will be read by Docker Compose, but you need to install Docker and Docker Compose first.

    Installing Docker and the Docker Compose Plugin

    Because Docker and the Docker Compose plugin aren't included in Ubuntu's official package repositories, they can't be installed with sudo apt install yet. To fix this, add Docker's repository to apt by running the following commands one at a time.

    # Add Docker's official GPG key:
    sudo apt update
    sudo apt install ca-certificates curl jq
    sudo install -m 0755 -d /etc/apt/keyrings
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    sudo chmod a+r /etc/apt/keyrings/docker.asc

    # Add the repository to Apt sources:
    sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
    Types: deb
    URIs: https://download.docker.com/linux/ubuntu
    Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
    Components: stable
    Signed-By: /etc/apt/keyrings/docker.asc
    EOF

    sudo apt update

    Finally, install Docker and Docker Compose with sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin.

    tip

    If you get stuck at any point, visit Docker's official documentation for up to date instructions.

    Before you can start the node, you need to make sure it has enough memory.

    Checking Available Memory

    Run free -h and confirm that the server has about 2 GB of physical RAM. A 2 GB plan usually appears as roughly 1.9 GiB in this output. This is a whole-server requirement, not the node process's requirement: the Compose configuration limits the node to 1 GB but does not hard-cap MySQL, because a 512 MB database limit can cause a fresh bootstrap to fail. Do not count swap toward this requirement: under bootstrap load, swapping can turn memory pressure into long disk stalls. Keep host swap available as an emergency safeguard. The Compose configuration allows the node to use up to 1 GB of swap, but this may reduce synchronization throughput, increase voting latency, and increase disk activity.

    warning

    An all-in-one voter with an already-bootstrapped database may run on a 1 GB server when swap is enabled. Do not use a 1 GB server for a fresh bootstrap. At least 2 GB of physical RAM is recommended to reduce swap use, preserve synchronization throughput, and keep voting latency low. Do not count swap as physical RAM when comparing server plans.

    Adding a Swapfile on a 1 GB Server

    If you are moving an already-bootstrapped database to a server with only 1 GB of physical RAM, run swapon --show to check whether it already has about 2 GB of swap. If it does not and /swapfile does not exist, create and enable a 2 GB swapfile:

    sudo fallocate -l 2G /swapfile
    sudo chmod 0600 /swapfile
    sudo mkswap /swapfile
    sudo swapon /swapfile

    If /swapfile already exists but is not listed by swapon --show, enable it with sudo swapon /swapfile instead of recreating it.

    Make the swapfile available again after a reboot without adding a duplicate entry to /etc/fstab:

    sudo grep -qF '/swapfile none swap sw 0 0' /etc/fstab || \
    echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

    Run free -h again and confirm that the Swap row reports approximately 2 GB before continuing.

    Starting the node

    First ensure that you have permission to interact with Docker:

    sudo gpasswd -a <username> docker

    Membership in the docker group grants root-level control of the host. Add only the trusted account that will operate the node.

    Then log out with Ctrl+D and log back in.

    Before starting the node, allow inbound TCP port 8082 in your provider's network firewall and the server's host firewall. Keep ports 8080 and 8081 closed to the internet. Publishing the port in Docker Compose does not bypass either firewall.

    Ensure you're in the atto-voter-node project directory you created and tell Docker to read the compose.yaml file in the current directory and start the containers in the background:

    cd ~/atto-voter-node
    if docker compose config --quiet; then
    echo "Compose configuration: valid"
    docker compose up -d
    docker compose ps
    else
    echo "Compose configuration: invalid"
    fi

    Docker will download the images and start the two containers: The node, which participates in the network, and the MySQL database, where the node stores all transactions. The node waits until MySQL accepts the configured credentials before starting.

    Because the containers are started in the background, you can't tell whether they are running successfully yet. The next section provides you with the tools needed to inspect your node and make sure it's operating correctly.

    Verifying that the node is running

    First check the node's health, then follow its activities in the log:

    if [ "$(curl --silent --show-error --output /dev/null --write-out '%{http_code}' http://127.0.0.1:8081/health)" = "200" ]; then
    echo "Node health: healthy"
    else
    echo "Node health: not healthy"
    fi
    docker compose logs --tail 200 -f node

    You should see messages like "Saved X unchecked transactions" and "Resolved X unchecked transactions", which indicate that the node is bootstrapping.

    Stop following the log by interrupting the currently running command with Ctrl+C.

    Bootstrapping means that your node is downloading a full history of all transactions on the network. Check its progress with:

    {
    local_height=$(curl -fsSL http://127.0.0.1:8081/metrics/account.height.count \
    | jq -r '.measurements[] | select(.statistic == "VALUE") | (.value | floor)') &&
    public_height=$(curl -fsSL https://gatekeeper.live.application.atto.cash/projections/metrics \
    | jq -r '.metrics[] | select(.name == "account.height.count") | .value') &&
    unchecked=$(curl -fsSL http://127.0.0.1:8081/metrics/transactions.unchecked.count \
    | jq -r '.measurements[] | select(.statistic == "VALUE") | (.value | floor)') &&
    progress=$(awk -v local="$local_height" -v public="$public_height" \
    'BEGIN { if (public <= 0) exit 1; value = 100 * local / public; if (value > 100) value = 100; printf "%.1f", value }') &&
    printf 'Local account height: %s\nPublic account height: %s\nUnchecked transactions: %s\n' \
    "$local_height" "$public_height" "$unchecked" &&
    if [ "$local_height" = "$public_height" ] && [ "$unchecked" = "0" ]; then
    echo "Synchronization: caught up"
    else
    printf 'Synchronization: still in progress (height %s%%; %s unchecked remaining)\n' \
    "$progress" "$unchecked"
    fi
    }

    The command reports Synchronization: caught up only when the local and public heights match and the unchecked count is zero. The unchecked metric refreshes once per minute, so wait at least one minute and run the command again before relying on the result.

    Congratulations, you are now running a node! It may still be bootstrapping. To ensure that it keeps operating smoothly, you need to learn how to maintain it on a regular basis. After that, you will learn how to register for staking.

    Maintaining the Node

    Maintaining a node involves four steps:

    1. Updating the system to receive security updates.
    2. Checking the node's log for issues.
    3. Checking the node's database for unchecked transactions.
    4. Updating the node Docker image to the latest version.

    Updating the System

    Run sudo apt update. When that finishes, run sudo apt upgrade.

    Checking the Node Log

    Repeat the health check from the previous section, followed by docker compose logs --tail 200 node.

    Inspect the logs to determine if your node is still connected to other nodes.

    Checking for Unchecked Transactions

    Your node should normally have zero unchecked transactions after it has caught up. During a fresh bootstrap or recovery from downtime, the count may rise before it falls. Repeat the synchronization command from the previous section to check it.

    If there are any unchecked transactions and they aren't going away, update the node, wait a few hours and check again.

    Updating the Node Docker Image

    To update the node's Docker Image:

    1. Enter the project directory containing the compose.yaml file.
    2. Run docker compose pull to download updates for all images used by the project, and docker compose up -d to restart the containers for which there were any updates.

    Now that you know how to maintain your node, you are ready to request registration as a staking voter.

    Registering for Staking

    To request staking registration, you need to add information about yourself and your node to the known-addresses GitHub repository. The information about you is required to verify your authenticity in the future so that no one else may make changes to your voter's details such as the payout address or the percentage shared with your delegates.

    The first piece of information you need is your node's address.

    Getting Your Node's Address

    Run the following command to get your node's address:

    curl -fsSL http://127.0.0.1:8081/metrics/node.version \
    | jq -r '.availableTags[] | select(.tag == "address") | "Voter address: \(.values[0])"'

    Adding your information

    Create an account on GitHub. Then, fork the known-addresses repo and start editing entities.ndjson and add the following information on a new line, using the exact same format as the existing lines:

    • entity: Short identifier used to link from other files. Lowercase letters, numbers and dashes.
    • organization: Canonical organization identifier (can match 'entity' or be more specific).
    • label: Human-friendly display name.
    • description: Short description of what this entity is.
    • tags: Classification tags for this entity.
    • website: Public website for this entity.

    Commit your changes, then click on "Create pull request", name it "Add <entity> entity", and click on "Create pull request" again.

    Start editing voters.json, adding the following information on a new line:

    • address: Voter address (Atto URI) that you got in the previous section.
    • label: Short human-friendly name.
    • entity: Entity identifier this address belongs to (must match an entity from entities.ndjson).
    • payToAddress: If null, this voter does NOT participate in staking rewards. Must never be the same as 'address'.
    • sharePercentage: Percentage of rewards this voter shares with delegators.
    • addedAt: Date the entry was created (UTC).
    • updatedAt: Date the entry was last updated (UTC) (same as addedAt).
    • description: Longer description of the voter.

    Commit and create a pull request as before, naming it "Add <entity> voter".

    You will now need to wait for your pull requests to get accepted, or make changes if requested by the Atto team.

    Once your pull requests have been accepted, your voter will be added to the wallet. At this point, you should elect it with your own accounts as well as encourage others to do so, in order to pass the minimum weight threshold needed for it to start voting.

    Frequently Asked Questions (FAQ)

    Why does the wallet say "Last voted: Unknown"?

    Your node hasn't voted. This could be because it's still bootstrapping, doesn't have enough weight or is experiencing an error.

    Accumulating weight takes time as your voter gains popularity and more people delegate to it. In the meantime, you can check if it has finished bootstrapping and is experiencing no errors by checking the node's log and the number of resolved and unchecked transactions.


    Your computer is now operating an Atto voter. Once it is caught up and has enough delegated weight, it can participate in ORV. Thank you for making a difference!