How to create custom folders and run an executable on boot in a Yocto-built image (Verdin iMX8M Plus)

Hello,

I’m working on customizing a Yocto-based Linux image for the Toradex Verdin iMX8M Plus board.

What I want to achieve:
Include a prebuilt executable (PIC) in the image

Ensure the binary runs automatically at boot

Automatically create a custom directory, for example: /root/store

My questions:
What is the proper way in Yocto to include custom files like a compiled binary into the image?

How can I create a specific directory (e.g., /root/store) during image build so that it’s present in the final root filesystem?

What is the best practice to set up a systemd service so that the executable starts on boot?

Where should I install the binary and service file in the recipe?

Are there recommended examples or references that show how to package and auto-start an application in Yocto for Toradex or similar boards?

Additional info:
Board: Verdin iMX8M Plus

Yocto Version: Toradex BSP 6.x

The executable is Qt5-based and was cross-compiled using the appropriate toolchain.

I’m currently writing a .bb recipe and a .service file, but I’d prefer to follow the correct and standard method to avoid packaging errors or missing runtime dependencies.
Any help, guidance, or references would be much appreciated!

Thank you.

Hi, I’m the Toradex AI Assistant.

I might make mistakes. Our support team will step in if needed. If I answered your question, please tick the “Solution” checkbox. For follow-up questions to me, please use @ToradexAI in your post.

Did you like my answer? :+1: :-1:


Hello! Thank you for your detailed questions. Here are step-by-step answers and best practices for packaging and auto-starting your Qt5 application on your Verdin iMX8M Plus using Yocto with Toradex BSP 6.x.


1. Including a Prebuilt Executable in the Image

  • Create a Custom Layer and Recipe
    Start by creating a custom meta-layer if you have not already. Add your binary (your-qt5-app) to the files directory inside your recipe.

    Example recipe (your-qt5-app_1.0.bb):

    SUMMARY = "Your Qt5 Application"
    LICENSE = "MIT"
    LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
    
    SRC_URI = "file://your-qt5-app \
               file://your-qt5-app.service"
    S = "${WORKDIR}"
    
    # Install binary to /usr/bin and create /root/store directory
    do_install() {
        install -d ${D}${bindir}
        install -m 0755 ${S}/your-qt5-app ${D}${bindir}
        install -d ${D}/root/store
        # Install systemd service
        install -d ${D}${systemd_unitdir}/system
        install -m 0644 ${WORKDIR}/your-qt5-app.service ${D}${systemd_unitdir}/system
    }
    
    inherit systemd
    
    NATIVE_SYSTEMD_SUPPORT = "1"
    SYSTEMD_PACKAGES = "${PN}"
    SYSTEMD_SERVICE_${PN} = "your-qt5-app.service"
    
    # Qt5 dependencies (adjust modules as needed)
    DEPENDS = "qtdeclarative qtgraphicaleffects"
    RDEPENDS_${PN} = "qtdeclarative-qmlplugins qtgraphicaleffects-qmlplugins"
    
  • References:
    Add custom binaries with Yocto


2. Creating a Custom Directory (e.g., /root/store)

  • The do_install() function in your recipe should use:
    install -d ${D}/root/store
    
    This ensures /root/store exists in the final root filesystem.

3. Systemd Service for Auto-Start at Boot

  • Create your-qt5-app.service alongside your binary in the recipe’s files directory, for example:

    [Unit]
    Description=Your Qt5 application service
    After=multi-user.target
    
    [Service]
    Type=simple
    ExecStart=/usr/bin/your-qt5-app
    
    [Install]
    WantedBy=multi-user.target
    
  • Let your recipe inherit systemd and install the service as described above.

  • References:
    How to autorun application at the start-up in Linux


4. Where to Install the Binary and Service File

  • Binaries: /usr/bin (use ${bindir} in recipe)
  • Service files: ${systemd_unitdir}/system (typically /lib/systemd/system or /usr/lib/systemd/system depending on your distro)

Your do_install() in the recipe (see point 1) assures this layout.


5. References & Examples


Best Practices for Qt5 Applications

  • Ensure Qt5 libraries (and any needed plugins) are present at runtime. Add the required Qt5 modules to DEPENDS and RDEPENDS_${PN} as shown.
  • Always test the resulting image in QEMU or on hardware to verify that your binary runs and the directory exists.
  • Add your new package to the image with IMAGE_INSTALL:append = " your-qt5-app" in conf/local.conf or in a custom image recipe.

If you follow these steps, your prebuilt Qt5 executable will be properly packaged, the /root/store directory will be present, and the application will auto-start at boot using systemd.

Let me know if you’d like a more detailed example for any of the files!

Best regards,
Toradex AI Assistant.

What I Did

My goal is to add a custom executable (pic) that auto-starts on boot. I created the following recipe and file structure in my custom meta-layer:

1. Directory Structure:

meta-my-layer/
└── recipes-kernel/
    └── pic/
        ├── files/
        │   ├── pic           (My pre-compiled executable)
        │   └── pic.service   (My systemd service file)
        └── pic_1.0.bb        (My recipe file)`

2. Recipe Content (pic_1.0.bb):

BashSUMMARY = "PIC Qt Executable and Service"
LICENSE = "CLOSED"
LIC_FILES_CHKSUM = ""

SRC_URI = "file://pic file://pic.service"

S = "${WORKDIR}"

inherit systemd
SYSTEMD_SERVICE:${PN} = "pic.service"
FILES:${PN} += "${bindir}/pic"

do_install() {
    install -m 0755 ${WORKDIR}/pic ${D}${bindir}/
    install -m 0644 ${WORKDIR}/pic.service ${D}${systemd_system_unitdir}/
    install -d ${D}/root/store
}

The Problem

Before adding this recipe, the build seemed to work fine. After adding it and running bitbake, the build fails while trying to fetch an official Toradex recipe (backports), which is completely unrelated to my changes.

Error Log Snippet:

ERROR: ExpansionError during parsing ... backports_5.4.bb
...
fatal: unable to access 'https://git.toradex.com/backports-toradex.git/': Failed to connect to git.toradex.com port 443: Connection refused`

My Question

How can my recipe, which only uses local file:// sources, cause a network error in a different recipe? Is it possible that my change is triggering this, or is this just a coincidental network/firewall issue on my side?

Thank you for your help.