Board Firmware Packaging
PX4 supports building distributable firmware packages for Linux-based (POSIX) boards. While NuttX boards produce .px4 firmware files that are flashed via QGroundControl, POSIX boards can produce .deb (Debian) packages that are installed using standard Linux package management tools (dpkg, apt).
This page covers how manufacturers can add .deb packaging to their boards, with examples for both single-processor and multi-processor architectures.
Overview
The packaging framework uses CMake CPack with the DEB generator. It is built on two extension points in the PX4 build system:
boards/<vendor>/<board>/cmake/package.cmake: CPack variable overrides (package name, version, dependencies, architecture, maintainer info). Loaded during CMake configure.boards/<vendor>/<board>/cmake/install.cmake:install()rules that define what goes into the package (binaries, scripts, config files, service files). Loaded fromplatforms/posix/CMakeLists.txtwhere build targets are available.
When a board provides these files, CI automatically discovers and builds the _deb target alongside the normal firmware build.
Build Command
For any board with packaging support:
sh
make <vendor>_<board>_debFor example:
sh
make modalai_voxl2_debThis builds the _default configuration (and any companion builds for multi-processor boards), then runs cpack -G DEB in the build directory. The resulting .deb file is placed in build/<vendor>_<board>_default/.
Adding Packaging to a Board
File Structure
boards/<vendor>/<board>/
cmake/
package.cmake # CPack configuration (required)
install.cmake # Install rules (required)
debian/
postinst # Post-install script (optional)
prerm # Pre-remove script (optional)
<name>.service # Systemd unit file (optional)Step 1: CPack Configuration (package.cmake)
This file sets CPack variables that control the .deb metadata. It is included from cmake/package.cmake after the base CPack defaults are configured.
cmake
# boards/<vendor>/<board>/cmake/package.cmake
# Derive Debian-compatible version from git tag
# v1.17.0-alpha1-42-gabcdef -> 1.17.0~alpha1.42.gabcdef
# v1.17.0 -> 1.17.0
string(REGEX REPLACE "^v" "" _deb_ver "${PX4_GIT_TAG}")
string(REGEX REPLACE "-" "~" _deb_ver "${_deb_ver}")
string(REGEX REPLACE "~([0-9]+)~" ".\\1." _deb_ver "${_deb_ver}")
# Target architecture (use the target arch, not the build host)
set(CPACK_DEBIAN_ARCHITECTURE "arm64")
# Package identity
set(CPACK_DEBIAN_PACKAGE_NAME "my-px4-board")
set(CPACK_DEBIAN_FILE_NAME "my-px4-board_${_deb_ver}_arm64.deb")
# Install prefix
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr")
set(CPACK_INSTALL_PREFIX "/usr")
set(CPACK_SET_DESTDIR true)
# Package metadata
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "PX4 Autopilot for My Board")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Vendor <support@vendor.com>")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "some-dependency (>= 1.0)")
set(CPACK_DEBIAN_PACKAGE_CONFLICTS "")
set(CPACK_DEBIAN_PACKAGE_REPLACES "")
# Disable shlibdeps for cross-compiled boards
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF)
# Include post-install and pre-remove scripts (optional)
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
"${PX4_BOARD_DIR}/debian/postinst;${PX4_BOARD_DIR}/debian/prerm")Key variables:
| Variable | Purpose |
|---|---|
CPACK_DEBIAN_ARCHITECTURE | Target architecture. Set explicitly for cross-compiled boards since dpkg --print-architecture reports the build host, not the target. |
CPACK_DEBIAN_PACKAGE_NAME | Package name as it appears in dpkg -l. |
CPACK_DEBIAN_FILE_NAME | Output .deb filename. |
CPACK_DEBIAN_PACKAGE_DEPENDS | Runtime dependencies (comma-separated, Debian format). |
CPACK_DEBIAN_PACKAGE_SHLIBDEPS | Set to OFF for cross-compiled boards where dpkg-shlibdeps cannot inspect target binaries. |
Step 2: Install Rules (install.cmake)
This file defines what files are packaged in the .deb. It is included from platforms/posix/CMakeLists.txt where the px4 build target is available.
All paths are relative to CPACK_PACKAGING_INSTALL_PREFIX (typically /usr). Use ../ to install outside the prefix (e.g., ../etc/ installs to /etc/).
Minimal example (single-processor board):
cmake
# boards/<vendor>/<board>/cmake/install.cmake
# PX4 binary
install(TARGETS px4 RUNTIME DESTINATION bin)
# Module alias script (generated during build)
install(PROGRAMS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/px4-alias.sh DESTINATION bin)
# Startup scripts
install(PROGRAMS
${PX4_BOARD_DIR}/target/my-px4-start
DESTINATION bin
)
# Configuration files
install(FILES
${PX4_BOARD_DIR}/target/my-config.conf
DESTINATION ../etc/my-board
)
# Systemd service
install(FILES ${PX4_BOARD_DIR}/debian/my-px4.service
DESTINATION ../etc/systemd/system
)
# Component metadata
install(FILES
${PX4_BINARY_DIR}/actuators.json.xz
${PX4_BINARY_DIR}/parameters.json.xz
DESTINATION ../data/px4/etc/extras
OPTIONAL
)
install(FILES ${PX4_BINARY_DIR}/events/all_events.json.xz
DESTINATION ../data/px4/etc/extras
OPTIONAL
)Step 3: Debian Scripts (optional)
postinst
Runs after the package is installed. Common tasks:
- Create
px4-*module symlinks frompx4-alias.sh - Set up required directories with correct ownership
- Run
systemctl daemon-reloadto pick up the service file - Board-specific setup (e.g., DSP signature generation)
bash
#!/bin/bash
set -e
# Create px4-* symlinks
if [ -f /usr/bin/px4-alias.sh ]; then
grep "^alias " /usr/bin/px4-alias.sh | \
sed "s/alias \(px4-[a-zA-Z0-9_]*\)=.*/\1/" | while read cmd; do
ln -sf px4 "/usr/bin/${cmd}"
done
fi
# Create data directories
mkdir -p /data/px4/param
mkdir -p /data/px4/etc/extras
# Reload systemd
if command -v systemctl > /dev/null 2>&1; then
systemctl daemon-reload
fiprerm
Runs before the package is removed:
bash
#!/bin/bash
set -e
# Stop the service
if command -v systemctl > /dev/null 2>&1; then
systemctl stop my-px4 2>/dev/null || true
fi
# Remove px4-* symlinks
for f in /usr/bin/px4-*; do
if [ -L "$f" ] && [ "$(readlink "$f")" = "px4" ]; then
rm -f "$f"
fi
doneBoth scripts must be executable (chmod +x).
Multi-Processor Boards
Some boards run PX4 across multiple processors, for example the ModalAI VOXL2 which has a POSIX apps processor (ARM) and a Hexagon DSP (SLPI). These produce two separate CMake builds, but the .deb must contain artifacts from both.
How It Works
- The
_defaultbuild (POSIX/apps processor) owns the.deb. - The Makefile
%_debtarget builds_default, which chains any companion builds as CMake prerequisites. - The
install.cmakepulls companion build artifacts via absolute path to the sibling build directory. - CPack runs in the
_defaultbuild tree and produces a single.deb.
Companion Build Artifacts
In install.cmake, reference the companion build output by absolute path:
cmake
# DSP firmware blob from companion SLPI build
set(SLPI_BUILD_DIR "${PX4_SOURCE_DIR}/build/<vendor>_<board>-slpi_default")
install(FILES ${SLPI_BUILD_DIR}/platforms/qurt/libpx4.so
DESTINATION lib/rfsa/adsp
OPTIONAL
)The OPTIONAL keyword allows the .deb to build even when the companion build hasn't run (useful for development/testing of just the apps-processor side).
VOXL2 Reference
The VOXL2 board is a complete working example of multi-processor packaging:
boards/modalai/voxl2/
cmake/
package.cmake # CPack config: voxl-px4, arm64, deps, shlibdeps off
install.cmake # px4 binary, SLPI libpx4.so, scripts, configs, metadata
debian/
postinst # Symlinks, DSP signature, directory setup
prerm # Stop service, remove symlinks
voxl-px4.service # Systemd unit (after sscrpcd, restart on-failure)
target/
voxl-px4 # Main startup wrapper
voxl-px4-start # PX4 module startup scriptThe resulting .deb installs:
| Path | Contents |
|---|---|
/usr/bin/px4 | Apps processor PX4 binary |
/usr/bin/px4-alias.sh | Module alias script |
/usr/bin/voxl-px4 | Startup wrapper |
/usr/bin/voxl-px4-start | Module startup script |
/usr/lib/rfsa/adsp/libpx4.so | DSP firmware (from SLPI build) |
/etc/modalai/*.config | Board configuration files |
/etc/systemd/system/voxl-px4.service | Systemd service |
/data/px4/etc/extras/*.json.xz | Component metadata |
CI Integration
Automatic Discovery
The CI system (Tools/ci/generate_board_targets_json.py) automatically discovers boards with cmake/package.cmake and adds a <vendor>_<board>_deb target to the board's existing CI group. No manual CI configuration is needed.
Artifact Collection
The Tools/ci/package_build_artifacts.sh script collects .deb files alongside .px4 and .elf artifacts. On tagged releases, .deb files are uploaded to both S3 and GitHub Releases.
Version Format
The .deb version is derived from PX4_GIT_TAG using Debian-compatible formatting:
| Git Tag | Debian Version | Notes |
|---|---|---|
v1.17.0 | 1.17.0 | Stable release |
v1.17.0-beta1 | 1.17.0~beta1 | Pre-release (~ sorts before release) |
v1.17.0-alpha1-42-gabcdef | 1.17.0~alpha1.42.gabcdef | Development build |
The ~ prefix in Debian versioning ensures pre-releases sort lower than the final release: 1.17.0~beta1 < 1.17.0.
Checklist for New Boards
- Create
boards/<vendor>/<board>/cmake/package.cmakewith CPack variables - Create
boards/<vendor>/<board>/cmake/install.cmakewith install rules - (Optional) Create
boards/<vendor>/<board>/debian/postinstandprerm - (Optional) Create
boards/<vendor>/<board>/debian/<name>.service - Test locally:
make <vendor>_<board>_deb - Verify:
dpkg-deb --info build/<vendor>_<board>_default/<name>_*.deb - Verify:
dpkg-deb --contents build/<vendor>_<board>_default/<name>_*.deb - CI picks it up automatically on the next push