How Starship Automates Blockchain Upgrades with Cosmovisor

Starship automates Cosmos SDK blockchain upgrades by embedding Cosmovisor into validator containers, pre-building genesis and upgrade binaries, and enabling zero-downtime binary switches at scheduled block heights without manual pod restarts.

Starship, developed by hyperweb-io/starship, eliminates manual intervention during blockchain upgrades by integrating Cosmovisor directly into its Kubernetes-native infrastructure. This automation allows Cosmos SDK chains to transition between protocol versions seamlessly through governance proposals, handling binary compilation, distribution, and execution within the container orchestration layer.

Understanding the Starship Upgrade Architecture

Starship’s upgrade mechanism operates through three tightly coupled layers: configuration management, binary preparation, and runtime orchestration. Each layer maps to specific source files in the repository that handle distinct responsibilities in the upgrade lifecycle.

Configuration Layer: The Upgrade Struct

User-defined upgrade parameters are unmarshalled into the Upgrade struct defined in starship/cmd/starship/model.go. This struct captures the genesis version and future upgrade paths specified in Helm values.

The configuration requires upgrade.enabled: true alongside specific version tags:

upgrade:
  enabled: true
  genesis: v7.0.0
  upgrades:
    - name: v8.0.0
      version: v8.0.0
    - name: v9.0.0
      version: v9.0.0

When the generator processes this configuration, it triggers the CosmosValidatorStatefulSetGenerator to inject additional init containers into the validator StatefulSet manifest.

Build and Installation Process

The CosmosValidatorStatefulSetGenerator (located in packages/generator/src/builders/chains/cosmos/validator.ts) programmatically adds an init-build-images container when chain.upgrade.enabled evaluates to true. This container handles two critical tasks:

  1. Cosmovisor Installation: Executes go install github.com/cosmos/cosmos-sdk/cosmovisor/cmd/cosmovisor@v1.0.0 to place the binary in the container’s $GOBIN.

  2. Binary Compilation: Iterates through the upgrade list, invoking build-chain.sh with UPGRADE_NAME and CODE_TAG environment variables for each version.

The build-chain.sh script (found in charts/devnet/scripts/default/build-chain.sh) checks out the specified source code tags, compiles binaries using make install, and organizes them into Cosmovisor’s expected directory hierarchy:

if [[ $UPGRADE_NAME == "genesis" ]]; then
  mkdir -p $UPGRADE_DIR/genesis/bin
  cp $GOBIN/$CHAIN_BIN $UPGRADE_DIR/genesis/bin
else
  mkdir -p $UPGRADE_DIR/upgrades/$UPGRADE_NAME/bin
  cp $GOBIN/$CHAIN_BIN $UPGRADE_DIR/upgrades/$UPGRADE_NAME/bin
fi

Runtime Execution and Binary Switching

The validator pod defined in charts/devnet/templates/chains/cosmos/validator.yaml uses specialized init containers to prepare the runtime environment. The init-validator and init-config containers copy the genesis binary from $CHAIN_DIR/cosmovisor/genesis/bin/$CHAIN_BIN to /usr/bin, establishing Cosmovisor as the entry point.

When a governance proposal triggers an on-chain upgrade at a specific block height, Cosmovisor detects the matching directory under $UPGRADE_DIR/upgrades/<upgrade-name>/bin, swaps the active binary, and restarts the process automatically. This occurs without pod recreation or manual intervention, as demonstrated in the upgrade test suite.

Step-by-Step Implementation Guide

Implementing automated upgrades requires configuring the Helm values, ensuring proper binary placement, and submitting governance proposals.

1. Enable Upgrades in Helm Configuration

Define the upgrade path in your values.yaml by specifying the genesis version and all subsequent upgrades:

chains:
  - id: persistencecore
    name: persistencecore
    image: ghcr.io/cosmology-tech/persistencecore:latest
    upgrade:
      enabled: true
      genesis: v7.0.0
      upgrades:
        - name: v8.0.0
          version: v8.0.0
        - name: v9.0.0
          version: v9.0.0

2. Verify Init Container Generation

Starship generates the init-build-images container automatically. The generated manifest includes commands to build each binary version:

initContainers:
  - name: init-build-images
    image: ghcr.io/cosmology-tech/starship/builder:latest
    command:
      - bash
      - -c
      - |
        go install github.com/cosmos/cosmos-sdk/cosmovisor/cmd/cosmovisor@v1.0.0
        UPGRADE_NAME=genesis CODE_TAG=v7.0.0 bash -e /scripts/build-chain.sh
        UPGRADE_NAME=v8.0.0 CODE_TAG=v8.0.0 bash -e /scripts/build-chain.sh

3. Submit Governance Proposal

Use the chain’s governance module to schedule the upgrade. The test in examples/upgrade-test/upgrade_test.go demonstrates the complete flow:

plan := upgradetypes.Plan{
    Name:   "v8.0.0",
    Height: upgradeHeight,
}
msg := &gov.MsgSubmitProposal{}
msg.SetContent(upgradetypes.NewSoftwareUpgradeProposal(
    "Software upgrade",
    "software upgrade",
    plan,
))
res, err := chain.SendMsg(ctx, msg, "Software upgrade proposal")

4. Monitor Upgrade Execution

Wait for the chain to reach the specified upgrade height, then verify the upgrade applied successfully:

s.WaitForHeight(chain, upgradeHeight+1)
upgradeClient := upgradetypes.NewQueryClient(chain.Client)
planRes, _ := upgradeClient.AppliedPlan(ctx, &upgradetypes.QueryAppliedPlanRequest{Name: "v8.0.0"})

At the target height, Cosmovisor automatically switches to the new binary located in $UPGRADE_DIR/upgrades/v8.0.0/bin/.

Code Examples for Production Use

Validator Init Container Configuration

The validator template handles binary placement through conditional copy operations:

- name: init-validator
  image: {{ $chain.image }}
  command:
    - bash
    - -c
    - |
      if [[ -f $CHAIN_DIR/cosmovisor/genesis/bin/$CHAIN_BIN ]]; then
        cp $CHAIN_DIR/cosmovisor/genesis/bin/$CHAIN_BIN /usr/bin
      fi
      # Additional validator initialization...

Complete Upgrade Test Workflow

This Go test validates the end-to-end upgrade process, including proposal submission and state verification:

package upgradetest

import (
    upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
    gov "github.com/cosmos/cosmos-sdk/x/gov/types"
)

func TestSoftwareUpgrade(t *testing.T) {
    plan := upgradetypes.Plan{
        Name:   "v8.0.0",
        Height: 100,
    }
    
    msg := &gov.MsgSubmitProposal{}
    msg.SetContent(upgradetypes.NewSoftwareUpgradeProposal(
        "Software upgrade",
        "Upgrade to v8.0.0",
        plan,
    ))
    
    res, err := chain.SendMsg(ctx, msg)
    require.NoError(t, err)
    
    // Wait for upgrade height
    s.WaitForHeight(chain, 101)
    
    // Verify upgrade applied
    client := upgradetypes.NewQueryClient(chain.Client)
    applied, _ := client.AppliedPlan(ctx, &upgradetypes.QueryAppliedPlanRequest{Name: "v8.0.0"})
    require.Equal(t, int64(100), applied.Height)
}

Summary

  • Configuration-driven upgrades: The Upgrade struct in starship/cmd/starship/model.go parses Helm values to define genesis and future binary versions.
  • Automated binary preparation: The CosmosValidatorStatefulSetGenerator injects init-build-images containers that execute build-chain.sh to compile and organize binaries in Cosmovisor’s directory structure.
  • Zero-downtime execution: Cosmovisor manages binary switching at the scheduled block height without requiring pod restarts or manual intervention, as implemented in charts/devnet/templates/chains/cosmos/validator.yaml.
  • Governance integration: Upgrade proposals submitted through the SDK’s upgrade module trigger automatic binary swaps when the specified height is reached.

Frequently Asked Questions

What version of Cosmovisor does Starship install?

Starship installs Cosmovisor v1.0.0 by default, as specified in the init-build-images container command (go install github.com/cosmos/cosmos-sdk/cosmovisor/cmd/cosmovisor@v1.0.0). You can modify this version in the generator code at packages/generator/src/builders/chains/cosmos/validator.ts if your chain requires a specific Cosmovisor release.

How does Starship handle binary storage for multiple upgrade versions?

Starship organizes binaries using Cosmovisor’s standard directory layout. The build-chain.sh script places the genesis binary in $UPGRADE_DIR/genesis/bin/ and subsequent upgrade binaries in $UPGRADE_DIR/upgrades/<name>/bin/. This structure allows Cosmovisor to locate and switch binaries automatically when upgrade heights are reached.

Can I perform upgrades without restarting the validator pods?

Yes. Starship’s architecture eliminates the need for manual pod restarts. Once the init-build-images container completes the initial binary setup, Cosmovisor manages the runtime process. When an upgrade proposal executes at the specified block height, Cosmovisor swaps the binary and restarts the process internally within the same pod, maintaining chain continuity.

Where is the Cosmovisor binary located in the runtime container?

The Cosmovisor binary resides in the Starship runner image defined in docker/starship/runner/Dockerfile. This image serves as the base for validator containers, ensuring Cosmovisor is available at runtime to manage the chain binary lifecycle. The genesis binary is copied to /usr/bin during initialization to serve as Cosmovisor’s launch target.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →