This is an old revision of the document!
Table of Contents
Persistent Volumes and Content Trees
Volume instances are the storage layer for edge applications in ZEDEDA Cloud. There are two types, both created as zedcloud_volume_instance resources in Terraform but serving very different purposes.
- Persistent Volume (BLOCKSTORAGE) – blank block storage allocated on the edge node. Read-write. Data survives app restarts, app deletion, and node reboots. Used for stateful application data.
- Content Tree (CONTENT_TREE) – a volume pre-populated from an existing image. Read-only by default. Used to deliver an OS image, dataset, or binary to the node before or alongside an app.
Both types are node-scoped objects – they are created on a specific edge node or cluster, not in the project at large. If you want the same volume on five nodes, you create five volume instances.
Volume Instance Types
| Type value | Purpose | Access Mode | Image required | Typical use |
|---|---|---|---|---|
VOLUME_INSTANCE_TYPE_BLOCKSTORAGE | Blank block storage | READWRITE | No | App data, databases, persistent state, Node-RED flows |
VOLUME_INSTANCE_TYPE_CONTENT_TREE | Image-backed read-only volume | READONLY (default) | Yes | Pre-stage a VM image, distribute a dataset, deliver a base OS |
Key Fields
| Field | Description |
|---|---|
type | VOLUME_INSTANCE_TYPE_BLOCKSTORAGE or VOLUME_INSTANCE_TYPE_CONTENT_TREE |
accessmode | VOLUME_INSTANCE_ACCESS_MODE_READWRITE or VOLUME_INSTANCE_ACCESS_MODE_READONLY |
size_bytes | Size of the volume in bytes. For BLOCKSTORAGE: the allocated size. For CONTENT_TREE: set to 0 (size comes from the image). |
image | Image name to back the volume. Required for CONTENT_TREE, not used for BLOCKSTORAGE. |
cleartext | false = volume is encrypted at rest using EVE-OS full-disk encryption. true = unencrypted. Always use false in production. |
label | Optional label for the volume. Used when referencing it in app bundle drive definitions. |
device_id | Assign to a specific standalone edge node (mutually exclusive with edge_node_cluster). |
edge_node_cluster.id | Assign to an EVE-k cluster (mutually exclusive with device_id). |
Persistent Volume vs Standard Volume
This is an important distinction in ZEDEDA:
| Standard Volume | Persistent Volume | |
| Owned by | The app instance | The edge node |
| Lifecycle | Deleted when the app instance is deleted | Survives app deletion |
| Purge & Update | Available | Not available (Purge is unchecked) |
| Shared across apps | No | Yes – multiple apps on the same node can attach it |
| Created via | App bundle drive definition | zedcloud_volume_instance resource (separately) |
A standard volume is created implicitly when you deploy an app and define a drive in the app bundle. A persistent volume is created explicitly as its own resource, then referenced by the app.
Terraform Examples
Persistent Block Volume -- EVE-k Cluster
1 GB blank block volume on an EVE-k cluster, encrypted at rest, read-write. Typical use: Node-RED flow storage, database files, application state.
resource "zedcloud_volume_instance" "tf_demo_vol1_persist" {
edge_node_cluster {
id = zedcloud_edgenode_cluster.demo_edgenode_cluster_1.id
}
accessmode = "VOLUME_INSTANCE_ACCESS_MODE_READWRITE"
cleartext = false
label = "nodered-vol1-persist"
name = "nodered-vol1-persist"
size_bytes = 1073741824
title = "nodered-vol1-persist"
type = "VOLUME_INSTANCE_TYPE_BLOCKSTORAGE"
}
Notes:
size_bytes = 1073741824is exactly 1 GB (1024 * 1024 * 1024)cleartext = falseenables EVE-OS encryption at rest – the volume key is sealed to the TPM via measured boot attestationedge_node_clustertargets the cluster instead of a specific node. ZEDEDA creates the volume on each node in the cluster.labelis used when referencing this volume in an app bundledrivesblock
Content Tree Volume -- EVE-k Cluster
A read-only volume backed by the Ubuntu 24.04 image, used to pre-stage the OS on the cluster before deploying VMs. This avoids download delays when app instances are created.
resource "zedcloud_volume_instance" "tf_demo_vol1_ub_ct" {
edge_node_cluster {
id = zedcloud_edgenode_cluster.demo_edgenode_cluster_1.id
}
accessmode = "VOLUME_INSTANCE_ACCESS_MODE_READONLY"
cleartext = false
image = zedcloud_image.demo_atl_ub_image_1.name
label = ""
name = "UBUNTU-24-CT"
size_bytes = 0
title = "UBUNTU-24-CT"
type = "VOLUME_INSTANCE_TYPE_CONTENT_TREE"
}
Notes:
imagereferences the image name (not ID) of a registered app image – in this case the Ubuntu 24.04 QCOW2 registered earliersize_bytes = 0– for content trees the size is determined by the image itself, not declared hereaccessmode = READONLYis standard for content trees; the image data is not modified at runtimelabel = “”– no label needed since the image name is the identifier- EVE-OS downloads the image from the datastore when this volume instance is created, caching it locally. Subsequent VMs using this image on the same node do not need to re-download it.
Persistent Block Volume -- Standalone Edge Node
The same 1 GB block volume but targeting a specific standalone node instead of a cluster. Use device_id instead of edge_node_cluster:
resource "zedcloud_volume_instance" "tf_demo_vol1_persist_nocl" {
device_id = zedcloud_edgenode.demo_en_advantech_1.id
accessmode = "VOLUME_INSTANCE_ACCESS_MODE_READWRITE"
cleartext = false
label = "nodered-vol1-persist-nocl"
name = "nodered-vol1-persist-nocl"
size_bytes = 1073741824
title = "nodered-vol1-persist"
type = "VOLUME_INSTANCE_TYPE_BLOCKSTORAGE"
}
The only structural difference is device_id vs edge_node_cluster.id. Everything else is identical. device_id and edge_node_cluster are mutually exclusive – you cannot specify both.
Size Reference
| Size | Bytes |
|---|---|
| 512 MB | 536870912 |
| 1 GB | 1073741824 |
| 5 GB | 5368709120 |
| 10 GB | 10737418240 |
| 20 GB | 21474836480 |
| 50 GB | 53687091200 |
ZEDUI Walkthrough
Volume instances can be created standalone in ZEDUI or as part of the app deployment wizard.
Standalone (recommended for persistent volumes)
- Navigate to Library > Volume Instances
- Click +
- Select the target Edge Node or Edge Node Cluster
- Select Type: Block Storage or Content Tree
- For Content Tree: select the Image
- Set Size (for Block Storage) or leave at 0 (Content Tree)
- Set Access Mode: Read-Write or Read-Only
- Set Encryption: on (recommended)
- Click Add
As part of App Deployment
When creating an App Bundle under Edge Apps, the Drives section lets you define volumes inline. These become standard (non-persistent) volumes tied to the app instance lifecycle unless you uncheck Purge.
To make a drive persistent: uncheck Purge in the Drives section. Note that Purge & Update will then be unavailable for that drive.
ZCLI
# Persistent block volume on a standalone node zcli volume-instance create nodered-vol1-persist-nocl \ --volume-type=BLOCKSTORAGE \ --project=demo-project \ --edge-node=demo-en-advantech-1 \ --size=1073741824 \ --access-mode=READWRITE \ --title="nodered-vol1-persist"
# Content tree on a cluster zcli volume-instance create UBUNTU-24-CT \ --volume-type=CONTENT_TREE \ --project=demo-project \ --edge-node-cluster=demo-edgenode-cluster-1 \ --image=TF-ATL-UBUNTU-24-IMAGE \ --size=0 \ --access-mode=READONLY \ --title="UBUNTU-24-CT"
# Show volume instances on a specific node zcli volume-instance show \ --project=demo-project \ --edge-node=demo-en-advantech-1
API
# Persistent block volume
POST /v1/volumes
{
"name": "nodered-vol1-persist-nocl",
"type": "VOLUME_INSTANCE_TYPE_BLOCKSTORAGE",
"accessMode": "VOLUME_INSTANCE_ACCESS_MODE_READWRITE",
"sizeBytes": 1073741824,
"clearText": false,
"deviceId": "<device_id>",
"projectId": "<project_id>"
}
# Content tree
POST /v1/volumes
{
"name": "UBUNTU-24-CT",
"type": "VOLUME_INSTANCE_TYPE_CONTENT_TREE",
"accessMode": "VOLUME_INSTANCE_ACCESS_MODE_READONLY",
"sizeBytes": 0,
"clearText": false,
"image": "TF-ATL-UBUNTU-24-IMAGE",
"deviceId": "<cluster_id>",
"projectId": "<project_id>"
}
What Happens on the Edge Node
Block Storage (BLOCKSTORAGE)
- EVE-OS receives the volume instance config at next heartbeat
- EVE-OS allocates the requested size from the node's data partition storage pool
- A raw block device or ext4 filesystem is created depending on how the app references it
- When an app instance references this volume in its drive definition, EVE-OS bind-mounts or block-attaches it into the VM or container at the specified mount path
- Data written by the app is stored on the node's local storage and is NOT automatically synced to the cloud
- The volume persists if the app is stopped, restarted, updated, or deleted – it is owned by the node, not the app
Content Tree (CONTENT_TREE)
- EVE-OS receives the volume instance config referencing the image
- EVE-OS downloads the image from the registered datastore (same path as an app image download)
- The image is stored in EVE-OS's local volume pool
- When an app instance references this volume, EVE-OS presents the image data as a read-only block device or mount point
- For QCOW2 images used as VM drives, EVE-OS creates a copy-on-write overlay so the app's writes go to a separate layer while the base image remains unchanged and reusable
Encryption
When cleartext = false, the volume is encrypted at rest using EVE-OS's full-disk encryption:
- The encryption key is generated per-volume on the node
- The key is sealed to the TPM using the measured boot PCR values
- If the node fails attestation (PCR values change – indicating possible tampering), the key cannot be unsealed and the volume is inaccessible
- This means tampered or reflashed nodes cannot read encrypted volume data
Always use cleartext = false for production workloads that store sensitive data.
Common Patterns
| Pattern | Type | Example |
|---|---|---|
| App data persistence | BLOCKSTORAGE | Node-RED flows, InfluxDB data, SQLite DB |
| Pre-stage VM image | CONTENT_TREE | Ubuntu 24.04 cached on node before app deployment |
| Shared data between apps | BLOCKSTORAGE (READWRITE) | Sensor data written by one app, read by another |
| Configuration delivery | CONTENT_TREE (READONLY) | Static config files packaged as a custom image |
| Log offload staging | BLOCKSTORAGE | App writes logs to volume; separate log shipper reads them |
