Prevent Risky YugabyteDB and YBA Upgrades by Comparing Release Dates

When planning an upgrade for YugabyteDB or YugabyteDB Anywhere (YBA), it is easy to assume that a higher version number automatically means you are moving to newer software.

That is not always true.

Because YugabyteDB release tracks are maintained concurrently, a maintenance release on an older track can be published after an earlier release on a newer track. This means a target version can be numerically higher while actually containing software that was released earlier.

This tip includes a Bash script, check_yb_upgrade.sh, that checks both version ordering and release chronology before an upgrade. The script supports YugabyteDB and YBA, resolves partial target versions such as 2026.1, detects numeric downgrades, and warns when a target release was published before the version currently running.

For example:

Version Release Date Role
2024.2.9.0 April 29, 2026 Current version
2025.2.2.2 April 7, 2026 Target version

Even though 2025.2.2.2 is numerically higher than 2024.2.9.0, the target release was published earlier.

Key Point: Version ordering and release chronology are two different checks. A target can be numerically higher while still pointing to software that was released earlier.

Why Can a Chronologically Older Target Be a Problem?

Moving to a target release that was finalized before the currently running release can introduce several problems.

YugabyteDB Risks

  • ● Missing YSQL catalog migrations: The current release may contain YSQL system catalog updates or migrations that do not exist in the older target release, potentially resulting in pg_upgrade failures.
  • ● Missing backported fixes: Maintenance releases can receive important bug fixes or security fixes that were not present when the target release was produced.
  • ● Incompatible system state: Cluster state created by newer code may depend on behavior or changes that do not exist in an earlier binary.

YugabyteDB Anywhere Risks

  • ● Internal database migration issues: YBA maintains internal database state for cluster metadata, provider configurations, users, and other platform information.
  • ● Task-engine incompatibilities: YBA manages stateful operations such as rolling upgrades, volume expansion, and backups. Older YBA logic may not understand state created by a later release.
  • ● Managed-version compatibility: An older YBA release may not contain orchestration logic required for newer YugabyteDB versions.

YugabyteDB and YBA Need Separate Checks

The script accepts a component option so it knows which product is being checked:

  • -c db for YugabyteDB
  • -c yba for YugabyteDB Anywhere
Option Component Lookup Method
-c db YugabyteDB GitHub release metadata
-c yba YugabyteDB Anywhere YBA release documentation and installer metadata

For YugabyteDB, the script retrieves the GitHub published_at timestamp.

For YBA, it examines the YBA release documentation, resolves the installer package, and retrieves its HTTP Last-Modified timestamp. If that timestamp is unavailable, the script falls back to the release date published in the documentation.

Why the Component Option Matters: YugabyteDB and YBA are separate software components. The script should check the release information for the component you are actually upgrading rather than assuming that the same version number represents the same release timestamp.

Partial Version Matching

The script can also accept a partial target version.

For example:

				
					./check_yb_upgrade.sh -c yba 2024.2.9.0 2026.1
				
			

Instead of requiring you to know the complete patch version, the script searches the available releases and resolves 2026.1 to the matching full release.

The script can also handle a year-only YBA input such as:

				
					2026
				
			

This makes the utility useful when the goal is to select the latest release matching a particular release series.

What the Script Checks

Check Example Result
Resolve partial target 2026.1 Find matching full release
Detect numeric downgrade 2024.2.9.0 → 2024.2.7.1 Warn and stop
Compare release chronology 2024.2.9.0 → 2025.2.2.2 Warn if target is older

Complete Bash Script

Save the following as check_yb_upgrade.sh:

				
					#!/bin/bash

# check_yb_upgrade.sh
# Validates version sequence and release timestamps for YugabyteDB (DB) and YugabyteDB Anywhere (YBA)
# Usage: ./check_yb_upgrade.sh [-c db|yba] <current_version> <target_version>

COMPONENT="db"

while getopts "c:" opt; do
  case $opt in
    c)
      COMPONENT=$(echo "$OPTARG" | tr '[:upper:]' '[:lower:]')
      ;;
    *)
      echo "Usage: $0 [-c db|yba] <current_version> <target_version>"
      exit 1
      ;;
  esac
done

shift $((OPTIND -1))

CURRENT_INPUT=$1
TARGET_INPUT=$2

if [ -z "$CURRENT_INPUT" ] || [ -z "$TARGET_INPUT" ]; then
  echo "Usage: $0 [-c db|yba] <current_version> <target_version>"
  echo "Examples:"
  echo "  $0 -c db 2024.2.9.0 2025.2.2.2"
  echo "  $0 -c yba 2024.2.9.0 2026.1"
  exit 1
fi

if [[ "$COMPONENT" != "db" && "$COMPONENT" != "yba" ]]; then
  echo "Error: Invalid component '$COMPONENT'. Must be 'db' or 'yba'." >&2
  exit 1
fi

# Query GitHub API for YugabyteDB Engine releases
get_db_release_date() {
  local input_ver="${1#v}"
  local response tag published_at resolved_ver

  for try_ver in "$input_ver" "${input_ver}.0" "${input_ver}.0.0"; do
    tag="v${try_ver}"
    response=$(curl -s "https://api.github.com/repos/yugabyte/yugabyte-db/releases/tags/${tag}")
    published_at=$(echo "$response" | grep '"published_at"' | head -n 1 | awk -F'"' '{print $4}')

    if [ -n "$published_at" ]; then
      echo "${try_ver}|${published_at}"
      return 0
    fi
  done

  # Fallback: Prefix matching against GitHub release list
  response=$(curl -s "https://api.github.com/repos/yugabyte/yugabyte-db/releases?per_page=100")
  tag=$(echo "$response" | grep '"tag_name"' | awk -F'"' '{print $4}' | grep -E "^v${input_ver}(\.|\-|$)" | head -n 1)

  if [ -n "$tag" ]; then
    published_at=$(echo "$response" | grep -A 10 "\"tag_name\": \"${tag}\"" | grep '"published_at"' | head -n 1 | awk -F'"' '{print $4}')
    resolved_ver="${tag#v}"

    if [ -n "$published_at" ]; then
      echo "${resolved_ver}|${published_at}"
      return 0
    fi
  fi

  return 1
}

# Scrape YBA docs and download headers for release timestamps
get_yba_release_date() {
  local input_ver="${1#v}"
  local series=$(echo "$input_ver" | awk -F'.' '{print $1"."$2}')

  # Handle partial input year (e.g. "2026")
  if [[ "$input_ver" =~ ^[0-9]{4}$ ]]; then
    local main_docs=$(curl -sL "https://docs.yugabyte.com/stable/releases/yba-releases/")
    series=$(echo "$main_docs" | grep -oE "v${input_ver}\.[0-9]+" | head -n 1 | tr -d 'v')
  fi

  local docs_url="https://docs.yugabyte.com/stable/releases/yba-releases/v${series}/"
  local docs_html=$(curl -sL "$docs_url")

  # Resolve full version if user entered partial version (e.g. 2026.1 -> 2026.1.0.1)
  local resolved_ver="$input_ver"

  if [[ "$input_ver" != *.*.*.* ]]; then
    resolved_ver=$(echo "$docs_html" | grep -oE "20[0-9]{2}\.[0-9]+\.[0-9]+\.[0-9]+" | grep -E "^${input_ver}(\.|$)" | head -n 1)
  fi

  if [ -z "$resolved_ver" ]; then
    return 1
  fi

  # Extract exact installer URL and fetch HTTP Last-Modified header
  local installer_url=$(echo "$docs_html" | grep -oE "https://downloads\.yugabyte\.com/releases/${resolved_ver}/yba_installer_full-${resolved_ver}-b[0-9]+-linux-x86_64\.tar\.gz" | head -n 1)
  local iso_date=""

  if [ -n "$installer_url" ]; then
    local last_modified=$(curl -sI "$installer_url" | grep -i "last-modified" | cut -d':' -f2- | xargs)

    if [ -n "$last_modified" ]; then
      iso_date=$(date -u -d "$last_modified" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -j -f "%a, %d %b %Y %T GMT" "$last_modified" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null)
    fi
  fi

  # Fallback: Parse date from documentation text
  if [ -z "$iso_date" ]; then
    local date_str=$(echo "$docs_html" | grep -iE "v${resolved_ver}[[:space:]]*-[[:space:]]*[A-Za-z]+[[:space:]]+[0-9]+,[[:space:]]+[0-9]{4}" | head -n 1 | sed -E 's/.*-[[:space:]]*([A-Za-z]+[[:space:]]+[0-9]+,[[:space:]]+[0-9]{4}).*/\1/')

    if [ -n "$date_str" ]; then
      iso_date=$(date -u -d "$date_str" +"%Y-%m-%dT00:00:00Z" 2>/dev/null)
    fi
  fi

  if [ -n "$iso_date" ]; then
    echo "${resolved_ver}|${iso_date}"
    return 0
  fi

  return 1
}

echo "Fetching release metadata for component [$COMPONENT]..."

if [ "$COMPONENT" = "db" ]; then
  CURRENT_INFO=$(get_db_release_date "$CURRENT_INPUT")
  TARGET_INFO=$(get_db_release_date "$TARGET_INPUT")
else
  CURRENT_INFO=$(get_yba_release_date "$CURRENT_INPUT")
  TARGET_INFO=$(get_yba_release_date "$TARGET_INPUT")
fi

if [ -z "$CURRENT_INFO" ]; then
  echo "Error: Could not resolve $COMPONENT version or release date for '$CURRENT_INPUT'." >&2
  exit 1
fi

if [ -z "$TARGET_INFO" ]; then
  echo "Error: Could not resolve $COMPONENT version or release date for '$TARGET_INPUT'." >&2
  exit 1
fi

CURRENT_VER=$(echo "$CURRENT_INFO" | cut -d'|' -f1)
CURRENT_DATE=$(echo "$CURRENT_INFO" | cut -d'|' -f2)

TARGET_VER=$(echo "$TARGET_INFO" | cut -d'|' -f1)
TARGET_DATE=$(echo "$TARGET_INFO" | cut -d'|' -f2)

READABLE_CURRENT=$(echo "$CURRENT_DATE" | tr 'TZ' ' ' | xargs)
READABLE_TARGET=$(echo "$TARGET_DATE" | tr 'TZ' ' ' | xargs)

echo "--------------------------------------------------------"
echo "Component:       ${COMPONENT^^}"
echo "Current Version: $CURRENT_VER (Released: $READABLE_CURRENT UTC)"
echo "Target Version:  $TARGET_VER (Released: $READABLE_TARGET UTC)"
echo "--------------------------------------------------------"

# Version sequence check using sort -V
LOWEST_VER=$(printf '%s\n%s\n' "$CURRENT_VER" "$TARGET_VER" | sort -V | head -n 1)

if [ "$CURRENT_VER" = "$TARGET_VER" ]; then
  echo "ℹ️  NOTE: Target version matches current version ($CURRENT_VER)."
  exit 0
elif [ "$LOWEST_VER" = "$TARGET_VER" ]; then
  echo "⚠️  WARNING: Target version ($TARGET_VER) is a NUMERIC DOWNGRADE from current version ($CURRENT_VER)."
  echo "    Downgrading is not supported and can lead to system corruption or failures."
  exit 1
fi

# Compare ISO 8601 release timestamps
if [[ "$TARGET_DATE" < "$CURRENT_DATE" ]]; then
  echo "⚠️  WARNING: Target $COMPONENT version ($TARGET_VER) was released BEFORE your current version ($CURRENT_VER)!"
  echo "    This upgrade is chronologically backward. It can lead to migration failures,"
  echo "    missing bug fixes, or state incompatibilities."
  exit 1
else
  echo "✅  SUCCESS: Target $COMPONENT version ($TARGET_VER) is chronologically newer. Upgrade check passed."
  exit 0
fi
				
			

Make the script executable:

				
					chmod +x check_yb_upgrade.sh
				
			

Example 1: Validate a Partial YBA Target

Suppose the current YBA version is 2024.2.9.0 and you want to move to the latest matching 2026.1 release:

				
					./check_yb_upgrade.sh -c yba 2024.2.9.0 2026.1
				
			

The script resolves the partial target to the matching full release and performs the comparison:

This example demonstrates both partial-version resolution and chronological validation.

Example 2: Detect a Chronologically Backward YugabyteDB Upgrade

Now consider:

				
					./check_yb_upgrade.sh -c db 2024.2.9.0 2025.2.2.2
				
			

Output:

The version number moved forward, but the release date moved backward.

Example 3: Detect a Numeric Downgrade

A true downgrade is a different situation:

				
					./check_yb_upgrade.sh -c yba 2024.2.9.0 2024.2.7.1
				
			

Output:

Because the script checks numeric ordering first, it identifies this as a downgrade instead of simply reporting that the target release is older.

Final Takeaway

A YugabyteDB or YBA upgrade should not be evaluated by version number alone.

Before upgrading, determine:

  • ● Which component is being upgraded: YugabyteDB or YBA?
  • ● Does the requested target release exist?
  • ● Does a partial target need to be resolved to a full release?
  • ● Is the target numerically higher?
  • ● Was the target actually released after the currently running version?
✅ A Successful Check Is Only a Precheck: A successful result means the target version passed the version-ordering and release-chronology checks performed by this script. It does not validate every YugabyteDB or YBA upgrade prerequisite. Always review the supported upgrade path and component-specific requirements before by this script. It does not validate every YugabyteDB or YBA upgrade prerequisite. Always review the supported upgrade path and component-specific requirements before proceeding.

The check_yb_upgrade.sh script automates these checks and can expose an easy-to-miss situation: a version number that looks newer but represents software that was actually released earlier.

Have Fun!

A work colleague shared this photo of the 99.9% eclipse in Madrid. Pretty amazing! 🌘✨