Skip to content

Add bootc switch --once for temporary disconnected updates #1806

@cgwalters

Description

@cgwalters

(Note: filing this as a discussion starting point; it was Assisted-by: Sonnet 4.5 and definitely contains a bit of slop)

Summary

Add a --once flag to bootc switch that allows temporarily switching to a different image source while retaining the original registry reference for future updates. This enables devices to alternate between offline and online update modes without losing their registry configuration.

The feature includes built-in idempotency checking, making it safe for edge-triggered automation (such as USB insertion events) to repeatedly invoke the command without creating redundant staged deployments when already at the desired state.

Motivation

Some devices need to support alternating between offline and online updates - receiving updates via local media when disconnected from the registry, while resuming registry-based updates when connectivity is available. Currently, when using bootc switch --transport oci /var/mnt/usb/myos.oci to load an update from local media (USB, SCP, etc.), the system's image reference is permanently changed to the local source. When connectivity is restored, operators must manually run another bootc switch command to restore the original registry reference.

This creates operational burden and increases the risk of systems becoming desynchronized from their intended update channel.

Use Cases

Devices Alternating Between Offline and Online Updates: Deploy updates via USB stick when disconnected from the registry while maintaining the registry reference for when connectivity is restored. Automated scripts can edge-trigger on USB insertion without concern for redundant operations due to built-in idempotency.

Proposed Solution

Add a --once flag to bootc switch:

bootc switch --once --transport oci /var/mnt/usb/myos.oci

Behavior

When --once is specified:

  1. The next staged deployment uses the provided image source (works with all transports: registry, oci, oci-archive, containers-storage, etc.)
  2. Both the source image reference (where the image was actually fetched from) and the target image reference (what should be tracked for future updates) are persisted in the origin metadata
  3. The bootc status continues to show the original image reference as the tracked target; in verbose mode, it also displays the source reference
  4. Subsequent bootc upgrade operations will attempt to pull from the tracked target reference, not the temporary source reference
  5. The temporary source is used only for the immediate switch operation
  6. Idempotency: If the system is already at the desired state (same image digest), bootc switch --once will detect this and skip the operation, returning success without staging a new deployment

Example Workflow

# Initial state: tracking registry image
$ bootc status
Staged: registry:quay.io/exampleos/edge-prod:v2.1

# Temporarily switch to local image from USB
$ bootc switch --once --transport oci /var/mnt/usb/edge-prod-v2.2.oci

# Check status (verbose mode shows both source and target)
$ bootc status --verbose
...
Staged:
  Image: registry:quay.io/exampleos/edge-prod:v2.1
  Source: oci:/var/mnt/usb/edge-prod-v2.2.oci
...

# After reboot and when connectivity returns
$ bootc upgrade
Pulling from registry:quay.io/exampleos/edge-prod:v2.1...

Automated USB Update Workflow with Idempotency

A common use case is edge-triggered automation that runs when a USB key is plugged in:

# Initial state: running v2.1
$ bootc status --booted
Booted:
  Image: registry:quay.io/exampleos/edge-prod:v2.1
  Version: 2.1.0

# USB key with v2.2 is inserted, automation triggers
$ bootc switch --once --transport oci /mnt/usb/edge-prod-v2.2.oci
Staging image from USB...
Staged deployment created.

# System reboots into v2.2
# USB key is still plugged in, automation runs again on boot
$ bootc switch --once --transport oci /mnt/usb/edge-prod-v2.2.oci
Image digest matches current deployment, no action needed.

# No redundant staging occurs - system recognizes it's already at desired state

This idempotency is critical for automation: scripts can safely run bootc switch --once repeatedly without creating unnecessary staged deployments.

The --once flag also works with registry transport for testing scenarios:

# Initial state: tracking production tag
$ bootc status
Staged: registry:quay.io/exampleos/edge-prod:stable

# Temporarily test canary version
$ bootc switch --once quay.io/exampleos/edge-prod:canary
Staged: registry:quay.io/exampleos/edge-prod:canary (temporary)
Target: registry:quay.io/exampleos/edge-prod:stable

# After testing, upgrade returns to stable channel
$ bootc upgrade
Pulling from registry:quay.io/exampleos/edge-prod:stable...

Implementation Considerations

The implementation would need to:

  1. Store both the source image reference (where the image was fetched from) and the target image reference (what to track for updates) in the deployment's origin metadata
  2. Display the target reference by default in bootc status, and additionally show the source reference in --verbose mode
  3. Ensure bootc upgrade always pulls from the target reference, not the source reference
  4. Implement idempotency checking by comparing the image digest of the provided source against the currently booted (or staged) deployment. If the digest matches, skip the operation and return success.
  5. Consider interaction with --retain flag (should they be mutually exclusive or complementary?)

This follows the same pattern as bootc install --source-imgref and --target-imgref, which already support this source/target distinction during installation.

Idempotency Detection

The idempotency check is particularly important for edge-triggered automation (e.g., USB insertion events). The implementation should:

  • Fetch the image manifest/metadata from the provided source to obtain the digest
  • Compare this digest against the booted deployment's digest
  • If they match, report "No action needed" and exit successfully without staging
  • This check should be relatively lightweight, avoiding full layer downloads when possible

Alternative Syntax Considered

An alternative approach could explicitly specify both the source and target:

bootc switch --transport oci /var/mnt/usb/myos.oci --target quay.io/exampleos/edge-prod:v2.1

However, --once is simpler and more intuitive for the common case where the user wants to retain their current target.

Current Workarounds

Users currently have two workarounds:

  1. Use bootc switch --transport oci /path/to/local.oci, then manually run bootc switch registry:quay.io/example/prod:tag when connectivity is restored
  2. Use bootc switch --transport containers-storage PODMAN_IMAGE_ID, with the same manual restoration requirement

Both workarounds:

  • Require manual intervention
  • Risk configuration drift if the second step is forgotten
  • Don't clearly indicate to automation that the system is in a temporary state

Relationship to Existing Features

bootc install --source-imgref and --target-imgref

The --once flag builds on the same source/target image reference pattern already used in bootc install:

  • --source-imgref: Install the system from an explicitly given source
  • --target-imgref: Specify the image to fetch for subsequent updates

The bootc switch --once option applies this same distinction to runtime updates: the source is where you fetch from now (possibly offline/local), while the target is what you track for future updates (typically a registry reference).

--retain Flag

The existing --retain flag keeps a reference to the currently booted image. The --once flag is conceptually similar but inverted: it retains a reference to the target registry while temporarily using a different source.

Disconnected Updates Documentation

The existing documentation at docs/src/registries-and-offline.md describes using bootc switch --transport oci for offline updates, but notes that subsequent bootc upgrade commands are idempotent. The --once flag would change this behavior to make bootc upgrade pull from the original registry when connectivity is restored.

Open Questions

  1. Should there be a way to "clear" the temporary source without rebooting, making the target reference active again immediately?
  2. How does this interact with pinning and explicit digest references?
  3. Should --once be compatible with --retain, or should they be mutually exclusive?
  4. When a deployment is booted that was created with --once, should bootc status indicate this in some special way (e.g., a warning or notice that the booted deployment was fetched from a temporary source)?
  5. For idempotency checking: If there's already a staged deployment (different from the requested image), should bootc switch --once replace it, or should it refuse to operate? What if the staged deployment was also created with --once?

Related Issues

  • #20 - Discusses the separation between podman storage and bootc storage
  • #1320 - Download without applying (finalization locking) - complementary feature for pre-staging updates
  • #1375 - Trickle-fetch support for bandwidth-constrained scenarios

Relationship to Other Proposed Features

Finalization Locking (#1320): This feature is about pre-positioning updates - downloading them without applying. This addresses a different use case than --once, which is about using already pre-positioned images (whether via USB, SCP, or other means) while maintaining the original registry target for future updates.

Trickle-fetch (#1375): The proposed trickle-fetch feature would help with bandwidth-constrained scenarios by limiting download rates or sizes. The --once feature addresses a different scenario: when bandwidth is unavailable or connectivity is completely absent, use a pre-positioned local image while maintaining the registry reference for when connectivity returns.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions