Refresh control

Every snap updates automatically, and by default, the snapd daemon checks for updates 4 times a day. Each update check is called a refresh.

For snap developers, autonomous updates can occasionally cause issues. A snap may update while it’s being used for ongoing video playback, for example, or it may be providing a critical service for flying a drone.

In such cases, it can be beneficial to have greater control over when a snap is refreshed, and the impact that a refresh has on any dependent snaps.

Dependent snaps include those that:

For snap developers, the gate-auto-refresh hook, and its associated snapctl refresh commands, can be used to control when a snap is refreshed and how a refresh interacts with any dependent snaps.

This feature is currently considered experimental and requires an experimental feature-flag to be set before use:

$ sudo snap set system experimental.gate-auto-refresh-hook=true

Experimental refresh control also requires snapd version 2.53+ .


The gate-auto-refresh hook

The gate-auto-refresh hook is executed by snapd for every snap that will be updated with the next automatic refresh. It’s also executed for every snap that is dependent on a snap that will be updated.

This hook is capable of executing the snapctl refresh command with 3 specific arguments, hold, proceed and pending:

snapctl refresh --hold

Requests that snapd does not refresh the calling snap, nor the snaps it depends upon, during the current automatic refresh. A snap can hold its own refresh for up to 90 days and other snaps for up to 48 hours. The command prints an error and returns a non-zero exit status if these deadlines are reached and the refresh can no longer be held.

snapctl refresh --proceed

Signals to snapd that a refresh can proceed for both the calling snap and the snaps it depends upon. This does not necessarily mean the update will happen, because they may be held by other snaps, and snapd only proceeds with auto-refresh after consulting gate-auto-refresh hooks of all potentially affected snaps.

snapctl refresh --pending

Checks whether the executing snap has a pending refresh, or will be affected by the refresh of its base snap.

The output from snapctl refresh --pending includes the following details:

  • pending: none, inhibited or ready
  • channel: tracking-channel
  • version: version (only if there is a pending refresh for the snap itself)
  • revision: revision (only if there is a pending refresh for the snap itself)
  • base: true or false (true if the snap is affected by refresh of its base snap)
  • restart: true or false (true if refresh will cause system restart)

The pending output value is set to “none” if there is no pending refresh for the snap and the value is “ready” if there is a pending refresh. A pending value of “inhibited” indicates that the next refresh is inhibited because one or more of the snap’s applications are running. This currently requires the experimental refresh app awareness feature to be enabled (see below).

The snap-refresh-control interface

The snapctl refresh --proceed command can be executed by a snapped application outside of the gate-auto-refresh hook if the snap has the snap-refresh-control interface and the interface is connected. This enables the snap to trigger an auto-refresh outside of the normal auto-refresh schedule and should be used cautiously.

Please note that the “snapctl refresh” commands cannot be used from hooks other than gate-auto-refresh hook.

If the gate-auto-refresh-hook doesn’t invoke “snapctl refresh --proceed” or “snapctl refresh --hold” commands and exits with exit code 0, the refresh proceeds normally as if the hook didn’t exist (except for respecting “inhibited” status if refresh app awareness is in use).

If the hook fails with an error, snapd assumes “hold” as long as the maximum deadline hasn’t been reached.

2 Likes

Refresh control is tailor made for the use-cases where a snap needs to self-manage its upgrade cycle, such for as Desktop applications, tools like microk8s or multipass, IoT devices, etc. It complements well the option for the upgrade cycle to be externally managed, using cohorts for the use-case where the snap is managed by something like a Charmed Operator or manually by an sysadmin, especially where upgrades need to be coordinated across multiple machines.

Should the title be different to disambiguate this content from Refresh Control (the feature)?

That’s a good point. I think you’re right. How about something more like Refresh control with snapctl?

We seem to have a clash of names with the Refresh Control feature (I am not sure where that doc is now). I’m guessing this new hook should be documented here, the snapctl part should be documented here, and this new feature-level guide should be incorporated into a new top level section “Managing Refreshes” which lists/covers all such methods, where this is a new device-side method. (Maybe this proposed new section would also cover Refresh Control, Validation Sets, Managed Updates, Snap Store Proxy Overrides, and maybe Refresh App Awareness.)

Thanks @kyleN. I was going to add to the hook and snapctl docs after this landed. I think you’re right in that the various docs that deal with refresh control need to be better arranged and referenced so there’s no naming confusion. I’ll do this.

Is this feature possible only for automatic refresh? Can I control and conditionally stop manual refresh as well with this?

The alleged explanation, “The gate-auto-refresh hook is executed by snapd for every snap that will be updated with the next automatic refresh. It’s also executed for every snap that is dependent on a snap that will be updated.” says nothing about when that hook is actually invoked.

So that hook is executed by snapd for certain snaps … when?

Is it worth mentioning the upcoming “–hold=forever” option?

Two questions about gate-auto-refresh hook (one which I asked before).

First, when is that hook script run? That is, what precisely is the action that triggers it? Is it snapd just about to perform a refresh operation?

Second, this page suggests that hook is still experimental. Is that true?