Skip to main content

Container Development

Creating Example Containers

Example containers live in recipes-containers/pv-examples/. Each container needs:

1. Recipe file (pv-example-foo_1.0.bb)

SUMMARY = "Example Foo Container"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

inherit core-image container-pvrexport

IMAGE_BASENAME = "pv-example-foo"
PVRIMAGE_AUTO_MDEV = "0"

IMAGE_INSTALL += "busybox"

SRC_URI += "file://${PN}.services.json \
file://${PN}.args.json"

PVR_APP_ADD_EXTRA_ARGS += "--config=Entrypoint=/bin/sh"

2. services.json (providers)

Declares the services this container exports. The #spec versioning field is required:

{
"#spec": "service-manifest-xconnect@1",
"services": [
{"name": "my-service", "type": "unix", "socket": "/run/my-service.sock"}
]
}

Note: The parser supports both the new object format and the legacy array format for backwards compatibility.

3. args.json (consumers)

Declares which services this container needs:

{
"PV_SERVICES_REQUIRED": [
{"name": "my-service", "target": "/run/pv/services/my-service.sock"}
]
}

For xconnect service manifest details and supported service types (unix, rest, dbus, drm, wayland), see the pantavisor xconnect reference or pantavisor/docs/overview/xconnect.md.

Placing a container in the app group

Set PVR_APP_ADD_GROUP = "app" in the recipe to inherit the group's default auto_recovery policy. Containers in the app group automatically get restart-on-failure with backoff behaviour unless they provide their own PV_AUTO_RECOVERY in args.json.

Building Containers

./kas-container build kas/build-configs/release/docker-x86_64-scarthgap.yaml:kas/with-workspace.yaml \
--target pv-example-foo

Output: build/tmp-scarthgap/deploy/images/docker-x86_64/pv-example-foo.pvrexport.tgz

Adding a New Example Container (full workflow)

# 1. Create recipe
cat > recipes-containers/pv-examples/pv-example-mytest_1.0.bb << 'EOF'
SUMMARY = "My Test Container"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

inherit core-image container-pvrexport

IMAGE_BASENAME = "pv-example-mytest"
PVRIMAGE_AUTO_MDEV = "0"
IMAGE_INSTALL += "busybox"
SRC_URI += "file://${PN}.args.json"
PVR_APP_ADD_EXTRA_ARGS += "--config=Entrypoint=/bin/sh"
EOF

# 2. Create args.json
cat > recipes-containers/pv-examples/files/pv-example-mytest.args.json << 'EOF'
{
"PV_SERVICES_REQUIRED": [
{"name": "raw", "target": "/run/pv/services/raw.sock"}
]
}
EOF

# 3. Build and deploy for testing
./kas-container build kas/build-configs/release/docker-x86_64-scarthgap.yaml:kas/with-workspace.yaml \
--target pv-example-mytest
cp build/tmp-scarthgap/deploy/images/docker-x86_64/pv-example-mytest.pvrexport.tgz pvtx.d/

Inspecting Pvrexports

Use pvr tools to inspect pvrexports — do not manually extract tarballs:

# Quick inspection — show state JSON
pvr inspect /path/to/container.pvrexport.tgz

# Clone to directory for detailed inspection
pvr clone /path/to/container.pvrexport.tgz /tmp/inspect-dir
cat /tmp/inspect-dir/<container-name>/run.json

Common Issues

Pseudo Path Mismatch Errors

Errors like path mismatch [1 link]: ino XXXXX db '...' req '...' during image builds indicate pseudo database corruption, typically triggered by pvr file operations.

Fix:

kas shell <config.yaml> -c "bitbake -c cleansstate <recipe-name>"
kas build <config.yaml>

The pvroot-image.bbclass includes PSEUDO_IGNORE_PATHS entries to mitigate this for pvr working directories.

Multiconfig TMPDIR Conflicts

When using BBMULTICONFIG, each config must have a separate TMPDIR to avoid conflicts with package feeds, sstate, and deploy directories:

TMPDIR = "${TOPDIR}/tmp-${DISTRO_CODENAME}-${MULTICONFIG_NAME}-${MACHINE}"