IOS Upgrade via TFTP — Copy Image, Verify MD5 & Update Boot Statement

Every production Cisco router and switch will require an IOS upgrade at some point — to patch a critical security vulnerability, enable a new feature, fix a known defect, or simply maintain support from Cisco TAC. The TFTP method is the most widely used approach for in-band upgrades: you place the new image on a TFTP server reachable by the device, transfer it to flash, verify its integrity with an MD5 checksum, update the boot statement, and reload. The entire procedure is performed from the CLI without physical access to the device — making it suitable for remote upgrades during maintenance windows.

The procedure requires careful attention to three potential failure points: flash space (the new image must fit in available flash — often requiring the old image to be deleted first), image integrity (the MD5 hash from Cisco's download page must match the hash computed on the device after transfer — any mismatch means a corrupt or tampered image that must never be booted), and boot statement order (IOS loads the first image it finds — if the boot statement is wrong or missing, the device may reboot into the old image or get stuck in ROMMON). Getting all three right is what separates a clean upgrade from an emergency recovery call at 3 AM. For saving configurations before and after the upgrade see Saving & Managing Cisco Configurations.

This lab covers the full upgrade workflow for both Cisco ISR routers (IOS / IOS-XE) and Catalyst switches, flash space management, TFTP server setup, the complete verification sequence, and a ROMMON recovery procedure for the case where a bad upgrade leaves the device unbootable. For recovering from a device that will not boot at all — including using ROMMON's tftpdnld command — see ROMMON & Password Recovery. For automating upgrades across a fleet of devices see Ansible Playbook — Automate IOS Configuration.

1. Core Concepts — Flash, Boot Sequence & Image Types

How Cisco IOS Locates and Loads an Image

  IOS IMAGE SELECTION ORDER (evaluated at every boot):

  Step 1 — Read config-register boot field (bits 0–3):
    0x0 → Stay in ROMMON
    0x1 → Load ROM image (mini-IOS / boot image)
    0x2–0xF → Proceed to boot system statements

  Step 2 — Check for 'boot system' statements in startup-config:
    boot system flash:isr4300-universalk9.16.12.04.SPA.bin  ← try first
    boot system flash:isr4300-universalk9.16.09.06.SPA.bin  ← try second (fallback)
    boot system tftp:isr4300-universalk9.16.12.04.SPA.bin 192.168.1.100  ← tftp fallback
    boot system rom                                          ← ROM fallback

    IOS tries each statement in ORDER — first one that succeeds wins.
    If a file is not found, it tries the next statement.
    If NO statements exist → loads first .bin file found in flash (alphabetical).

  Step 3 — If no boot system statements AND no .bin file in flash:
    → Boots into ROMMON (rommon 1 >)

  ┌──────────────────────────────────────────────────────────────────┐
  │  WHY EXPLICIT BOOT STATEMENTS MATTER                            │
  │                                                                  │
  │  Without a boot system statement, IOS loads the FIRST .bin file  │
  │  alphabetically in flash. After an upgrade where the OLD image   │
  │  was not deleted, you may have:                                  │
  │    flash:isr4300-universalk9.16.09.06.SPA.bin  (old)             │
  │    flash:isr4300-universalk9.16.12.04.SPA.bin  (new)             │
  │                                                                  │
  │  Alphabetically, 16.09 comes before 16.12 — the device boots    │
  │  the OLD image after every reload until you add an explicit      │
  │  boot system statement pointing to the new one.                  │
  │                                                                  │
  │  Always set an explicit boot system statement after any upgrade. │
  └──────────────────────────────────────────────────────────────────┘
  

IOS Image Filename Anatomy

  Example: isr4300-universalk9.16.12.04.SPA.bin

  isr4300      → Platform identifier (ISR 4300 series)
  universal    → Feature set: universal = all features, unlocked by licence
  k9           → Encryption capable (supports IPsec, SSH, etc.)
  16.12.04     → IOS-XE version: major.minor.maintenance (16.12.4)
  SPA          → Software type: SPA = Specific Platform Aggregated
  .bin         → Binary image file

  Classic IOS example: c2960-lanbasek9-mz.152-7.E9.bin
  c2960        → Platform (Catalyst 2960)
  lanbasek9    → Feature set: LAN Base with encryption
  mz           → m = runs from RAM (decompressed), z = compressed in flash
  152-7.E9     → Version 15.2(7)E9
  .bin         → Binary image

  IOS-XE Packaging (16.x and later):
  Newer IOS-XE uses a PACKAGE-based install instead of a single .bin:
    show version → shows "packages.conf" as boot file
    install add file flash:image.bin → adds to package repository
    install activate → activates without full reload
    install commit → makes permanent
  This lab covers the traditional .bin file method (install mode
  is covered separately in advanced upgrade procedures).
  

Flash Memory — Types and Locations

Platform Flash Location IOS Command Reference Typical Capacity
ISR 1900 / 2900 / 3900 Onboard CompactFlash flash: or flash0: 256 MB – 1 GB
ISR 4000 series Onboard flash + optional USB bootflash: (primary), usb0: 8 GB – 16 GB (bootflash)
Catalyst 2960 / 3560 Onboard flash flash: 64 MB – 128 MB
Catalyst 3750 / 3850 Onboard flash flash: 128 MB – 2 GB
Catalyst 9000 series Onboard flash flash: or bootflash: 16 GB – 32 GB
ASR 1000 series Hard disk + bootflash harddisk:, bootflash: 200 GB+ (harddisk)
Always check free flash before downloading. IOS images are 300 MB–600 MB for ISR platforms and 50 MB–150 MB for Catalyst switches. If flash is nearly full, the TFTP transfer will fail partway through — after writing hundreds of megabytes — leaving a corrupt partial file that must be deleted. Always run show flash: (or show bootflash:) and verify sufficient free space before starting the transfer.

2. Lab Topology & TFTP Server Setup

  ┌──────────────────┐           ┌─────────────────────────────────┐
  │  TFTP Server     │           │  NetsTuts_R1 (ISR 4321)         │
  │  192.168.1.100   │──Gi0/0────│  Gi0/0: 192.168.1.1/24          │
  │  Ubuntu / SolarWinds │       │  Current: IOS-XE 16.09.06       │
  │  Tftpd64 / macOS│           │  Target:  IOS-XE 16.12.04       │
  └──────────────────┘           └─────────────────────────────────┘
                                           │
                                      ┌────┴────┐
                                      │  SW1    │
                                      │Catalyst │
                                      │ 2960    │
                                      │Current: │
                                      │15.2(7)E6│
                                      │Target:  │
                                      │15.2(7)E9│
                                      └─────────┘

  TFTP Server Requirements:
    OS:       Windows (Tftpd64 / SolarWinds TFTP), Linux (tftpd-hpa),
              or macOS (built-in tftpd)
    IP:       192.168.1.100 (reachable from device management interface)
    Root dir: Folder containing the .bin image file(s)
    Firewall: UDP port 69 OPEN inbound on the TFTP server
              (TFTP uses UDP 69 for requests, random high port for data)

  IOS Image Files Required (obtain from cisco.com — CCO login needed):
    Router: isr4300-universalk9.16.12.04.SPA.bin  (approx 520 MB)
            MD5 from Cisco download page: a3f8c12d...  (record this)
    Switch: c2960-lanbasek9-mz.152-7.E9.bin       (approx 12 MB)
            MD5 from Cisco download page: 7b4e91a2...  (record this)

  Management Connectivity:
    Console cable connected (REQUIRED — you will reboot the device)
    SSH / Telnet access to privileged exec confirmed
    Ping from device to TFTP server succeeds before starting
  

Step 1 — Set Up the TFTP Server (Linux/Ubuntu)

# Install tftpd-hpa on Ubuntu/Debian
$ sudo apt install tftpd-hpa

# Configure TFTP root directory and options
$ sudo nano /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"          # where image files must be placed
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure --create"    # --create allows uploads from device

# Create root directory and set permissions
$ sudo mkdir -p /srv/tftp
$ sudo chown tftp:tftp /srv/tftp
$ sudo chmod 777 /srv/tftp

# Copy your IOS image to the TFTP root
$ sudo cp ~/Downloads/isr4300-universalk9.16.12.04.SPA.bin /srv/tftp/
$ sudo cp ~/Downloads/c2960-lanbasek9-mz.152-7.E9.bin /srv/tftp/

# Start and verify the TFTP service
$ sudo systemctl restart tftpd-hpa
$ sudo systemctl status tftpd-hpa
● tftpd-hpa.service - LSB: HPA's tftp server
     Active: active (running)

# Verify UDP 69 is listening
$ sudo ss -ulnp | grep :69
UNCONN  0  0   0.0.0.0:69  0.0.0.0:*  users:(("in.tftpd",...))

# Note the MD5 of the image on the server for later comparison
$ md5sum /srv/tftp/isr4300-universalk9.16.12.04.SPA.bin
a3f8c12d9e4b72f1c8d5a0e3b6f91234  isr4300-universalk9.16.12.04.SPA.bin
  
The --create option in tftpd-hpa allows the Cisco device to write a file to the TFTP server — needed if you want to back up the current running-config or the old IOS image to the server before upgrading. This is optional but recommended as a safety measure. On Windows, Tftpd64 and SolarWinds Free TFTP Server provide a GUI interface where you set the root folder — place the .bin file in that folder and the service handles the rest. Always record the MD5 hash of the image on the TFTP server before transferring — this is the reference value you will compare against after the copy.

3. Step 2 — Pre-Upgrade Checks on the Device

Run these checks on the router or switch before initiating any file transfer. Skipping pre-checks is the most common cause of mid-transfer failures and post-upgrade problems.

Check 1 — Verify Current IOS Version

Use show version to confirm the current IOS version, flash path, and config-register before any upgrade.

NetsTuts_R1#show version
Cisco IOS XE Software, Version 16.09.06
Cisco IOS Software [Fuji], ISR Software (X86_64_LINUX_IOSD-UNIVERSALK9-M),
Version 16.9.6, RELEASE SOFTWARE (fc3)
...
cisco ISR4321/K9 (1RU) processor with 1647778K/6147K bytes of memory.
Processor board ID FLM2044W0LT
...
4 Gigabit Ethernet interfaces
32768K bytes of non-volatile configuration memory.
8388608K bytes of physical memory.
7697408K bytes of flash memory at bootflash:.        ← total flash
...
Configuration register is 0x2102
  
Record the current IOS version, the flash storage path (bootflash: on ISR 4000, flash: on ISR 1900/2900/3900 and Catalyst), and the config-register value. The config-register must be 0x2102 (or similar normal value) before starting — if it shows 0x2142 from a previous password recovery that was not cleaned up, fix it now before upgrading.

Check 2 — Verify Flash Space

! ── ISR 4000: use 'show bootflash:' ──────────────────────────────
NetsTuts_R1#show bootflash:
-#- --length-- -----date/time------ path
  1  521347072 Mar 05 2026 09:14:22  isr4300-universalk9.16.09.06.SPA.bin
  2       4096 Mar 05 2026 09:14:22  .installer/
  3    1823744 Mar 05 2026 09:15:01  nvram:startup-config-20260305

7696801792 bytes available (789737472 bytes used)   ← ~7.3 GB free
! ── New image is ~520 MB — 7.3 GB free is more than enough ────────

! ── ISR 1900/2900 or Catalyst: use 'show flash:' ─────────────────
NetsTuts_SW1#show flash:
Directory of flash:/

    2  -rwx   12582912  Mar 05 2026  c2960-lanbasek9-mz.152-7.E6.bin   ← old
    3  -rwx    4414     Mar 05 2026  config.text
    4  -rwx     736     Mar 05 2026  vlan.dat

64016384 bytes total (50511872 bytes available)   ← ~48 MB free
! ── New switch image is ~12 MB — 48 MB free is sufficient ─────────
  

Check 3 — Verify TFTP Reachability

! ── Ping TFTP server from the management interface ───────────────
NetsTuts_R1#ping 192.168.1.100
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 192.168.1.100, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5)

! ── Extended ping to test with larger packets (TFTP uses 512-byte blocks)
NetsTuts_R1#ping 192.168.1.100 size 1500 repeat 20
!!!!!!!!!!!!!!!!!!!!
Success rate is 100 percent (20/20)

! ── If management VRF is used, specify it ────────────────────────
NetsTuts_R1#ping vrf Mgmt-intf 192.168.1.100
  
TFTP transfers large files in 512-byte data blocks over UDP — packet loss causes retransmissions that slow the transfer dramatically. Testing with larger packet sizes and multiple repetitions before starting a 500 MB transfer avoids the scenario of a transfer taking 45 minutes instead of 5 minutes due to a marginal network path. If any packet loss is detected (! replaced by .), fix the underlying network issue before proceeding — do not attempt a large file transfer over a lossy path.

Check 4 — Back Up Current Configuration

Back up the running-config before any changes. Ensure NTP is configured so backup filenames and logs have accurate timestamps.

! ── Save running-config to TFTP before any changes ───────────────
NetsTuts_R1#copy running-config tftp:
Address or name of remote host []? 192.168.1.100
Destination filename [NetsTuts_R1-confg]? NetsTuts_R1-backup-20260307.cfg
!!
1823 bytes copied in 0.204 secs (8937 bytes/sec)

! ── Optionally back up current IOS image to TFTP ─────────────────
! ── WARNING: this copies a 500MB+ file — only if TFTP has space ──
NetsTuts_R1#copy bootflash:isr4300-universalk9.16.09.06.SPA.bin tftp:
Address or name of remote host []? 192.168.1.100
Destination filename [isr4300-universalk9.16.09.06.SPA.bin]?
!!!!!!!!!!!!!!!!...
521347072 bytes copied in 1234.567 secs (422465 bytes/sec)
  

4. Step 3 — Copy the New IOS Image to Flash

Router — Copy TFTP to bootflash (ISR 4000)

NetsTuts_R1#copy tftp: bootflash:
Address or name of remote host []? 192.168.1.100
Source filename []? isr4300-universalk9.16.12.04.SPA.bin
Destination filename [isr4300-universalk9.16.12.04.SPA.bin]?
  [press Enter — keep same filename]

Accessing tftp://192.168.1.100/isr4300-universalk9.16.12.04.SPA.bin...
Loading isr4300-universalk9.16.12.04.SPA.bin
  from 192.168.1.100 (via GigabitEthernet0/0/0):
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
[OK - 521347072 bytes]

521347072 bytes copied in 1187.234 secs (439151 bytes/sec)
  
Each ! represents a UDP block successfully received and written to flash. A . indicates a timeout — the block was retransmitted. For a 500 MB image, expect 1000+ exclamation marks. If the transfer shows mostly dots and times out, the TFTP connection is unreliable — abort with Ctrl+C, diagnose the network path, and retry. TFTP transfer speed over a 1 Gbps LAN is typically 300–500 MB/s (throughput limited by flash write speed). Over a WAN or slower management network, plan for 10–30 minutes per 500 MB image.

Router — Alternative: Direct URL Syntax

! ── Single-line copy with full URL (faster to type) ──────────────
NetsTuts_R1#copy tftp://192.168.1.100/isr4300-universalk9.16.12.04.SPA.bin bootflash:
Destination filename [isr4300-universalk9.16.12.04.SPA.bin]?
  [press Enter]
!!!!!!!!!!!!!!!!!!!!!!!!!!!...
521347072 bytes copied in 1187.234 secs
  

Catalyst Switch — Copy TFTP to flash

NetsTuts_SW1#copy tftp: flash:
Address or name of remote host []? 192.168.1.100
Source filename []? c2960-lanbasek9-mz.152-7.E9.bin
Destination filename [c2960-lanbasek9-mz.152-7.E9.bin]?
  [press Enter]

Accessing tftp://192.168.1.100/c2960-lanbasek9-mz.152-7.E9.bin...
Loading c2960-lanbasek9-mz.152-7.E9.bin from 192.168.1.100:
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
[OK - 12582912 bytes]

12582912 bytes copied in 35.211 secs (357348 bytes/sec)
  

Verify the File Was Written Correctly

! ── Check new image exists in flash ─────────────────────────────
NetsTuts_R1#show bootflash:
-#- --length-- -----date/time------ path
  1  521347072 Mar 05 2026 09:14:22  isr4300-universalk9.16.09.06.SPA.bin  ← old
  2  521347072 Mar 07 2026 14:22:08  isr4300-universalk9.16.12.04.SPA.bin  ← new ✓

7175454720 bytes available (1311883264 bytes used)

NetsTuts_SW1#show flash:
Directory of flash:/

    2  -rwx   12582912  Mar 05 2026  c2960-lanbasek9-mz.152-7.E6.bin   ← old
    3  -rwx   12582912  Mar 07 2026  c2960-lanbasek9-mz.152-7.E9.bin   ← new ✓
    4  -rwx    4414     Mar 05 2026  config.text
    5  -rwx     736     Mar 05 2026  vlan.dat

64016384 bytes total (37748736 bytes available)
  

5. Step 4 — Verify the MD5 Checksum

MD5 verification is mandatory — never boot a new IOS image without verifying its integrity first. A corrupted or tampered image can cause the device to boot into a broken state, crash mid-operation, or introduce security vulnerabilities. The MD5 hash from Cisco's software download page must match the hash computed on the device after transfer.

Where to Find the Official MD5 Hash

  Source: software.cisco.com (requires CCO account)

  When downloading an IOS image, the download page shows:
    File name:    isr4300-universalk9.16.12.04.SPA.bin
    File size:    521,347,072 bytes
    MD5 checksum: a3f8c12d9e4b72f1c8d5a0e3b6f91234  ← copy this exactly
    SHA-512:      7b4e91a2f3c8d5e0a1b6c9d2e4f70123...

  ALWAYS record the MD5 (or SHA-512) from the DOWNLOAD PAGE before
  downloading — not from a secondary source or email.

  You can also verify the file on the TFTP server (Linux):
    $ md5sum /srv/tftp/isr4300-universalk9.16.12.04.SPA.bin
    a3f8c12d9e4b72f1c8d5a0e3b6f91234

  This gives you two reference points:
    1. Cisco's official hash (from cisco.com download page)
    2. Hash on the TFTP server before transfer
  All three values must match: Cisco → TFTP server → device flash.
  

Verify MD5 on the Router

! ── Compute MD5 of the newly transferred image ───────────────────
! ── WARNING: this takes 2–5 minutes for a 500 MB file ─────────────
NetsTuts_R1#verify /md5 bootflash:isr4300-universalk9.16.12.04.SPA.bin
....................................................................
....................................................................
....................................................................
Done!
verify /md5 (bootflash:isr4300-universalk9.16.12.04.SPA.bin) = a3f8c12d9e4b72f1c8d5a0e3b6f91234

! ── Compare against Cisco's published MD5: a3f8c12d9e4b72f1c8d5a0e3b6f91234
! ── ✓ MATCH — image integrity confirmed, safe to proceed ─────────

! ── Optional: compare against a known-good hash in one command ───
NetsTuts_R1#verify /md5 bootflash:isr4300-universalk9.16.12.04.SPA.bin a3f8c12d9e4b72f1c8d5a0e3b6f91234
....................................................................
Verified (bootflash:isr4300-universalk9.16.12.04.SPA.bin) = a3f8c12d9e4b72f1c8d5a0e3b6f91234
  

Verify MD5 on the Switch

NetsTuts_SW1#verify /md5 flash:c2960-lanbasek9-mz.152-7.E9.bin
............................................
Done!
verify /md5 (flash:c2960-lanbasek9-mz.152-7.E9.bin) = 7b4e91a2f3c8d5e0a1b6c9d2e4f70123

! ── Compare with Cisco's value: 7b4e91a2f3c8d5e0a1b6c9d2e4f70123
! ── ✓ MATCH — proceed to boot statement update ──────────────────
  

What to Do If MD5 Does Not Match

! ── MISMATCH EXAMPLE — do not proceed ────────────────────────────
verify /md5 (bootflash:isr4300-universalk9.16.12.04.SPA.bin) = ff00aa11bb22cc33dd44ee55ff66aa77
! ── Cisco's value:                                                 a3f8c12d9e4b72f1c8d5a0e3b6f91234
! ── These do not match — image is CORRUPT or TAMPERED ─────────────

! ── Delete the bad file ─────────────────────────────────────────
NetsTuts_R1#delete bootflash:isr4300-universalk9.16.12.04.SPA.bin
Delete filename [isr4300-universalk9.16.12.04.SPA.bin]?
Delete bootflash:isr4300-universalk9.16.12.04.SPA.bin? [confirm]

! ── Diagnose before retrying ────────────────────────────────────
! ── 1. Re-check the MD5 on the TFTP server itself ────────────────
!    $ md5sum /srv/tftp/isr4300-universalk9.16.12.04.SPA.bin
!    If server MD5 ≠ Cisco's MD5 → re-download from cisco.com
!    If server MD5 = Cisco's MD5 → transfer was corrupted
! ── 2. Check network for packet loss before retrying ─────────────
! ── 3. Re-download image from cisco.com if in doubt ──────────────
! ── NEVER BOOT AN IMAGE WITH A MISMATCHED MD5 ────────────────────
  
A mismatched MD5 is a hard stop — do not boot the image under any circumstances. Common causes: (1) network packet loss corrupting data in transit, (2) the TFTP server itself has a corrupt file (re-download from Cisco), (3) the wrong image variant was downloaded (verify filename matches your platform exactly), (4) flash media errors (rare, but a flash chip with write errors can produce corrupt images even from a clean source — test flash health with verify /md5 flash:[filename] multiple times; inconsistent results indicate hardware issues). The MD5 algorithm detects any single-bit change in the file — you cannot "mostly" pass MD5. Either it matches exactly or the file is wrong.

6. Step 5 — Update the Boot System Statement

With the new image in flash and MD5 verified, update the boot configuration to point to the new image. This involves removing (or replacing) the old boot statement and adding the new one before the reload.

Router — Update Boot Statement (ISR 4000)

! ── Check current boot statements ────────────────────────────────
NetsTuts_R1#show running-config | include boot
boot-start-marker
boot system bootflash:isr4300-universalk9.16.09.06.SPA.bin
boot-end-marker

! ── Remove old boot statement ────────────────────────────────────
NetsTuts_R1(config)#no boot system bootflash:isr4300-universalk9.16.09.06.SPA.bin

! ── Add new boot statement ───────────────────────────────────────
NetsTuts_R1(config)#boot system bootflash:isr4300-universalk9.16.12.04.SPA.bin

! ── Add the old image as a FALLBACK (best practice) ─────────────
NetsTuts_R1(config)#boot system bootflash:isr4300-universalk9.16.09.06.SPA.bin

! ── Verify boot statements in running-config ─────────────────────
NetsTuts_R1#show running-config | include boot system
boot system bootflash:isr4300-universalk9.16.12.04.SPA.bin   ← 1st (new)
boot system bootflash:isr4300-universalk9.16.09.06.SPA.bin   ← 2nd (fallback)

! ── Confirm config-register is correct ──────────────────────────
NetsTuts_R1#show version | include register
Configuration register is 0x2102
  
Listing the old image as a second boot system statement creates an automatic fallback — if the new image is corrupt or fails to load for any reason, IOS automatically tries the second statement and boots the old image. This is a critical safety net in production. Without it, a failed boot of the new image drops the device to ROMMON, requiring manual intervention. With the fallback, the device self-heals by loading the previous known-good image, keeping the network operational until you can diagnose the issue.

Catalyst Switch — Update Boot Statement

! ── Check current boot statements ────────────────────────────────
NetsTuts_SW1#show running-config | include boot
boot system flash:c2960-lanbasek9-mz.152-7.E6.bin

! ── Remove old boot statement ────────────────────────────────────
NetsTuts_SW1(config)#no boot system flash:c2960-lanbasek9-mz.152-7.E6.bin

! ── Add new boot statement ───────────────────────────────────────
NetsTuts_SW1(config)#boot system flash:c2960-lanbasek9-mz.152-7.E9.bin

! ── Add fallback to old image ────────────────────────────────────
NetsTuts_SW1(config)#boot system flash:c2960-lanbasek9-mz.152-7.E6.bin

! ── Verify ───────────────────────────────────────────────────────
NetsTuts_SW1#show running-config | include boot system
boot system flash:c2960-lanbasek9-mz.152-7.E9.bin   ← primary
boot system flash:c2960-lanbasek9-mz.152-7.E6.bin   ← fallback
  

Save Configuration Before Reload

! ── CRITICAL: save running-config — the new boot statements must ─
! ── be in startup-config or they are lost on reload ──────────────
NetsTuts_R1#copy running-config startup-config
Destination filename [startup-config]?
Building configuration...
[OK]

NetsTuts_SW1#copy running-config startup-config
Building configuration...
[OK]

! ── Verify the boot statements are in the startup-config ─────────
NetsTuts_R1#show startup-config | include boot system
boot system bootflash:isr4300-universalk9.16.12.04.SPA.bin
boot system bootflash:isr4300-universalk9.16.09.06.SPA.bin
! ── Both confirmed in startup-config ─────────────────────────────
  
This is the most critical save in the upgrade process. Boot system statements are only evaluated from the startup-config — not the running-config. If you skip copy running-config startup-config here and then reload, the device uses the startup-config that still has the old boot system statement (or none at all), and may not boot the new image. Always verify the boot statements are present in show startup-config before issuing the reload command.

7. Step 6 — Reload and Verify the New Version

Schedule and Execute the Reload

! ── Optional: schedule a reload with a delay (allows remote sessions
!    to disconnect gracefully and gives you a window to abort) ──────
NetsTuts_R1#reload in 5
Reload scheduled in 5 minutes by admin on vty0 (192.168.1.50)
Proceed with reload? [confirm]

! ── To cancel a scheduled reload ────────────────────────────────
NetsTuts_R1#reload cancel
***
*** --- SHUTDOWN ABORTED ---
***

! ── Immediate reload ─────────────────────────────────────────────
NetsTuts_R1#reload
System configuration has been modified. Save? [yes/no]: no
  ← type 'no' — we already saved manually; don't overwrite
Proceed with reload? [confirm]
  [press Enter]

! ── Console output during reload ────────────────────────────────
*Mar  7 14:35:00.001: %SYS-5-RELOAD: Reload requested by admin on vty0.
  Reload Reason: Reload Command.

System Bootstrap, Version 16.x
...
Self decompressing the image: ##############...
...
Cisco IOS XE Software, Version 16.12.04   ← new version loading
  
When prompted "System configuration has been modified. Save?", always answer no if you have already manually run copy running-config startup-config. If you answer yes, IOS does one final save — which is safe if the running-config is correct, but if anything changed in the running-config during the upgrade process that you did not intend to keep, answering yes saves those changes. The safer discipline is to save explicitly when you intend to save, and say no to this prompt. Use reload in [minutes] for production changes — it gives you a brief window to abort if you suddenly realize something is wrong before the reload executes.

Verify the New Version with show version

! ── After reload completes — verify new IOS version ─────────────
NetsTuts_R1#show version
Cisco IOS XE Software, Version 16.12.04          ← ✓ new version
Cisco IOS Software [Gibraltar], ISR Software (X86_64_LINUX_IOSD-UNIVERSALK9-M),
Version 16.12.4, RELEASE SOFTWARE (fc5)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2020 by Cisco Systems, Inc.
Compiled Sun 05-Jan-20 13:48 by mcpre

Cisco IOS-XE software, Copyright (c) 2005-2020 by cisco Systems, Inc.

...
cisco ISR4321/K9 (1RU) processor with 1647778K/6147K bytes of memory.
Processor board ID FLM2044W0LT

Configuration register is 0x2102                 ← ✓ correct register

! ── Verify on the switch ────────────────────────────────────────
NetsTuts_SW1#show version
Cisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 15.2(7)E9   ← ✓
RELEASE SOFTWARE (fc3)
...
Configuration register is 0xF                    ← note: switches use 0xF
  

Full Post-Upgrade Verification Sequence

! ── 1. Confirm version ───────────────────────────────────────────
NetsTuts_R1#show version | include Version
Cisco IOS XE Software, Version 16.12.04

! ── 2. Confirm running image path ────────────────────────────────
NetsTuts_R1#show version | include System image
System image file is "bootflash:isr4300-universalk9.16.12.04.SPA.bin"  ← ✓

! ── 3. Confirm boot statements survived reload ───────────────────
NetsTuts_R1#show running-config | include boot system
boot system bootflash:isr4300-universalk9.16.12.04.SPA.bin
boot system bootflash:isr4300-universalk9.16.09.06.SPA.bin

! ── 4. Confirm config-register ───────────────────────────────────
NetsTuts_R1#show version | include register
Configuration register is 0x2102

! ── 5. Confirm all interfaces came back up ───────────────────────
NetsTuts_R1#show ip interface brief
GigabitEthernet0/0/0   192.168.1.1    YES NVRAM  up  up
GigabitEthernet0/0/1   10.0.12.1      YES NVRAM  up  up

! ── 6. Confirm routing protocols re-established ──────────────────
NetsTuts_R1#show ip ospf neighbor
Neighbor ID   Pri  State    Dead Time  Address      Interface
2.2.2.2         1  FULL/DR  00:00:37   10.0.12.2    Gi0/0/1

! ── 7. Optional: delete old image to reclaim flash space ─────────
! ── Only AFTER confirming new version is running correctly ────────
! ── And AFTER removing the fallback boot statement ────────────────
NetsTuts_R1(config)#no boot system bootflash:isr4300-universalk9.16.09.06.SPA.bin
NetsTuts_R1#copy running-config startup-config

NetsTuts_R1#delete bootflash:isr4300-universalk9.16.09.06.SPA.bin
Delete filename [isr4300-universalk9.16.09.06.SPA.bin]?
Delete bootflash:isr4300-universalk9.16.09.06.SPA.bin? [confirm]
  
Wait at least 24–48 hours (or one full traffic cycle) before deleting the old image. This gives time to observe the device under real load and confirm no unexpected behaviour from the new version. If issues appear — unexpected reboots, feature regressions, protocol instability — having the old image still in flash with a fallback boot statement means recovery requires only adding the old boot statement back and reloading, rather than another TFTP transfer.

8. Emergency Recovery — ROMMON TFTP Download

If the device fails to boot after an upgrade — either because the image was corrupt, the boot statement pointed to a non-existent file, or flash was accidentally wiped — ROMMON provides a TFTP boot option. This is the last-resort procedure before returning the device to Cisco.

ROMMON Recovery via tftpdnld (ISR Routers)

! ── Device is stuck at rommon 1 > ───────────────────────────────
! ── Set ROMMON environment variables for TFTP ───────────────────
rommon 1 > IP_ADDRESS=192.168.1.1
rommon 2 > IP_SUBNET_MASK=255.255.255.0
rommon 3 > DEFAULT_GATEWAY=192.168.1.254
rommon 4 > TFTP_SERVER=192.168.1.100
rommon 5 > TFTP_FILE=isr4300-universalk9.16.12.04.SPA.bin
rommon 6 > GE_PORT=0              ← use GigabitEthernet0 (management port)

! ── Initiate TFTP download ───────────────────────────────────────
rommon 7 > tftpdnld
IP_ADDRESS: 192.168.1.1
IP_SUBNET_MASK: 255.255.255.0
DEFAULT_GATEWAY: 192.168.1.254
TFTP_SERVER: 192.168.1.100
TFTP_FILE: isr4300-universalk9.16.12.04.SPA.bin
GE_PORT: 0

Invoke this command for disaster recovery only.
WARNING: all existing data in all partitions on flash will be lost!
Do you wish to continue? y/n:  [n]: y

Receiving isr4300-universalk9.16.12.04.SPA.bin
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
[OK - 521347072/521347072 bytes]

File reception completed.
Booting flash:isr4300-universalk9.16.12.04.SPA.bin
  
The tftpdnld command writes the image directly to flash from ROMMON — it does NOT require IOS to be running. The warning "all existing data in all partitions on flash will be lost" is important: on some platforms (particularly ISR 1900/2900/3900), tftpdnld formats flash before writing. Backup your config first if possible. On ISR 4000 platforms, tftpdnld writes to a specific partition and preserves the startup-config in most cases. After the download completes, ROMMON boots the image automatically. You will still need to re-enter boot system statements and copy running-config startup-config once IOS loads.

Catalyst Switch ROMMON Recovery (switch: prompt)

! ── Switch stuck at switch: prompt — flash is empty or corrupt ───
switch: set IP_ADDR 192.168.1.10
switch: set NETMASK 255.255.255.0
switch: set DEFAULT_ROUTER 192.168.1.1
switch: set TFTP_SERVER 192.168.1.100
switch: set TFTP_FILE c2960-lanbasek9-mz.152-7.E9.bin

switch: tftp_init
switch: ether_init

! ── Download and boot ────────────────────────────────────────────
switch: boot tftp://192.168.1.100/c2960-lanbasek9-mz.152-7.E9.bin
  Loading c2960-lanbasek9-mz.152-7.E9.bin from 192.168.1.100:
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  [OK]
  Booting image...
  

Upgrade Troubleshooting Reference

Symptom Most Likely Cause Resolution
TFTP transfer shows %Error opening tftp://... TFTP server unreachable, wrong IP, UDP 69 blocked by firewall, or wrong source interface Ping TFTP server first. Check firewall rules. Try copy tftp: flash: with explicit source: ip tftp source-interface [int]. See also SSH Configuration for SCP as a more secure alternative.
Transfer starts but stalls with many dots (...) Network packet loss, TFTP server overloaded, or slow WAN link causing timeouts Abort (Ctrl+C), delete partial file, diagnose network with ping/MTR, retry on better path
%Error: Not enough space on device Flash does not have enough free space for the new image Delete old image or other unused files. Verify with show flash: free bytes vs image size
MD5 mismatch after transfer Transfer corruption, wrong image file on TFTP server, or flash write error Delete the file, verify MD5 on TFTP server, check for network packet loss, retry transfer
Device boots old image after reload Boot system statement saved but points to old image, or no boot system statement saved Check show startup-config | include boot. Add/fix boot system statement, save, reload
Device drops to ROMMON after reload Boot system statement points to a filename that does not exist in flash, or image is corrupt From ROMMON: set correct filename and boot flash:[correct-filename], or use tftpdnld
show version shows wrong version after reload Old image loaded as fallback (new image failed), or boot statement not saved before reload Verify which image is actually running with show version | include System image. Re-check and fix boot statements
Interfaces down after upgrade New IOS version uses different interface naming or a licence issue disabled some features Check show ip interface brief. Re-apply no shutdown on affected interfaces. Verify licence with show licence

9. Complete Upgrade Workflow & Key Points

End-to-End Upgrade Checklist

  PRE-UPGRADE
  ✔  Download correct IOS image from cisco.com for your exact platform
  ✔  Record MD5 hash from the Cisco download page
  ✔  Place image in TFTP server root directory
  ✔  Verify TFTP service is running (UDP 69 listening)
  ✔  Back up device running-config to TFTP
  ✔  Check current IOS version: show version
  ✔  Check flash space: show flash: / show bootflash:
  ✔  Confirm TFTP reachability: ping [tftp-ip]
  ✔  Confirm config-register: 0x2102 (not 0x2142)
  ✔  Schedule maintenance window (reload = service disruption)

  DURING UPGRADE
  ✔  Copy image: copy tftp: bootflash: (or flash:)
  ✔  Verify file size matches: show bootflash: / show flash:
  ✔  Verify MD5: verify /md5 bootflash:[filename] [hash]
  ✔  Confirm MD5 matches Cisco's published value
  ✔  Update boot statement: remove old, add new (+ fallback to old)
  ✔  Save: copy running-config startup-config
  ✔  Confirm boot statements in startup-config
  ✔  Reload: reload (or reload in [minutes])

  POST-UPGRADE
  ✔  Verify version: show version | include Version
  ✔  Verify running image: show version | include System image
  ✔  Verify boot statements: show running-config | include boot system
  ✔  Verify config-register: show version | include register
  ✔  Verify all interfaces up: show ip interface brief
  ✔  Verify routing protocols: show ip ospf neighbor / show ip route
  ✔  Run traffic/connectivity tests
  ✔  Wait 24–48 hours before deleting old image
  ✔  Delete old image + remove fallback boot statement + save
  

Key Points & Exam Tips

  • MD5 verification is non-negotiable. Always run verify /md5 flash:[image] after every TFTP transfer and compare against Cisco's published hash. A corrupt image may appear to boot initially but crash the device under load or introduce stability issues that are extremely difficult to diagnose.
  • Boot system statement order is a priority list. IOS tries each statement sequentially and boots the first image it successfully opens. New image first, old image as fallback — this order provides both an automatic upgrade and a self-healing fallback with zero additional effort.
  • Save before reload — always. Boot system statements in the running-config are lost on reload if not saved. The sequence is: update boot statement → copy running-config startup-config → verify in startup-config → reload. No exceptions.
  • ISR 4000 uses bootflash:; older ISR and Catalyst use flash:. Using the wrong filesystem prefix causes "file not found" errors. Always confirm the flash location with show version or dir flash: / dir bootflash: before starting.
  • Never delete the old image until the new version is confirmed stable. The old image + fallback boot statement is your insurance policy. Remove it only after the device has run under real load for at least one business day.
  • TFTP has no authentication or encryption. It is suitable for management-network-only file transfers. Never transfer IOS images over the public internet via TFTP — use SCP (copy scp: flash:) for encrypted, authenticated file transfers in security-sensitive environments. See SSH Configuration for enabling SSH and SCP on the device.
  • The install mode on IOS-XE 16.x+ (install add, install activate, install commit) provides in-service software upgrade (ISSU) on supported platforms — some models can upgrade without a full reload. The traditional .bin method in this lab applies to all platforms; install mode is the modern approach for high-availability environments.
  • On the CCNA exam: know the correct order of operations (copy → verify MD5 → update boot statement → save → reload → verify), the verify /md5 command syntax, the correct config-register value for normal operation (0x2102), and how to interpret show version output to confirm which image is running and where it was loaded from.
Related Labs: If the upgrade leaves the device unbootable and you need to recover via ROMMON console access, see ROMMON & Password Recovery — including the tftpdnld procedure for re-imaging a completely failed flash. For automating IOS upgrades across multiple devices simultaneously using Ansible's ios_command and ios_config modules see Ansible Playbook — Automate IOS Configuration. For using NAPALM's Python library to manage IOS image deployment programmatically see Python NAPALM Library. For securing the file transfer itself with encryption, replace TFTP with SCP — see AAA Configuration which covers enabling SSH and SCP on Cisco IOS.

TEST WHAT YOU LEARNED

1. A router has two IOS images in flash — isr4300-universalk9.16.09.06.SPA.bin (old) and isr4300-universalk9.16.12.04.SPA.bin (new). There are NO boot system statements in the startup-config. Which image does the router boot, and why?

Correct answer is C. When IOS finds no boot system statements in the startup-config, it falls back to loading the first .bin file found in the flash filesystem. The ordering used is alphabetical/lexicographic — the same ordering used by directory listings. In this case, comparing the two filenames: "isr4300-universalk9.16.09.06.SPA.bin" versus "isr4300-universalk9.16.12.04.SPA.bin". The common prefix is "isr4300-universalk9.16." — after that, we compare "09" versus "12". Lexicographically, "09" comes before "12" (because "0" has ASCII value 48 and "1" has ASCII value 49). So the router loads 16.09.06 — the old image. This behaviour is why the lab explicitly teaches setting a boot system statement — it is the only reliable way to control which image the router loads. The fix is: add boot system bootflash:isr4300-universalk9.16.12.04.SPA.bin before the old image's name, save, and reload.

2. During an IOS upgrade via TFTP, the MD5 verification step produces a hash that does not match the value published on Cisco's download page. What are the correct next steps, in order?

Correct answer is A. An MD5 mismatch is an absolute hard stop in IOS upgrade procedures. MD5 is a deterministic hash function — the same input always produces the same 128-bit output. If the MD5 of the transferred file does not match Cisco's published value, the file is either corrupt or has been tampered with. There are no exceptions, no "close enough," and no retrying the verification to get a different result (option D is wrong — MD5 is not probabilistic; the same file always produces the same hash). Booting a corrupt image (option B) can cause: the device to crash immediately on boot, memory corruption during operation leading to intermittent failures, security vulnerabilities if the image was tampered with, or data plane corruption affecting production traffic. The diagnostic process matters: checking the TFTP server's own MD5 helps pinpoint whether the corruption happened during the download from Cisco (server file is already corrupt) or during the TFTP transfer to the device (network issue). This gives the correct remediation — re-download vs re-transfer. Delete-first before retrying is also critical: if you retry without deleting, you may have two copies of a corrupt file and be looking at the wrong one.

3. What is the purpose of configuring the old image as a second boot system statement after adding the new image as the first? Is there any risk to this approach?

Correct answer is D. The boot system statement list is evaluated sequentially — IOS tries each entry in order and uses the first one that succeeds. The fallback pattern (new image first, old image second) is a standard production safety practice: if anything prevents the new image from loading, the device self-heals by loading the old image and maintaining network connectivity. Without the fallback, a failed new image boot drops the device to ROMMON, requiring physical console access for recovery — a potentially severe incident for a remote site. The subtle risk mentioned in option D is real: if a newly upgraded device has a software defect that causes it to crash and reload, and the new image consistently fails to load (due to a recurring bug), the fallback keeps the device running on the old image. From the network's perspective everything looks fine — routes are up, traffic is flowing. But a log check would reveal repeated reload events. Always check show version | include uptime and review syslog after an upgrade to confirm the device has been running continuously on the new image rather than silently falling back.

4. An engineer copies a new IOS image to flash and updates the boot system statement, but forgets to run copy running-config startup-config before issuing reload. What happens?

Correct answer is B. This scenario describes one of the most common IOS upgrade mistakes. The running-config and startup-config are separate — changes to the running-config are volatile and lost on reload unless explicitly saved. Boot system statements are just regular running-config entries — they are not stored separately and not automatically persisted. When the engineer runs boot system bootflash:new-image.bin, this adds a line to the running-config. When reload is issued without saving, the reload prompt "System configuration has been modified. Save? [yes/no]:" appears. If the engineer types 'no' (thinking they already saved earlier), the running-config is discarded, and the startup-config is used for the next boot — still containing the old boot statement or no statement. IOS then loads according to the startup-config: either the old image (if the old boot statement is there) or the first alphabetical .bin file (if no statement). The new image stays in flash but is never used. To discover this after the fact: show version | include System image will show the old image path rather than the new one, and show startup-config | include boot system will show the old or missing statement.

5. What is the difference between copy tftp: flash: and booting via tftpdnld in ROMMON? When would you use each?

Correct answer is C. The key distinction is when and from what environment each method runs. copy tftp: flash: is an IOS privilege-exec command — it requires a fully booted, operational IOS instance. The device must have routing or switching functioning so that the TFTP server is reachable, and the IOS file system must be mounted and writable. This is the normal planned-upgrade method. tftpdnld is a ROMMON command that operates at the lowest firmware level, before IOS loads. ROMMON has its own minimal TCP/IP stack and Ethernet driver that can communicate with a directly connected or locally reachable TFTP server without any routing tables, ARP tables, or IOS processes. This is why tftpdnld is the emergency recovery tool — it works even when IOS cannot boot because the flash is empty, the image is corrupt, or the boot image was accidentally deleted. The caveat about reformatting flash is platform-dependent: ISR 1900/2900/3900 ROMMON typically erases flash before writing (destructive), while ISR 4000 ROMMON writes to a specific bootflash partition less destructively. Always check the platform-specific documentation before using tftpdnld on a device with configuration you want to preserve.

6. After a successful IOS upgrade, show version displays "System image file is bootflash:packages.conf" instead of the .bin filename. What does this indicate?

Correct answer is A. IOS-XE 16.x and later supports two operating modes for image management. Bundle mode (traditional): a single monolithic .bin file is loaded at boot. show version shows the .bin filename. This is the method covered in this lab. Install mode (package mode): the .bin file is "installed" using the install add/activate/commit workflow, which unpacks it into sub-packages in flash. The bootflash:packages.conf manifest file tracks which packages are active. show version shows "packages.conf" as the boot source. Install mode benefits: (1) some platforms support ISSU — upgrading the data plane while the control plane stays up, minimising downtime; (2) smaller individual packages can be patched without replacing the entire image (SMUs — Software Maintenance Upgrades); (3) rollback is faster since old packages remain on disk. Install mode is the default on Catalyst 9000 and is increasingly the standard for enterprise deployments. If you see packages.conf and are expecting bundle mode, you can convert with software-install mode bundle. For install mode upgrades, the commands are: install add file bootflash:image.bin activate commit rather than the boot system statement method.

7. Why should SCP be preferred over TFTP for IOS image transfers in a security-sensitive environment, and what must be configured on the Cisco device to use SCP?

Correct answer is D. TFTP was designed in the 1980s for simple, fast file transfer in trusted environments — it has no authentication (anyone can request any file), no encryption (all data is readable in plain text on the wire), and uses UDP which provides no guaranteed delivery or ordering. In a management network that is properly isolated (separate management VLAN, ACLs preventing non-management traffic), TFTP's simplicity is acceptable. In environments where the management network is shared or where regulatory compliance requires encrypted file transfers (PCI-DSS, HIPAA, SOX), TFTP is inappropriate. SCP uses SSH as its transport layer — the same protocol that secures CLI management sessions. The file contents are encrypted using AES or ChaCha20, the server's identity is verified via SSH host key, and the client authenticates with username/password or SSH keys. The Cisco device acts as an SCP server when ip scp server enable is configured, allowing SCP clients on Linux/macOS (scp command) or Windows (WinSCP, pscp) to push files directly to flash. The device's SSH infrastructure (RSA keys, ip ssh version 2, local user database) must be in place before SCP works — which aligns with the AAA and SSH hardening that should already be present on any well-secured device.

8. An ISR router's flash shows 250 MB free, and the new IOS image is 520 MB. The current running image (500 MB) is the only image in flash. What is the correct approach to upgrade this device?

Correct answer is B. Insufficient flash space for dual-image coexistence is a common real-world constraint, particularly on older routers (ISR 1900/2900 series often have only 256 MB or 512 MB flash). The solution requires accepting the loss of the in-flash fallback, but this risk can be mitigated by having the old image on the TFTP server. The correct procedure when flash is tight: (1) Backup the current image to TFTP — this takes time but ensures recovery is possible even without an in-flash fallback. (2) Backup the running-config to TFTP — essential if the new image has issues and you need to reconfigure from scratch. (3) Delete the old image — confirm it's actually gone and flash has enough space for the new image plus headroom. (4) Copy new image. (5) Verify MD5. (6) Update boot statement, save, reload. (7) If boot fails, use ROMMON tftpdnld to re-download from TFTP. The key risk management insight: the fallback mechanism moved from in-flash to TFTP-server. This is less reliable (requires network connectivity from ROMMON, TFTP server must be up) but still provides recovery. For devices with chronic flash space issues, the real solution is a hardware upgrade — adding a larger flash module or CompactFlash card.

9. After a successful upgrade and several weeks of stable operation, an engineer wants to delete the old image and remove the fallback boot statement to reclaim flash space. What is the correct sequence of steps?

Correct answer is C. The order matters for cleanliness and safety. Removing the boot statement first and saving ensures that the startup-config is consistent — it no longer references a file that is about to be deleted. If you delete the file first while the boot statement still exists in startup-config, the startup-config now references a non-existent file. On the next reboot, IOS will attempt to load that file, fail, and then move to the next boot statement (the new image). While this works, it creates a messy configuration with a dangling reference and a brief failed load attempt on every reboot. Option D is tempting but incorrect — IOS does not validate boot statement filenames against flash contents at runtime; it only discovers the file is missing when it tries to open it during boot. For the cleanest procedure: remove the statement → save → verify startup-config → delete the file → verify flash. A final save after deletion ensures any internal state is flushed. The verification steps (show startup-config | include boot system, and show flash:) provide confirmation that both the configuration and the filesystem are in the expected clean state.

10. How do you confirm which IOS image the router actually booted from, as opposed to which image is stored in flash? What two show version outputs give you this information?

Correct answer is D. The distinction between "what is in flash" and "what is actually running" is important in post-upgrade verification. show flash: tells you what files exist in flash storage — it does not tell you which one is currently executing. A device with two images in flash only ever runs one. show version provides the authoritative answer about the current running image through two fields. The Version line shows the IOS version string that was compiled into the running image — confirming the version number matches your target. The "System image file is" line shows the exact path IOS used when decompressing and loading the image into RAM at boot — this is the definitive proof that the specific .bin file you intended was actually loaded. If upgrade went wrong and the fallback triggered, "System image file is bootflash:old-image.bin" would immediately reveal the problem. For install mode (packages.conf), the version confirmation is still in the Version line, but the system image path shows "bootflash:packages.conf" — in that case, use show install active to see which specific packages are running. This two-field verification should be the first thing checked after every IOS upgrade, before any other post-upgrade testing.