First-boot / device initialization UX

This is a longer post on user experience issues I’m struggling with during first-boot / initialization of a RasPi-based Ubuntu Core appliance. We have a gadget image which is distributed as software-only, i.e. our (non-technical) users flash the image themselves – so a clean first-boot experience is absolutely necessary. The software is a kiosk-style application where users are not expected to connect input devices.

With this thread, I’d like to:

  • Validate my assumptions about snapd/device workflows – please do tell me if I missed something from the docs!
  • Maybe gather workarounds from more experienced developers
  • Provide feedback for first-boot / initialization improvements to Ubuntu Core / snapd developers

I have described my current workflow in this thread regarding snap refreshes. Basically, I’m flashing a fresh image from ubuntu-image onto an SD card, wait for the seeding to finish and then resize + grab an image from the SD card to distribute.

This is time-consuming and I run into all sorts of trouble: For example, snapd force-refreshes all snaps right after a user has set up the image for internet access (see the other thread for reasons). This can result in minutes of blank screens/error messages, sudden reboots or incomplete setups. Less than ideal … I’ve implemented a “snap is updating” message for my own snap via pre/post-refresh hooks, but I couldn’t find a way to be notified about core/snapd updates from within my snap.

I guess I could mitigate this with a system refresh.hold setting in my gadget snap, but even then a) I’d have to rebuild the image at least every 59 days b) as far as I understand, users won’t get any snap updates until the hold period is over. Smaller hold period means earlier update but more frequent rebuilds, so I’m between a rock and a hard stone :slightly_frowning_face:

Another option would be to just distribute the “fresh” image directly from ubuntu-image. This mitigates the refresh problem since I could just recreate this image every night to ensure the latest snaps are seeded. But as outlined in this post, the first boot then takes around 10-15 minutes during which the user sees either a blank screen or an error message if interfaces for my snap haven’t been set up yet. For comparison, the pre-booted image takes around 45 sec. My gadget (fork of the reference pi-gadget) uses psplash to show a custom boot logo, but that is hidden as soon as mir-kiosk is seeded. Plus, I don’t know how to switch from a one-time “preparing device” logo to the regular boot splash.

Is there any way how I can

  1. detect from within a snap if the seeding process is still running? snap get system seed.loaded is unavailable from snapctl. As long as snaps/hooks install in run mode, I guess UC20 install mode doesn’t help me either.
  2. prevent snap services from starting up until seeding is finished? Specifically, mir-kiosk starts right after installation, which results in a blank screen until my wpe-webkit-mir-kiosk is installed.
  3. superimpose a huge “DO NOT TOUCH, STILL WORKING” screen during seeding so that impatient users do not pull the plug :sweat_smile:

Maybe something along the lines of this systemd service: https://github.com/snapcore/snapd/blob/61b66aaa1beb861f308ea7dd75c2f0c042bf29a3/data/systemd/snapd.seeded.service.in

pinging @ogra @ijohnson @mvo @cjp256 – and thanks in advance for all helpful comments!

we have now added support for snapctl system-mode which will show you whether the device is seeded:

$ snapctl system-mode
system-mode: run
seed-loaded: true

well we do have the the install-device hook for gadgets now that runs in UC20 install mode, not sure if that helps your situation at all or not though

Snaps are seeded in the order they are defined in the model assertion, so if you put the snaps that manipulate the screen last, they will be seeded last and your users will only have to see the blank screen while those last two snaps are seeded. In addition, if you had a snap that used snapd-control, you could have your snaps (if you publish and control them) disable their services on install, and then from your snap that uses snapd-control re-enable and start the services once your snap that uses snapd-control is started during seeding (and this snap would be seeded last as per the model assertion).

Additionally, maybe you could explore having the configuration of mir-kiosk allow setting something with snap set, etc. that displays something when it is first installed before something like wpe-webkit-mir-kiosk is installed and starts running.