Run script on boot

Hey everyone!

We’re running BalenaOS on some of our production devices and we’re trying to figure out how to run a script on boot. Specifically, we want to run curl <some endpoint> each time our device boots to get notified whenever a device comes online.

More generally, we’re also curious whether it’s possible to add additional systemd units. It would be great if we could add some static binaries to /resin-data and have those started and managed by systemd.

Are either of these use cases supported?

Thanks!

Hello,
You could run a shell script at the end of your Dockerfile that includes the commands you want to run on boot, as outlined in this thread: Custom Boot Action/Script - There is also some information there about using systemd as well. Please note that running “on boot” may not be the same as “coming online” so if these suggestions do not achieve what you are looking to do, just let us know.

We added a custom service into our build of the balenaOS image. This might be usefull for you, too.

Caution, building your own balenaOS image, is a little bit more difficult, since it contains working with the yocto project and definitely has a steep learning curve. Make sure that you have enough computational power and free diskspace. Building these images can take multiple tens of GB and run for hours!

We are using the jetson-nano, therefore replace balena-jetson by balena-{platform} that you are using.

The following guide is base on this repository

Create the following folder structure, that will contain all your code:
./layers/meta-balena-jetson/recipes-bsp/my-startup/my-startup/

Place a file containing your service here:
layers/meta-balena-jetson/recipes-bsp/my-startup/my-startup/my-startup.service
Reference on writing a service systemd, building a oneshot service might be suitable for you :slight_smile:

Create a .bb file that tells the build root system what to do here layers/meta-balena-jetson/recipes-bsp/my-startup/my-startup.bb

EXTRAPATHS_prepend := "${THISDIR}/${PN}:"

inherit systemd

DESCRIPTION = "Add your description here"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = " \
    file://my-startup.service \
"

SYSTEMD_SERVICE_${PN} = "my-startup.service"

RDEPENDS_${PN} = " bash systemd"

S = "${WORKDIR}"

do_install () {
    install -d ${D}/${systemd_unitdir}/system
    install -m 0644 ${WORKDIR}/my-startup.service ${D}/${systemd_unitdir}/system/my-startup.service
}

COMPATIBLE_MACHINE="jetson-nano"

Append the build instruction with your service layers/meta-balena-jetson/recipes-core/images/resin-image.inc

IMAGE_INSTALL_append_jetson-nano = " \
    my-startup \
"

As a site note, it might be use full to add curl to the IMAGE_INSTALL_append_jetson since it might not exist in the balenaOS :wink:

I’ve seen the solution of adding a script to entrypoint, but that doesn’t quite seem ideal. Restarting the container would rerun the script.

Building BalenaOS seems fun but it seems like a heavy solution for us. None of us really want to dive into Yocto.

Is there a way to just place a script in /resin-data and then run that? That would really be ideal.

Hi,

By ‘Is there a way to just place a script in /resin-data and then run that? That would really be ideal.’ do you mean from the container into the volume on the host and get the host to run the binary on startup? Unfortunately we don’t support this type of functionality.

Alternatively, if you don’t wish to build your own version of balenaOS, then installing systemd into a container sounds like the best way forward. It’s actually not very difficult to do, here’s an example built for balena which installs both systemd and avahi: https://github.com/balena-io-playground/avahi-zoo-publisher/blob/master/avahi-publisher/Dockerfile.template

The important points to note are that are the masking of irrelevant/potentially problematic services and the setting of the correct STOPSIGNAL. The Dockerfile also copies and enables a unit file to enable its use by systemd. You’ll either need to run the container in privileged mode or note the capabilities, security options and tmpfs mount here: https://github.com/balena-io-playground/avahi-zoo-publisher/blob/master/docker-compose.yml#L6 for a multicontainer project.

Best regards,

Heds

Looking for this behaviour as well. I have a special case, where I need to create a symbol link in the /var/lib folder. I can create this file, but after reboot it’s cleared again.

Is there really no workaround?

Unfortunately there is no way to currently do that from within your container (without abusing the docker socket etc). What is your use case if you don’t mind me asking?

Hi,

We’re trying to achieve a similar behavior (slightly different):

We have an service that records large volumes of data and also generates console output. We have a control service that starts/stops the main service (via the supervisor) when the user requests it. Due to the data volume, we don’t want the service to start recording immediately on reboot or when we update the device to a new release. In order for the console output to be captured in the device logs, we need to run the application as the CMD in its container, but doing that also causes it to run every time the container starts, including reboot/update.

Ideally we’d like the ability to flag the main service to not start automatically, but there doesn’t seem to be a way to do that currently. We looked at using an environment variable as suggested on another thread
(Selectively running containers on a device) to flag a “stop on reboot” condition. The idea would be to have a service that comes up on boot and sets a persistent “stop automatically” environment variable on the main service, and then the have the main service depend on the flag service so it is started second, and have it clear the flag after it skips its first startup. Unfortunately, there doesn’t seem to be a way to set persistent environment variables from within a container without access to the Balena backend/CLI.

Any suggestions?

Thanks,

Adam