Introduction
This guide provides a step-by-step walkthrough on how to run a dYdX non-validating full node on Testnet using an AWS EC2 instance. It's designed to help you explore the technology, not for production use due to security and maintainability concerns.
The dYdX Labs team provides a detailed guide and script to help set up a node. Inspired by those resources, we will use a similar code that has been tuned for readability. We'll cover three tasks:
- Running the node.
- Examining its basic diagnostics.
- Performing an automated upgrade of the node binary using Cosmovisor.
Let's get started!
Launching an EC2 Instance
To begin, you'll need to launch an EC2 instance on AWS:
- Go to the EC2 Dashboard and click Launch Instance.
- Choose the Ubuntu Server 22.04.3 (or later) AMI.
- Select an instance type with at least 8 vCPUs and 64 GB RAM (e.g., r5.2xlarge).
- Under Configure storage, select a 500 GB SSD.
- Create a new Security Group:
- Set Inbound Rules to allow All traffic (0.0.0.0/0).
- Choose or create a key pair for SSH access.
- Click Launch to start the instance.
Note: Opening all ports on the security group is acceptable for exploration and testing but is highly discouraged for production environments due to significant security risks. In real-world deployments, limit access to only necessary ports.
Spinning Up a Node
Once your EC2 instance is running, SSH into it using your configured key pair:
ssh -i /path/to/your-key.pem ubuntu@YOUR_EC2_PUBLIC_IP
Next, fetch the node initialization script provided by dYdX Operations subDAO:
wget https://raw.githubusercontent.com/dydxopsdao/node-tools/refs/heads/main/full-node-init.sh
Review the constants in the `full-node-init.sh` script. By default, it targets Testnet. The values for `CHAIN_ID`, `BASE_SNAPSHOT_URL`, and `SEED_NODES` come from the official dYdX docs.
`PROTOCOLD_VERSION` should match the latest protocold binary (the actual node engine) from the protocol's releases page. At the time of writing, the current protocol version is v8.0.x. We'll start with v8.0.8 and later upgrade to v8.0.9 (which contains an important security patch).
Finally, provide a unique `NODE_NAME` for your node. By default, a generic name with a random component will be used to avoid duplicating node names.
Enable execution of the script and run it:
chmod 755 full-node-init.sh
# Optional: Start a tmux session (see the pro tip below)
./full-node-init.sh
This process can take between 15 minutes and 3 hours, with the longest operation typically being the fetching of a snapshot file (which can be over 100 GB). Once the script finishes, you should see:
✅ Setup complete! The node is ready to start.
Pro Tip: To prevent the download operation from failing if your SSH connection is lost, run the process within a tmux session. This allows the script to continue running on the server without an attached user session.
What the Script Did
The script performs the following actions:
- Installs several low-level system dependencies, tools, Golang, and Cosmovisor. Cosmovisor is a process manager for Cosmos SDK application binaries that automates application binary switches during chain upgrades.
- Downloads the `dydxprotocold` binary at the specified `PROTOCOLD_VERSION`.
- Initializes the node with the `NODE_NAME`, creating its unique ID.
- Updates the configuration with the Testnet `SEED_NODES`, which help the node discover an initial list of active peers and the network topology.
- Creates a systemd service to run the node. The `dydxprotocold` binary is executed via Cosmovisor, which simplifies future upgrades.
Quoting Cosmovisor docs:
Cosmovisor is designed to be used as a wrapper for a Cosmos SDK app:
1. It will pass arguments to the associated app (configured by the `DAEMON_NAME` environment variable). Running `cosmovisor run arg1 arg2 ....` will run `app arg1 arg2 ...`.
2. It will manage an app by restarting and upgrading if needed.
- Downloaded and extracted the latest Testnet snapshot to speed up synchronization. In theory we could start our node from the genesis and have it process all the blocks up until now, however this process would be tedious and require us to plan all the version upgrades that happened along the way.
Launch the node with:
sudo systemctl start dydxprotocold
Exploring the Node
At this point, the node should be running. View the logs with:
sudo journalctl -u dydxprotocold -f
You should see information about subsequent blocks being processed as the node catches up with the blockchain's current state.
Run `sudo netstat -tpln`
to see which ports your node is listening on:
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:6060 0.0.0.0:* LISTEN 27351/dydxprotocold
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.54:53 0.0.0.0:* LISTEN -
tcp6 0 0 :::26656 :::* LISTEN 27351/dydxprotocold
tcp6 0 0 :::26657 :::* LISTEN 27351/dydxprotocold
tcp6 0 0 :::26660 :::* LISTEN 27351/dydxprotocold
tcp6 0 0 :::22 :::* LISTEN -
tcp6 0 0 :::1317 :::* LISTEN 27351/dydxprotocold
tcp6 0 0 :::9090 :::* LISTEN 27351/dydxprotocold
Apart from ports 22 for SSH and 53 for local DNS, here are the ports used by `dydxprotocold`:
- Exposed externally:
1317: REST server
26656: CometBFT P2P endpoint
26657: Tendermint RPC
26660: Prometheus endpoint
9090: Cosmos gRPC
- Localhost only:
9090: Cosmos gRPC
You can gather information about the node from the outside using these externally exposed services. Notable endpoints include:
http://YOUR_NODE_PUBLIC_IP:1317/cosmos/base/tendermint/v1beta1/node_info
http://YOUR_NODE_PUBLIC_IP:26657/status
http://YOUR_NODE_PUBLIC_IP:26660/metrics
From a new terminal (no SSH session), fetch and run the `watch-node.sh` convenience script
wget https://raw.githubusercontent.com/dydxopsdao/node-tools/refs/heads/main/watch-node.sh
chmod 755 watch-node.sh
./watch-node.sh YOUR_NODE_PUBLIC_IP
You should see live-updated information similar to:
Every 0.1s: probe 111.222.333.444 SomeMac.local: Mon Dec 2 13:47:11 2024
Node moniker: my-full-node-testnet-31528
Node ID: 1725754e35932db119c303129f4d5a24ab417378
Protocol version: 7.0.1
Block height: 26525504
Is catching up: true
Note the value of Is catching up . Right after ignition, your node is behind the current blockchain head and has to process past blocks to catch up. Depending on how recent your snapshot was, this make take from a few seconds up to a few hours. Once this is done you will start seeing:
Is catching up: false
From this moment on, the node can provide up-to-date information about the blockchain.
You can read more about Cosmos node endpoints in the official docs.
Why Cosmovisor?
Let’s SSH back into the node and inspect the systemd service that was created to run the protocol software. Type the following to view the service configuration:
cat /etc/systemd/system/dydxprotocold.service
You should see something like this:
[Unit]
Description=dydxprotocol node service
After=network-online.target
[Service]
User=ubuntu
ExecStart=/home/ubuntu/go/bin/cosmovisor run start --non-validating-full-node=true
WorkingDirectory=/home/ubuntu/.dydxprotocol
Restart=always
RestartSec=5
LimitNOFILE=4096
Environment="DAEMON_HOME=/home/ubuntu/.dydxprotocol"
Environment="DAEMON_NAME=dydxprotocold"
Environment="DAEMON_ALLOW_DOWNLOAD_BINARIES=false"
Environment="DAEMON_RESTART_AFTER_UPGRADE=true"
Environment="UNSAFE_SKIP_BACKUP=true"
[Install]
WantedBy=multi-user.target
This configuration allows you to control the node using systemctl (recall: sudo systemctl start dydxprotocold to start the node).
Note the ExecStart line. The service uses Cosmovisor, which runs dydxprotocold as its child process. Everything after cosmovisor run is passed as arguments to the node binary, while the configuration for Cosmovisor itself is set via the environment variables listed below. Essentially, the command under the hood is:
dydxprotocold run start --non-validating-full-node=true
Note: The non-validating-full-node flag specifies that we are running a full node rather than a validator. See the docs for more info.
Although you could configure systemd to run dydxprotocold directly (without Cosmovisor), using Cosmovisor is highly recommended. The reason: it enables seamless node version upgrades at a specific block height, without downtime.
For example, if you wanted to upgrade to version 1.2.3 of dydxprotocold exactly when the blockchain hits block 1000, you would just need to run:
cosmovisor add-upgrade 1.2.3 /path/to/newbinary-1.2.3 --upgrade-height 1000
This method ensures the upgrade happens with no downtime, in contrast to manually stopping the node, replacing the binary, and restarting the node at the exact right moment. For more details on Cosmovisor, check out the official docs.
Handle a protocol upgrade
Let’s write a bash function to download a new binary and schedule a Cosmovisor upgrade a chosen number of blocks ahead of the current block.
Note: Before proceeding, ensure that your node is no longer in the catching up state.
Paste the following to your console:
cat > ~/upgrade-node.sh << 'EOT'
#!/bin/bash
TARGET_VERSION=$1
BLOCKS_AHEAD=$2
TEMP_DIR=$(mktemp -d)
ARCH=$([ "$(uname -m)" = "x86_64" ] && echo "amd64" || echo "arm64")
BINARY_FILENAME="dydxprotocold-${TARGET_VERSION}-linux-${ARCH}"
BINARY_URL="<https://github.com/dydxprotocol/v4-chain/releases/download/protocol%2F${TARGET_VERSION}/${BINARY_FILENAME}.tar.gz>"
cd ${TEMP_DIR}
wget ${BINARY_URL}
tar -xzf ${BINARY_FILENAME}.tar.gz
BINARY_PATH="${TEMP_DIR}/build/${BINARY_FILENAME}"
CURRENT_HEIGHT=$(curl -s <http://127.0.0.1:26657/status> | jq -r '.result.sync_info.latest_block_height')
UPGRADE_HEIGHT=$((CURRENT_HEIGHT + BLOCKS_AHEAD))
echo "Current height: ${CURRENT_HEIGHT}"
echo "Scheduling Cosmovisor upgrade to ${TARGET_VERSION} at height ${UPGRADE_HEIGHT}"
cosmovisor add-upgrade "${TARGET_VERSION}" "${BINARY_PATH}" --upgrade-height "${UPGRADE_HEIGHT}" --force
rm -rf ${TEMP_DIR}
EOT
chmod 755 ~/upgrade-node.sh
Now you can schedule an upgrade to version v8.0.9 for 100 blocks from now:
~/upgrade-node.sh v8.0.9 100
Monitor your node as before, using the watch-node.sh script. When the specified block height arrives, you should see a brief error (1-2 seconds) as the node is being restarted, and shortly after, the Protocol version line should update from 8.0.8 to 8.0.9.
Or you can check directly from the node console with:
curl -s <http://localhost:1317/cosmos/base/tendermint/v1beta1/node_info> | jq '.application_version.version'
Conclusion
That concludes our introduction to running a dYdX full node. Happy tinkering!
Disclaimer
The content of this document (this “Document”) is provided for general informational and educational purposes only and does not constitute financial, legal, investment, or other professional advice. This Document represents the views and opinions of the author and does not necessarily reflect the views or opinions of the operator of this website or its affiliates (collectively, the “Ops subDAO”). All information is provided “as is” without representations or warranties of any kind as to its accuracy, completeness, or reliability, and may be subject to change. References to specific strategies, techniques, products, services, entities, or third-party projects are for informational purposes only and do not constitute an endorsement or recommendation by the Ops subDAO or any of its agents or representatives. Use of any referenced strategies, products, or services may involve material risks, including but not limited to financial loss, volatility or operational failures. Readers should conduct their own due diligence before making any decisions or taking any action.
Connect with Us
Dive deeper into the dYdX Operations subDAO community. Engage in discussions, stay updated with announcements, and be part of the dYdX Chain.