Put a Cosmos Hub node under Cosmovisor v1.7.3 so it switches to the Gaia v29.0.0 binary at the governance upgrade height on its own. It is for node operators and validators who still swap gaiad binaries by hand at the halt height.
Prerequisites#
- A Linux server with systemd. The Cosmos Hub installation docs (opens in a new tab) use Ubuntu 22.04 LTS.
- Hardware sized for the Hub. The join mainnet guide (opens in a new tab) lists 16 GB RAM and 2 TB of storage for a full node, and 32 GB RAM with 500 GB to 2 TB for a validator. Leave extra free disk for the backup Cosmovisor takes before an upgrade, which step 2 explains.
- Go 1.26. Gaia v29.0.0's
go.modrequires Go 1.26.7 or later, and Cosmovisor v1.7.3 requires Go 1.23 or later. The commands below install 1.26.10, the current 1.26 release. git,make,gcc,curlandgnupg.- A synced
gaiadnode on chain IDcosmoshub-4, with its home at~/.gaia. Run a Cosmos Hub full node with state sync shows how to set one up.
Install the build tools and Go on an amd64 server. On arm64, use the linux-arm64 archive and its SHA256 from go.dev/dl (opens in a new tab).
1sudo apt update
2sudo apt install -y git make gcc build-essential curl gnupg
3curl -LO https://go.dev/dl/go1.26.10.linux-amd64.tar.gz
4echo "ccfe7ae822d2c2f41f4fe7c956199967a4dac22d0ee0d2b2bfdb440aab3b20c5 go1.26.10.linux-amd64.tar.gz" | sha256sum -c -
5sudo rm -rf /usr/local/go
6sudo tar -C /usr/local -xzf go1.26.10.linux-amd64.tar.gz
7cat >> ~/.profile << 'EOF'
8export PATH=$PATH:/usr/local/go/bin
9export PATH=$PATH:$(go env GOPATH)/bin
10EOF
11source ~/.profile
12go versionCosmovisor only manages binaries and never needs your mnemonic or key files. Keep validator mnemonics offline. Leave priv_validator_key.json in ~/.gaia/config and keep its backup offline too.
How Cosmovisor works#
Cosmovisor is a process manager that wraps your chain binary. cosmovisor run start launches gaiad start as a child process and passes its output through.
When an upgrade plan reaches its height, the x/upgrade module writes $DAEMON_HOME/data/upgrade-info.json and halts the chain. Cosmovisor polls that file every 300 ms by default. When a new plan appears, Cosmovisor stops gaiad and backs up the data folder if backups are on. It then points the current symlink at the new binary, runs the new binary's pre-upgrade command and starts the node again.
Cosmovisor keeps its files under $DAEMON_HOME/cosmovisor:
1$DAEMON_HOME/cosmovisor
2├── current -> genesis or upgrades/<name>
3├── genesis
4│ └── bin
5│ └── gaiad
6└── upgrades
7 └── <name>
8 ├── bin
9 │ └── gaiad
10 └── upgrade-info.json<name> is the upgrade plan name, lowercased. The cosmovisor binary itself can live anywhere on your PATH.
1. Install Cosmovisor#
go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.7.3
cosmovisor version --cosmovisor-onlyCosmovisor v1.7.3, released August 26, 2026, is the current release. v1.7.2 fixed builds with Go 1.26 and later, and v1.7.3 publishes prebuilt binaries with that fix. The Cosmovisor README (opens in a new tab) notes that only a binary installed with go install reports the correct version string.
The v1.7.3 release page (opens in a new tab) has prebuilt archives for linux/amd64, linux/arm64, darwin/amd64 and windows/amd64. If you use one, check its hash before you extract it:
curl -LO https://github.com/cosmos/cosmos-sdk/releases/download/cosmovisor%2Fv1.7.3/cosmovisor-v1.7.3-linux-amd64.tar.gz
curl -LO https://github.com/cosmos/cosmos-sdk/releases/download/cosmovisor%2Fv1.7.3/SHA256SUMS-cosmovisor-v1.7.3.txt
sha256sum -c --ignore-missing SHA256SUMS-cosmovisor-v1.7.3.txt2. Set the environment variables#
Cosmovisor reads its settings from environment variables. Add them to your shell profile so CLI commands see them:
1cat >> ~/.profile << 'EOF'
2export DAEMON_NAME=gaiad
3export DAEMON_HOME=$HOME/.gaia
4export DAEMON_ALLOW_DOWNLOAD_BINARIES=false
5export DAEMON_RESTART_AFTER_UPGRADE=true
6export UNSAFE_SKIP_BACKUP=false
7EOF
8source ~/.profileHere is what each variable controls:
DAEMON_NAMEis the file name Cosmovisor looks for in eachbinfolder. For the Hub it isgaiad.DAEMON_HOMEis the node home that holdsconfig,dataandcosmovisor. It must be an absolute path and match the--homeyou start the node with.DAEMON_ALLOW_DOWNLOAD_BINARIESlets Cosmovisor fetch the new binary from URLs in the upgrade plan. The default isfalse. The Cosmovisor docs say this option is meant for full nodes, not validators. A failed download stops the node. Keep it off and stage binaries yourself. If you turn it on for a full node, also setDAEMON_DOWNLOAD_MUST_HAVE_CHECKSUM=true.DAEMON_RESTART_AFTER_UPGRADErestarts the node with the new binary after a successful switch. The default istrue. Withfalse, Cosmovisor exits after the switch and you start it again yourself.UNSAFE_SKIP_BACKUPdefaults tofalse. Withfalse, Cosmovisor copies$DAEMON_HOME/datatodata-backup-<date>before the switch, and the docs recommend keeping backups on. The copy needs as much free space as your data folder takes up, and the node stays down while it runs. To write the copy to another disk, setDAEMON_DATA_BACKUP_DIRto an existing absolute path. Gaia's sample unit file setsUNSAFE_SKIP_BACKUP=true. Only do that if you have a recent snapshot to restore from.
Compare the size of your data folder with the free space on its disk:
du -sh $DAEMON_HOME/data
df -h $DAEMON_HOME3. Initialize the folder layout#
Copy in the binary you run today#
The genesis folder holds the binary that matches your node's current height. For a synced node, that is your installed gaiad. At the time of writing, cosmoshub-4 nodes report v28.3.1. Confirm the current version on the Gaia releases page (opens in a new tab) before you continue.
gaiad version
cosmovisor init $(which gaiad)
cosmovisor configcosmovisor init creates cosmovisor/genesis/bin and copies the binary there. It also creates the current link and writes cosmovisor/config.toml. It is a different command from the chain's own gaiad init. cosmovisor config prints the values Cosmovisor sees. Run it after init, because it fails when the cosmovisor folder does not exist yet.
Carry over the last upgrade record#
An existing node usually has data/upgrade-info.json left from the previous upgrade. If current/upgrade-info.json is missing, Cosmovisor treats that old file as a new request and tries to switch on its first start. Copy it across:
if [ -f "$DAEMON_HOME/data/upgrade-info.json" ]; then
cp "$DAEMON_HOME/data/upgrade-info.json" "$DAEMON_HOME/cosmovisor/current/upgrade-info.json"
fi4. Stage the upgrade binary#
Read the upgrade plan#
gaiad query upgrade planThe plan shows the upgrade name and height. Your upgrade folder must use that name. The plan stays empty until a software upgrade proposal passes.
At the time of writing, no plan is active on cosmoshub-4. Proposal 1059 is in its voting period until October 27, 2026. It proposes the name v29.0.0 at height 33618500, which matches the upgrade name in the Gaia v29.0.0 code. Check its current status:
gaiad query gov proposal 1059If the proposal fails or a new one replaces it, use the name and height from the plan that passes. The commands below use v29.0.0.
Download and verify the release binary#
Gaia v29.0.0 came out on October 19, 2026.
cd ~
curl -LO https://github.com/cosmos/gaia/releases/download/v29.0.0/gaiad-v29.0.0-linux-amd64
curl -LO https://github.com/cosmos/gaia/releases/download/v29.0.0/gaiad-v29.0.0-linux-amd64.asc
curl -LO https://github.com/cosmos/gaia/releases/download/v29.0.0/SHA256SUMS-v29.0.0.txt
grep linux-amd64 SHA256SUMS-v29.0.0.txt | awk '{print $1" gaiad-v29.0.0-linux-amd64"}' | sha256sum -c -The checksum file lists the binary as ./gaiad-linux-amd64/gaiad, so the awk step swaps in the downloaded file name before sha256sum checks it. The output must end in OK.
Then check the signature, following the Gaia signing instructions (opens in a new tab):
curl --fail -LO https://raw.githubusercontent.com/cosmos/security/main/release/gaia/signing/keys/cosmos-release.asc
gpg --show-keys --with-fingerprint cosmos-release.asc
gpg --import cosmos-release.asc
gpg --verify gaiad-v29.0.0-linux-amd64.asc gaiad-v29.0.0-linux-amd64The fingerprint must be E625 9F16 9C7B 913D BB75 DE02 5395 305D 29B8 8B32, and gpg --verify must report Good signature. An [unknown] trust warning by itself is not a problem.
Gaia v29.0.0 ships binaries for linux/amd64 and darwin/amd64 only. On an arm64 server, build from source instead:
cd ~
git clone https://github.com/cosmos/gaia.git
cd gaia
git checkout v29.0.0
CGO_ENABLED=1 make buildThe build writes the binary to ~/gaia/build/gaiad. Unlike make install, make build leaves your installed gaiad untouched. Use that path in place of the downloaded file below.
Register it with add-upgrade#
cd ~
chmod +x gaiad-v29.0.0-linux-amd64
./gaiad-v29.0.0-linux-amd64 version
cosmovisor add-upgrade v29.0.0 ./gaiad-v29.0.0-linux-amd64The version command should print v29.0.0. add-upgrade copies the file to upgrades/v29.0.0/bin/gaiad. Do not pass --upgrade-height for a governance upgrade. That flag writes its own upgrade-info.json and exists for coordinated upgrades without a proposal.
5. Run Cosmovisor under systemd#
Only one process can use the node home at a time. If gaiad already runs as a service, stop and disable it. Replace <old-service> with its unit name:
sudo systemctl stop <old-service>
sudo systemctl disable <old-service>Create /etc/systemd/system/cosmovisor.service. Replace <your-user> with the Linux user that owns ~/.gaia, and confirm the binary path with which cosmovisor. Use absolute paths, because Environment= lines do not expand shell variables such as $HOME.
1[Unit]
2Description=Cosmovisor for gaiad
3After=network-online.target
4Wants=network-online.target
5
6[Service]
7User=<your-user>
8ExecStart=/home/<your-user>/go/bin/cosmovisor run start --home /home/<your-user>/.gaia
9Restart=always
10RestartSec=3
11LimitNOFILE=50000
12Environment="DAEMON_NAME=gaiad"
13Environment="DAEMON_HOME=/home/<your-user>/.gaia"
14Environment="DAEMON_ALLOW_DOWNLOAD_BINARIES=false"
15Environment="DAEMON_RESTART_AFTER_UPGRADE=true"
16Environment="UNSAFE_SKIP_BACKUP=false"
17Environment="COSMOVISOR_COLOR_LOGS=false"
18
19[Install]
20WantedBy=multi-user.targetCosmovisor handles the restart after an upgrade. With Restart=always, systemd restarts the service after a crash. COSMOVISOR_COLOR_LOGS=false keeps color codes out of the journal. Load and start the service:
sudo systemctl daemon-reload
sudo systemctl enable cosmovisor
sudo systemctl start cosmovisorVerify it works#
systemctl status cosmovisor
sudo journalctl -u cosmovisor -f -o catLook for a running app line whose path ends in cosmovisor/genesis/bin/gaiad. Then check the link and the sync state:
readlink -f $DAEMON_HOME/cosmovisor/current
gaiad statusgaiad status should show latest_block_height rising.
Watch an upgrade happen#
Before the plan height, confirm the plan and the staged folder, then follow the upgrade lines in the log:
gaiad query upgrade plan
ls $DAEMON_HOME/cosmovisor/upgrades/
sudo journalctl -u cosmovisor -f -o cat | grep -iE "upgrade|backup|running app"At the plan height you should see these lines, in order:
UPGRADE "v29.0.0" NEEDED at height: <height>fromgaiadas the chain halts.daemon shutting down in an attempt to restartfrom Cosmovisor.starting to take backup of data directory, thenbackup completed, if backups are on.pre-upgrade command does not exist. continuing the upgrade.Gaia v29.0.0 has nopre-upgradecommand, so this line is expected.upgrade detected, relaunching, thenrunning appwith a path underupgrades/v29.0.0/bin.
Blocks resume once validators with more than two thirds of the voting power run the new binary. Then confirm the switch:
readlink -f $DAEMON_HOME/cosmovisor/current
$DAEMON_HOME/cosmovisor/current/bin/gaiad version
$DAEMON_HOME/cosmovisor/current/bin/gaiad query upgrade applied v29.0.0The gaiad in your Go bin folder stays at the old version. Use the current/bin/gaiad path for CLI work after the upgrade.
Troubleshooting#
Cosmovisor logs binary not present, downloading disabled#
No binary exists at upgrades/<name>/bin/gaiad. The usual cause is a folder name that differs from the plan name. Cosmovisor takes the data backup before it looks for the binary, so this mistake also costs you the backup time. Compare the folder list with the name in the halt file:
ls $DAEMON_HOME/cosmovisor/upgrades/
cat $DAEMON_HOME/data/upgrade-info.jsonStop the service. Stage the binary with cosmovisor add-upgrade <name> <path-to-binary>, where <name> is the name field from the halt file. Then start the service.
The node logs upgrade handler is missing before the upgrade height#
The binary in cosmovisor/genesis/bin is already v29.0.0. That binary only carries the v29.0.0 handler, so it stops on the first block. This happens when make install replaced gaiad before you ran cosmovisor init. Stop the service. Put a verified v28.3.1 binary from the Gaia releases page (opens in a new tab) back in genesis/bin/gaiad.
Cosmovisor asks for an upgrade that already happened#
The node has an old data/upgrade-info.json and current/upgrade-info.json is missing. Stop the service, copy the file as shown in step 3 and start it again.
go install fails with invalid reference to encoding/json.unquoteBytes#
Cosmovisor v1.7.1 and older do not build with Go 1.26 and later. The Cosmos SDK Cosmovisor docs page (opens in a new tab) still shows v1.7.1. Install v1.7.3 with the command in step 1.
Next steps#
- Rehearse the full flow on a local chain with the SimApp example in the Cosmovisor README (opens in a new tab).
- Read each release's
UPGRADING.md, such as the one for Gaia v29.0.0 (opens in a new tab), before you stage a binary. - Look at
cosmovisor prepare-upgrade. It reads the active plan over gRPC from the address inDAEMON_GRPC_ADDRESS, which defaults tolocalhost:9090. When the plan lists download URLs, it fetches the binary. The node needs gRPC enabled. Test it on a full node before you rely on it. - Add an alert on block height, so you hear about a stalled upgrade before missed blocks pile up.
- If you run a validator, read Validator security: sentry nodes, remote signing and firewalls next.