# Persistent App

**URL:** https://hub.mender.io/t/persistent-app/8150
**Category:** Update Modules
**Created:** [December 23, 2025, 10:36am UTC](https://hub.mender.io/t/persistent-app/8150 "2025-12-23T10:36:00Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![TheYoctoJester](https://yyz2.discourse-cdn.com/flex036/user_avatar/hub.mender.io/theyoctojester/32/1444_2.png) [@TheYoctoJester](https://hub.mender.io/u/TheYoctoJester)
#### Post date: [December 23, 2025, 10:36am UTC](https://hub.mender.io/t/persistent-app/8150/1 "2025-12-23T10:36:00Z")

</div>

## Description

The Persistent App Update Module installs files to a user-defined directory on the persistent data partition, ensuring deployments survive rootfs A/B updates.

This module uses the `data-partition` software filesystem namespace, which means version tracking in `mender show-provides` persists across system updates. When a rootfs update clears `rootfs-image.*` provides, the `data-partition.*` provides remain intact.

The module supports rollback by maintaining backups on the same persistent partition.

Example use-cases:

- Deploy application files that must persist across OS updates
- Deploy configuration bundles to /data
- Deploy container images, ML models, or other large files to persistent storage

For a detailed tutorial on persistent update modules, visit [Mender Hub](https://hub.mender.io).

### Specification

| | |
| --- | --- |
| Module name | persistent-app |
| Supports rollback | yes |
| Requires restart | no |
| Artifact generation script | yes |
| Full system updater | no |
| Source code | [Update Module](https://github.com/mendersoftware/mender-update-modules/tree/master/persistent-app/module/persistent-app), [Artifact Generator](https://github.com/mendersoftware/mender-update-modules/blob/master/persistent-app/module-artifact-gen/persistent-app-artifact-gen) |

### Install the Update Module

Download the latest version of this Update Module by running:

```bash
mkdir -p /usr/share/mender/modules/v3 && wget -P /usr/share/mender/modules/v3 https://raw.githubusercontent.com/mendersoftware/mender-update-modules/master/persistent-app/module/persistent-app && chmod +x /usr/share/mender/modules/v3/persistent-app

```

### Create artifact

To download `persistent-app-artifact-gen`, run the following:

```bash
wget https://raw.githubusercontent.com/mendersoftware/mender-update-modules/master/persistent-app/module-artifact-gen/persistent-app-artifact-gen && chmod +x persistent-app-artifact-gen

```

Generate Mender Artifacts using the following command:

```bash
ARTIFACT_NAME="myapp-v1.0.0"
DEVICE_TYPE="raspberrypi4"
DEST_DIR="/data/myapp"
SOFTWARE_NAME="myapp"
SOFTWARE_VERSION="1.0.0"
OUTPUT_PATH="myapp-v1.0.0.mender"
./persistent-app-artifact-gen \
    -n ${ARTIFACT_NAME} \
    -t ${DEVICE_TYPE} \
    -d ${DEST_DIR} \
    --software-name ${SOFTWARE_NAME} \
    --software-version ${SOFTWARE_VERSION} \
    -o ${OUTPUT_PATH} \
    ./payload/

```

The artifact generator automatically includes `--software-filesystem data-partition` to ensure version tracking survives rootfs updates.

**Note on directory structure:** The artifact generator flattens all input files into a single directory. If you pass a directory like `./payload/` containing `bin/app` and `etc/config.json`, both files end up directly in the destination directory (e.g., `/data/myapp/app` and `/data/myapp/config.json`), not in subdirectories. This module is intended as a simple example; for complex directory hierarchies, consider using the dir-overlay module or adapting this module to your needs.

After deploying, verify with:

```bash
mender show-provides | grep data-partition
# Output: data-partition.myapp.version=1.0.0

```

### How it works

1. **ArtifactInstall** : The module backs up any existing content at the destination directory to `<dest_dir>.backup`, then copies the new files.

2. **ArtifactCommit** : On successful deployment, the backup is removed.

3. **ArtifactRollback** : If the deployment fails or is rejected, the backup is restored.

4. **Persistence** : Both the deployed files and the backup reside on the data partition (typically `/data`), which is not affected by rootfs A/B updates.

# Full explanation and tutorial

## Understanding the Persistence Mechanism

This section explains the underlying concepts that make persistent deployments work, followed by Yocto integration, verification testing, and guidance for building custom variants.

### The Persistence Problem

Mender uses an A/B partition scheme for rootfs updates:

```auto
┌─────────────────────────────────────────────────────────────┐
│ Boot │
├────────────────────────┬────────────────────────────────────┤
│ rootfs A (active) │ rootfs B (inactive) │
│ /usr, /opt, /etc... │ (next update goes here) │
├────────────────────────┴────────────────────────────────────┤
│ data partition (/data) │
│ Survives all updates │
└─────────────────────────────────────────────────────────────┘

```

When a rootfs update arrives, Mender writes it to the inactive partition and switches boot targets. Everything on the old rootfs becomes unreachable. If an update module installed files to `/opt/myapp/`, those files are gone.

The data partition is different. It’s never overwritten by system updates—that’s where Mender stores its database, logs, and where applications can persist data across updates.

### Software Versioning Namespaces

Mender tracks installed software through “provides” entries in its database. When you deploy an artifact, it records what version was installed. The problem: rootfs updates clear most provides entries to reflect that the entire system has changed.

The key is the `--software-filesystem` flag when creating artifacts:

```bash
# Default behavior - provides get cleared by rootfs updates
mender-artifact write module-image --type myapp ...
# Creates: rootfs-image.myapp.version (CLEARED on rootfs update)

# Persistent behavior - provides survive rootfs updates
mender-artifact write module-image --type persistent-app \
    --software-filesystem data-partition ...
# Creates: data-partition.myapp.version (SURVIVES rootfs update)

```

Rootfs updates only clear provides matching `rootfs-image.*`. By using `data-partition` as the filesystem namespace, version tracking survives system updates.

The artifact generator automatically includes this flag, so you get the correct behavior without remembering to add it manually.

## Installing via Yocto

For embedded Linux builds, create a recipe to install the module. In your layer:

`recipes-mender/mender-persistent-app-module/mender-persistent-app-module.bb`:

```bitbake
SUMMARY = "Mender persistent-app update module"
DESCRIPTION = "Update module for deploying files to the persistent data partition"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"

SRC_URI = "https://raw.githubusercontent.com/mendersoftware/mender-update-modules/master/persistent-app/module/persistent-app"
SRC_URI[sha256sum] = "${@bb.fetch2.get_checksum('sha256', d)}"

S = "${WORKDIR}"

do_install() {
    install -d ${D}${datadir}/mender/modules/v3
    install -m 0755 ${WORKDIR}/persistent-app ${D}${datadir}/mender/modules/v3/persistent-app
}

FILES:${PN} = "${datadir}/mender/modules/v3/persistent-app"

```

Add to your image:

```bitbake
IMAGE_INSTALL:append = " mender-persistent-app-module"

```

Alternatively, for quick testing during development, fetch the module directly:

```bitbake
# In local.conf
do_fetch_persistent_app() {
    install -d ${IMAGE_ROOTFS}${datadir}/mender/modules/v3
    wget -O ${IMAGE_ROOTFS}${datadir}/mender/modules/v3/persistent-app \
        https://raw.githubusercontent.com/mendersoftware/mender-update-modules/master/persistent-app/module/persistent-app
    chmod 0755 ${IMAGE_ROOTFS}${datadir}/mender/modules/v3/persistent-app
}
ROOTFS_POSTPROCESS_COMMAND += "do_fetch_persistent_app;"

```

## Verifying Persistence Across Rootfs Updates

This test proves that deployed applications survive a rootfs update.

### Step 1: Deploy the application

Create and deploy a test artifact:

```bash
mkdir -p payload
echo "version=1.0.0" > payload/version.txt
echo "My persistent application" > payload/readme.txt

./persistent-app-artifact-gen \
    -n myapp-v1.0.0 \
    -t your-device-type \
    -d /data/myapp \
    --software-name myapp \
    --software-version 1.0.0 \
    -o myapp-v1.0.0.mender \
    payload/

```

Transfer and install on the device:

```bash
mender install myapp-v1.0.0.mender
mender commit

```

### Step 2: Record the current state

```bash
# Note the current rootfs version
mender show-provides | grep rootfs-image.version

# Verify app is installed
cat /data/myapp/version.txt

# Check provides include the data-partition namespace
mender show-provides | grep data-partition
# Output: data-partition.myapp.version=1.0.0

```

### Step 3: Deploy a rootfs update

Build or obtain a rootfs update artifact and deploy it:

```bash
mender install core-image-full-cmdline-yourdevice.mender
# Device reboots...
mender commit

```

### Step 4: Verify persistence

After reboot:

```bash
# Application files should still exist
cat /data/myapp/version.txt
# Output: version=1.0.0

# Provides should be preserved
mender show-provides

```

The output should show:

```auto
data-partition.myapp.version=1.0.0
rootfs-image.version=<new-rootfs-version>

```

The `rootfs-image.version` changed (new OS version), but `data-partition.myapp.version` survived. The rootfs update cleared `rootfs-image.*` provides but left `data-partition.*` intact.

## Rollback Testing

Verify rollback works correctly:

```bash
# Create and install v2
./persistent-app-artifact-gen \
    -n myapp-v2.0.0 \
    -t your-device-type \
    -d /data/myapp \
    --software-name myapp \
    --software-version 2.0.0 \
    -o myapp-v2.0.0.mender \
    payload-v2/

mender install myapp-v2.0.0.mender

# Verify new version before committing
cat /data/myapp/version.txt
# Output: version=2.0.0

# Trigger rollback
mender rollback

# Verify original version restored
cat /data/myapp/version.txt
# Output: version=1.0.0

```

## Building Your Own Variant

For specialized use cases, build a custom variant using `persistent-app` as the starting point.

**Post-install actions** — restart services, reload configuration, or run migration scripts:

```bash
ArtifactInstall)
    # ... existing install logic ...

    # Custom: restart the application
    systemctl restart myapp.service
    ;;

```

**Validation before commit** — verify the deployment works before making it permanent:

```bash
ArtifactCommit)
    # Custom: verify the app is healthy
    if ! curl -s http://localhost:8080/health | grep -q "ok"; then
        log "Health check failed"
        exit 1
    fi
    # ... existing commit logic ...
    ;;

```

**Different backup strategies** — for large deployments, consider hard links or rsync instead of full copies.

**Nested directory structures** — the default artifact generator flattens files. For complex directory trees, modify the generator to use tar (see `dir-overlay` module for this pattern).

When building a custom variant:

1. Copy the module as your starting point
2. Rename it to reflect your use case (e.g., `myapp-deploy`)
3. Modify the artifact generator to match
4. Keep the `--software-filesystem data-partition` flag to maintain persistence

## Troubleshooting

### Module not found after rootfs update

The update module lives on the rootfs at `/usr/share/mender/modules/v3/persistent-app`. If a rootfs update doesn’t include the module, it won’t be present even though deployed files remain on `/data`.

Solution: Include the module in all images that need to deploy or manage persistent applications.

### Provides cleared unexpectedly

If `mender show-provides` doesn’t show `data-partition.*` entries after a rootfs update:

1. Verify the artifact was created with `--software-filesystem data-partition`
2. Check with `mender-artifact read your.mender` — the Provides section should show `data-partition.` prefix
3. The artifact generator includes this automatically; if using manual commands, ensure the flag is present

### Permission denied on /data

The data partition must be mounted read-write:

```bash
mount | grep data

```

Standard Mender partition layouts mount `/data` as writable. If permissions are wrong, check fstab or systemd mount units.

### Files missing after rootfs update

If files are missing from the destination directory:

1. Verify the destination starts with `/data/`
2. Check that `/data` is the persistent partition, not a tmpfs
3. Ensure the backup wasn’t restored due to a failed commit

### mender show-provides shows nothing

The Mender database at `/var/lib/mender/` should be a symlink to `/data/mender/`:

```bash
ls -la /var/lib/mender

```

If this symlink is broken or points to rootfs storage, provides won’t survive reboots.

## Extended Use Cases

The module handles these scenarios directly:

- **Configuration bundles** : Configs that persist across OS updates but remain deployable and rollbackable
- **Static assets** : Web assets, firmware blobs, or files independent of the OS
- **Version markers** : Version files for applications that self-update but need Mender tracking

For complex scenarios, extend the pattern:

- **Docker containers** : Run `docker load` after copying image tarballs, or `docker-compose up` after deploying compose files
- **Machine learning models** : Add validation that loads the model and runs test inference before committing
- **Database migrations** : Include migration scripts in `ArtifactInstall` with rollback support

The key principles:

1. Deploy to `/data/` for persistence
2. Keep backups on `/data/` for rollback
3. Use `--software-filesystem data-partition` for correct provides namespace
4. Include the module in all rootfs images that need it
