Description
The Banner Update Module manages the system’s message of the day (MOTD) displayed when users log in via SSH or console. It uses metadata-driven updates to change the banner text without requiring any file payload, supports rollback functionality, and operates without requiring system reboots.
Specification
| Module name | banner |
| Supports rollback | yes |
| Requires restart | no |
| Artifact generation script | yes |
| Full operating system updater | no |
| Source code | Update Module, Artifact Generator |
| Maintainer | Community |
Prepare the device
This section describes how to setup your target device, i.e. the device to be updated. This will also be referred to as the device environment.
All commands outlined in this section should be run in the device environment.
Prerequisites
This update module has the following prerequisites for the device environment:
- Install the Mender client, version 2.0 or later
- A recent version of the JSON parser
jqneeds to be installed on the device - A POSIX-compatible shell
Install the Update Module
Download the latest version of this Update Module by running:
mkdir -p /usr/share/mender/modules/v3 && wget -P /usr/share/mender/modules/v3 https://raw.githubusercontent.com/mendersoftware/mender-update-modules/master/banner/module/banner
Prepare the development environment on your workstation
This section describes how to set up your development environment on your workstation.
All commands outlined in this section should be run in the development environment.
Prerequisites
This Update Module has the following prerequisites for the development environment:
- Install mender-artifact, version 3.1.0 or later
- A recent version of the JSON parser
jq
Artifact creation
For convenience, an Artifact generator tool banner-artifact-gen is provided along the module. This tool will generate Mender Artifacts in the same format that the Update Module expects them.
Download banner-artifact-gen, by running the following command:
wget https://raw.githubusercontent.com/mendersoftware/mender-update-modules/master/banner/module-artifact-gen/banner-artifact-gen
Make it executable:
chmod +x banner-artifact-gen
Now generate a Mender Artifact using the following command:
ARTIFACT_NAME="my-banner-update-1.0"
DEVICE_TYPE="my-device-type"
OUTPUT_PATH="my-banner-update-1.0.mender"
BANNER_TEXT="Welcome to Production!"
./banner-artifact-gen -n ${ARTIFACT_NAME} -t ${DEVICE_TYPE} -o ${OUTPUT_PATH} -b "${BANNER_TEXT}"
ARTIFACT_NAME- The name of the Mender ArtifactDEVICE_TYPE- The compatible device type of this Mender ArtifactOUTPUT_PATH- The path where to place the output Mender Artifact. This should always have a.mendersuffixBANNER_TEXT- The banner text to display as the message of the day
You can either deploy this Artifact in managed mode with the Mender server (upload it under Releases in the server UI) or by using the Mender client only in Standalone deployments.
Artifact technical details
The Mender Artifact used by this Update Module has no payload files. Instead it uses the Metadata field to store the banner text, which will be written to /etc/motd by the device. This meta-data is composed by a single banner_text JSON key with the text to be displayed.
As an example, the artifact created as described above can be inspected with mender-artifact read my-banner-update-1.0.mender, which yield the following information:
Updates:
- Type: banner
Provides:
rootfs-image.banner.version: my-banner-update-1.0
Depends: {}
Clears Provides: [rootfs-image.banner.*]
Metadata:
{
"banner_text": "Welcome to Production!"
}
Files: []
Full explanation and tutorial
Understanding the Implementation
The banner module demonstrates a metadata-only update pattern. Unlike modules that deploy payload files, the banner text is embedded directly in the artifact metadata. This approach suits small configuration values where a separate payload file would be unnecessary overhead.
How Metadata-Only Artifacts Work
Standard update modules receive payload files in $FILES/files/. The banner module instead reads from $FILES/header/meta-data, a JSON file containing artifact metadata:
{
"banner_text": "Welcome to Production!"
}
The artifact generator creates this structure using mender-artifact write module-image with the --meta-data flag rather than --file:
mender-artifact write module-image \
--type banner \
--artifact-name "$ARTIFACT_NAME" \
--device-type "$DEVICE_TYPE" \
--meta-data "$METADATA_FILE" \
--output-path "$OUTPUT_PATH"
Inspecting a banner artifact with mender-artifact read shows no payload files—only metadata in the header.
Module Internals
The module handles six states. Two are queries that return values to stdout:
NeedsArtifactReboot)
echo "No"
;;
SupportsRollback)
echo "Yes"
;;
Four are actions that perform work:
ArtifactInstall: Backs up the existing /etc/motd, extracts banner_text from metadata using jq, writes to /etc/motd. Fails if no banner text is found.
ArtifactCommit: Removes the backup file, making the change permanent.
ArtifactRollback: Restores the backup to /etc/motd, or removes /etc/motd if it didn’t exist before.
ArtifactFailure: Same as rollback—restores original state on any failure.
The backup location /tmp/banner.backup persists across module invocations within a single update transaction but is cleared on reboot.
JSON Extraction with jq
The module uses jq to extract the banner text safely:
BANNER_TEXT=$(jq -r '.banner_text' "$FILES/header/meta-data" 2>/dev/null || echo "")
The -r flag outputs raw strings without JSON quoting. The fallback to empty string handles missing files gracefully, with explicit validation afterward:
if [ -z "$BANNER_TEXT" ] || [ "$BANNER_TEXT" = "null" ]; then
echo "Error: No banner_text found in metadata"
exit 1
fi
Yocto Integration
For devices built with Yocto/OpenEmbedded, include the module in your image with a recipe.
Module Recipe
Create mender-banner-module.bb:
SUMMARY = "Mender banner update module"
DESCRIPTION = "Update module for deploying MOTD banners via Mender"
HOMEPAGE = "https://github.com/mendersoftware/mender-update-modules"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
RDEPENDS:${PN} = "jq"
SRC_URI = "file://banner"
S = "${WORKDIR}"
do_install() {
install -d ${D}${datadir}/mender/modules/v3
install -m 0755 ${WORKDIR}/banner ${D}${datadir}/mender/modules/v3/banner
}
FILES:${PN} = "${datadir}/mender/modules/v3/banner"
Place the module script in files/banner alongside the recipe.
The RDEPENDS:${PN} = "jq" line ensures jq is installed on the target device—the module will fail without it.
Adding to Your Image
In your image recipe or local.conf:
IMAGE_INSTALL:append = " mender-banner-module"
Fetching from GitHub
Alternatively, fetch the module directly from the repository:
SUMMARY = "Mender banner update module"
DESCRIPTION = "Update module for deploying MOTD banners via Mender"
HOMEPAGE = "https://github.com/mendersoftware/mender-update-modules"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://LICENSE;md5=86d3f3a95c324c9479bd8986968f4327"
RDEPENDS:${PN} = "jq"
SRC_URI = "git://github.com/mendersoftware/mender-update-modules.git;protocol=https;branch=master"
SRCREV = "${AUTOREV}"
S = "${WORKDIR}/git"
do_install() {
install -d ${D}${datadir}/mender/modules/v3
install -m 0755 ${S}/banner/module/banner ${D}${datadir}/mender/modules/v3/banner
}
FILES:${PN} = "${datadir}/mender/modules/v3/banner"
Pin SRCREV to a specific commit for reproducible builds in production.
Testing
Verifying Module Installation
On the device, confirm the module is present and executable:
ls -l /usr/share/mender/modules/v3/banner
Verify jq is available:
which jq
jq --version
Manual Module Testing
Test the module directly without creating artifacts. First, simulate the context directory structure:
mkdir -p /tmp/test-banner/header
echo '{"banner_text": "Test Banner Message"}' > /tmp/test-banner/header/meta-data
Test each state:
# Query rollback support
/usr/share/mender/modules/v3/banner SupportsRollback /tmp/test-banner
# Should output: Yes
# Query reboot requirement
/usr/share/mender/modules/v3/banner NeedsArtifactReboot /tmp/test-banner
# Should output: No
# Install the banner
/usr/share/mender/modules/v3/banner ArtifactInstall /tmp/test-banner
# Verify installation
cat /etc/motd
# Should show: Test Banner Message
# Verify backup exists
cat /tmp/banner.backup
# Test rollback
/usr/share/mender/modules/v3/banner ArtifactRollback /tmp/test-banner
# Verify original content restored
cat /etc/motd
End-to-End Testing
Create and deploy an artifact:
# On your workstation
./banner-artifact-gen \
-n "test-banner-v1" \
-t "your-device-type" \
-o test-banner.mender \
-b "Welcome to the Test Environment"
# Deploy locally on device
mender install test-banner.mender
# Check result
cat /etc/motd
# Commit if satisfied
mender commit
# Or rollback
mender rollback
Monitor the update through system logs:
journalctl -u mender-updated -f
Testing Rollback Scenarios
Verify rollback works in failure scenarios:
# Install first version
./banner-artifact-gen -n "v1" -t "device" -o v1.mender -b "Version 1"
mender install v1.mender
mender commit
# Install second version but don't commit
./banner-artifact-gen -n "v2" -t "device" -o v2.mender -b "Version 2"
mender install v2.mender
# Verify new banner
cat /etc/motd
# Shows: Version 2
# Trigger rollback
mender rollback
# Verify original restored
cat /etc/motd
# Shows: Version 1
Troubleshooting
“No banner_text found in metadata”
The artifact metadata is missing or malformed. Verify the artifact structure:
mender-artifact read your-artifact.mender
Look for the metadata section. If missing, regenerate with the artifact generator.
“jq: command not found”
Install jq on the device:
# Debian/Ubuntu
apt-get install jq
# For Yocto, add to RDEPENDS or IMAGE_INSTALL
Banner not updating
Check file permissions on /etc/motd:
ls -l /etc/motd
The Mender client runs as root, so permission issues are rare but possible with immutable flags or SELinux policies.
Rollback restores empty file
This is expected behavior when /etc/motd didn’t exist before the first deployment. The module creates an empty backup file as a marker for “file did not exist.”
Module not found
Artifact Payload type 'banner' is not supported by this Mender Client
Verify installation path and permissions:
ls -l /usr/share/mender/modules/v3/banner
# Should show: -rwxr-xr-x ... banner
If the file exists but isn’t executable:
chmod +x /usr/share/mender/modules/v3/banner
Adapting the Pattern
The banner module serves as a template for similar metadata-only updates. To adapt it for other single-value configurations:
- Change
BANNER_FILEto your target path - Change the metadata key from
banner_textto your value - Update the artifact generator to accept your parameters
- Adjust backup location if
/tmpisn’t suitable (consider persistence requirements)
For multi-value configurations or file deployments, use the files-based approach with payload in $FILES/files/ instead of metadata.