Patching ExaCS clusters using OCI-CLI

Keeping your ExaCS clusters up to date is important. In this article I will provide some basic steps on what to do. I made an interactive script so I can do the whole patch cycle in my terminal without clicking through the console. It lets me choose the environment, which update I would like to precheck/apply, and provides me the status of the patch.

This can be helpful if not every employee can access the OCI console but needs to patch the system. For example your DBA or Service Desk Engineer can do it without any special tools — just run the script!

Prerequisites

Make sure you have a Linux machine installed with oci-cli and jq. In this case I have an ExaCS infrastructure with 4 different clusters — one for each DTAP (Development, Test, Acceptance, Production) environment. The goal is to keep all environments the same.

Declaring variables

Start by declaring all variables you need: the OCIDs for all ExaCS VM clusters and today’s date in UTC.

# Make sure to enter your OCIDs
DV_OCID="ocid1.cloudvmcluster.oc1.eu-amsterdam-1.xxxxxx"
TS_OCID="ocid1.cloudvmcluster.oc1.eu-amsterdam-1.xxxxxx"
AC_OCID="ocid1.cloudvmcluster.oc1.eu-amsterdam-1.xxxxxx"
PR_OCID="ocid1.cloudvmcluster.oc1.eu-amsterdam-1.xxxxxx"

# Current date in UTC
TODAY=$(date -u +%F)  # e.g. "2025-09-04"

Fetching available patches

Fetch all patches available for a cluster and format the JSON output as a readable table:

PATCH_LIST=$(oci db cloud-vm-cluster list-updates \
  --cloud-vm-cluster-id "$DV_OCID" \
  --all \
  --query "data[*].{id:id,description:description,version:version,release:\"time-released\",action:\"available-actions\"}" \
  --output table)

echo "$PATCH_LIST"

The result is a table with patch OCID, description, version, release time and available action (ROLLING-APPLY/PRECHECK). Save the OCID of the patch you want to install.

Running the precheck

oci db cloud-vm-cluster update \
  --cloud-vm-cluster-id "$DV_OCID" \
  --update-id "[SAVED PATCH OCID]" \
  --update-action PRECHECK \
  --force

Monitor the precheck status with a polling loop. The script checks every 60 seconds for up to 60 minutes and stops when the lifecycle state reaches SUCCEEDED or FAILED:

precheck_status() {
  local max_attempts="${2:-60}"
  local interval=60
  local attempt=1

  echo "Waiting for patch state to reach status SUCCEEDED..."

  while (( attempt <= max_attempts )); do
    vm_patch_status=$(oci db cloud-vm-cluster list-update-histories \
      --cloud-vm-cluster-id "$DV_OCID" \
      --output json | jq -r \
      --arg today "$TODAY" \
      '.data[] | select(."update-id" == "[SAVED_PATCH_ID]") | select(."update-action" == "PRECHECK") | select(."time-started" | startswith($today)) | ."lifecycle-state"')

    echo "Status is '$vm_patch_status'"

    if [[ "$vm_patch_status" == "SUCCEEDED" ]]; then
      echo "Precheck is successful!"
      run_vm_apply
    elif [[ "$vm_patch_status" == "FAILED" ]]; then
      echo "Patch failed the precheck. Please check the console for details."
      exit 0
    fi

    ((attempt++))
    sleep "$interval"
  done

  echo "Timeout reached after $((max_attempts * interval / 60)) minutes."
  exit 0
}

Applying the patch

Assuming the precheck succeeds, apply the patch by swapping PRECHECK for APPLY:

oci db cloud-vm-cluster update \
  --cloud-vm-cluster-id "$CLUSTER_OCID" \
  --update-id "[SAVED PATCH OCID]" \
  --update-action APPLY \
  --force

Then run the same polling loop, filtering on APPLY instead of PRECHECK:

apply_status() {
  local max_attempts="${2:-60}"
  local interval=60
  local attempt=1

  echo "Waiting for patch state to reach status SUCCEEDED..."

  while (( attempt <= max_attempts )); do
    vm_apply_status=$(oci db cloud-vm-cluster list-update-histories \
      --cloud-vm-cluster-id "$DV_OCID" \
      --output json | jq -r \
      --arg today "$TODAY" \
      '.data[] | select(."update-id" == "[SAVED_PATCH_ID]") | select(."update-action" == "APPLY") | select(."time-started" | startswith($today)) | ."lifecycle-state"')

    echo "Status is '$vm_apply_status'"

    if [[ "$vm_apply_status" == "SUCCEEDED" ]]; then
      echo "Patch has successfully been applied!"
      exit 0
    elif [[ "$vm_apply_status" == "FAILED" ]]; then
      echo "Patch failed to apply. Please check the console for details."
      exit 0
    fi

    ((attempt++))
    sleep "$interval"
  done

  echo "Timeout reached after $((max_attempts * interval / 60)) minutes."
  exit 0
}

When the patch is installed successfully it will confirm this. With some bash scripting skills you can combine everything into one single command and patch all your DTAP environments in one go. If you would like details on the full script, don’t hesitate to reach out!