Custom Boot Action/Script

Hey jcc273,

I took some time crafting a reply that provides what I think is the bare bones knowledge on Docker needed for you to confidently implement a solution to this need:

1. Dockerfile

If you take a look at the repository of your project, assuming you cloned it from one of our getting-started repo’s [1], you’ll see a file named:

Dockerfile.template.

A Dockerfile [2] is a file used by the container engine to build the OS image which will run. A Dockerfile can copy files in from the build environment (in this case, your project folder), can configure the machine in various ways, install packages, etc…

2. The boot process inside the container

One of the directives a Dockerfile can contain to define the image is the “CMD” directive [3]. This directive tells the container how it should boot. At boot, a linux system wants to run a command which will have PID 1 and spawn other processes. In a full OS, this is called the “init system” [4].

In a docker container, a regular process, like a bash script, or a binary of some other kind (node, apache, etc., etc.) functions as the root of the linux process tree with PID 1. All other process that will run in the container must be spawned by that process.

You probably currently have a “CMD” directive in your Dockerfile. Let’s say your Dockerfile has CMD ["node", "server.js"] (replace mentally with whatever you have). The only thing to do is to change it so that it points to a script: CMD ["entry.sh"], and make sure that “entry.sh” runs the commands you need before running “node server.js”. You would need to make sure that entry.sh is added to the working directory on the image.

So, if your project had, at the top level, the two files “entry.sh” and “Dockerfile”, your Dockerfile would end like this:

# add entry.sh to workdir
ADD entry.sh .
# run entry.sh
CMD ["entry.sh"]

and entry.sh would be something like:

#!/bin/bash

iw phy0 interface add wlan1 type managed
node index.js

(I removed the pipe of stderr to dev/null, since if this command fails, it’ll be nice to be able to see that in the logs).

4. Systemd is awesome

There is a more robust method to specify system boot-time processes, in case things get a little too complicated and entry.sh isn’t cutting it. You can to specify “systemd unit files”. This requires your machine to be running the systemd init system. Our debian- and fedora-based images can be configured to run systemd init by adding

ENV INITSYSTEM on

to your Dockerfile.

You would create a file “setup-wlan1.service” with the following contents:

[Unit]
Description=add wlan1 to phy0
After=network.target

[Service]
Type=forking
ExecStart=iw phy0 interface add wlan1 type managed
TimeoutSec=0
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

You would add this service file to your image and enable the service, in your Dockerfile:

ADD setup-wlan1.service /etc/systemd/system
RUN systemctl enable setup-wlan1
ENV INITSYSTEM on

I hope this has been a helpful guide!

– dt-rush


[1] A “getting started” example project - https://github.com/balena-io-projects/simple-server-node
[2] Docker documentation, “Dockerfile reference” - https://docs.docker.com/engine/reference/builder/
[3] Docker documentation, “Dockerfile reference :: CMD” - https://docs.docker.com/engine/reference/builder/#cmd
[4] Wikipedia - “Init” https://en.wikipedia.org/wiki/Init
[5] Balena documentation, “Balena base images :: Init system” - https://www.balena.io/docs/reference/base-images/base-images/#init-system