Table of Contents

EVE-OS / ZEDEDA Application Volumes

Covers: what a volume is, what has to be configured in the GUI vs Terraform, how to deploy an app with a volume, and how to grow a volume after deployment. Primary examples use an image-backed HDD drive; HDD_EMPTY is covered as a separate use case at the end.

This page stays at the controller/app-instance config layer — the fields you set in the GUI, Terraform, or zcli. For what actually happens on the storage backend underneath (qcow2 vs. zvol, pool RAID vs. per-app resize), see the companion page: EVE-OS Volume Provisioning: Pool Layer vs. Per-App Layer.

Sources: lf-edge/eve, zededa/zedcloud Terraform provider docs, ZEDEDA Help Center, and real working Terraform (confirmed against actual zedcloud_application / zedcloud_volume_instance resources, not just provider doc snippets).

1. What a volume is

A volume/drive is a storage object attached to an app. It backs one of:

A volume can be:

Important layering note: everything on this page — maxsize, preserve, ignorepurge, drvtype — is a controller-side config object. It says nothing about how the volume is physically stored. EVE's volumemgr agent on the node reads this config and decides how to realize it on whatever persist backend the node has (ext4 or ZFS). That translation step is a completely separate layer — see the companion page.

2. Key fields

Standalone Volume Instance (''zedcloud_volume_instance'')

Created independently of any app, targets an edge node or cluster directly.

GUI field TF field Notes
Type type VOLUME_INSTANCE_TYPE_BLOCKSTORAGE confirmed (blank disk). Content Tree type presumably VOLUME_INSTANCE_TYPE_CONTENTTREE — not yet confirmed against a real example.
Access Mode accessmode VOLUME_INSTANCE_ACCESS_MODE_READWRITE confirmed. Read-only presumably VOLUME_INSTANCE_ACCESS_MODE_READONLY — not confirmed.
Max Storage Size size_bytes Integer bytes.
Edge Node device_id Single edge node target.
(cluster target) edge_node_cluster { id = … } Used instead of device_id for a cluster.
Encrypted cleartext Inverted — cleartext = false means encrypted.
Label label This is the string an app's images block references via volumelabel to attach this standalone volume.

App-attached image/drive (''manifest.images'' block, inside ''zedcloud_application'')

This is the actual, confirmed schema — nested inside manifest { }, as a repeated images { } block, not a top-level drives block as earlier drafts of this page assumed.

Field Notes
imagename Set for an image-backed entry (references a zedcloud_image by name). Used for the app's own container/rootfs image.
volumelabel Set instead of imagename when this entry attaches a pre-created standalone zedcloud_volume_instance by its label. Mutually exclusive with imagename in the real examples seen.
imageformat CONTAINER for the app's own OCI image; QCOW2 for an attached persistent block-storage volume.
drvtype HDD confirmed for a persistent data disk. (HDD_EMPTY, CDROM, NET — see section 6 and drive-type discussion earlier in this doc's history; not all re-confirmed against a real example yet.)
mountpath Path inside the app, e.g. /data. Setting mountpath causes EVE to automatically mount that volume at that path inside the guest — this applies to both container-type and VM-type apps, not just containers. The guest doesn't need a manual mount step to get the volume attached at that path; EVE handles it. (The exact guest-side mechanism for VM apps — e.g. whether it's cloud-init-driven, an fstab entry EVE injects, or a guest agent — isn't confirmed from source; noted as an open item in section 7.) This is separate from whether the *filesystem on that volume* needs formatting on first use, or resizing after a maxsize change — see the guest-side step in section 5.
cleartext Inverted, same convention as the standalone volume instance — false means encrypted.
ignorepurge Real, distinct field from preserve — both appear together in real examples (ignorepurge = true, preserve = true). Exact semantic split between the two is still not confirmed from proto-level source; treat as “there are two separate purge-related knobs here, not one,” rather than assuming they're synonyms.
preserve See above — set alongside ignorepurge, not instead of it.
maxsize Bare integer, bytes — e.g. maxsize = 1073741824 for 1 GiB. Not a quoted string (correcting an earlier draft of this page).
target Seen as target = “Disk” on a persistent-volume image entry. Other possible values (e.g. for kernel/initrd-style entries) not confirmed.

App-level version field

3. What's required to configure — GUI vs Terraform

GUI

Terraform (zededa/zedcloud provider)

ZCLI

4. Step-by-step: Deploy an app with a persistent volume

This is the real, confirmed pattern: a standalone zedcloud_volume_instance (persistent, survives app redeploys) attached to an app's second images entry by volumelabel, alongside the app's own container image as the first entry.

Via Terraform

# 1. Standalone persistent volume, targeting a specific edge node
resource "zedcloud_volume_instance" "tf_demo_vol1_cont_persist" {
  device_id  = zedcloud_edgenode.demo_en_onlogic_cl250.id  # Edge Node ID
  accessmode = "VOLUME_INSTANCE_ACCESS_MODE_READWRITE"
  cleartext  = false
  label      = "adv-vol1-cont-persist"
  name       = "adv-vol1-cont-persist"
  size_bytes = 1073741824   # 1 GiB
  title      = "adv-vol1-persist"
  type       = "VOLUME_INSTANCE_TYPE_BLOCKSTORAGE"
 
  lifecycle {
    ignore_changes = [device_id]
  }
}
 
# 2. App that attaches the volume above as its second drive
resource "zedcloud_application" "tf_nginx_app_1" {
  name     = "TF-STND-NGINX-APP-1"
  title    = "TF-STND-NGINX-APP-1"
  networks = 1
  manifest {
    ac_kind    = "VMManifest"
    ac_version = "1.2.0"
    name       = "TF-NGINX-APP-1"
    owner {
      user    = "Manny"
      company = "Zededa"
      website = "www.zededa.com"
      email   = "manny@zededa.com"
    }
    desc {
      app_category = "APP_CATEGORY_CLOUD_APPLICATION"
      category     = "APP_CATEGORY_UNSPECIFIED"
      logo = {
        url = "https://nginx.org/img/nginx_logo.svg"
      }
    }
    images {
      # first drive: the app's own container image
      imagename   = zedcloud_image.demo_nginx_alpine.name
      cleartext   = false
      ignorepurge = true
      imageformat = "CONTAINER"
    }
    images {
      # second drive: attach the standalone persistent volume from above
      volumelabel = zedcloud_volume_instance.tf_demo_vol1_cont_persist.label
      imageformat = "QCOW2"
      mountpath   = "/data"
      cleartext   = false
      drvtype     = "HDD"
      ignorepurge = true
      preserve    = true
      maxsize     = 1073741824
      target      = "Disk"
    }
    interfaces {
      name         = "eth0"
      type         = "ip"
      directattach = false
      privateip    = false
      acls {
        matches {
          type  = "ip"
          value = "0.0.0.0/0"
        }
      }
    }
    vmmode    = "HV_PV"
    enablevnc = true
    resources {
      name  = "resourceType"
      value = "custom"
    }
    resources {
      name  = "cpus"
      value = 2
    }
    resources {
      name  = "memory"
      value = 2097152
    }
    configuration {
      custom_config {
        add      = true
        name     = "cloud-config"
        override = true
        template = ""
      }
    }
    app_type            = "APP_TYPE_CONTAINER"
    deployment_type     = "DEPLOYMENT_TYPE_STAND_ALONE"
    cpu_pinning_enabled = false
  }
  user_defined_version = "1"
  origin_type          = "ORIGIN_LOCAL"
  project_access_list  = []
}
  1. terraform plan / terraform apply.
  2. Confirm in the GUI that the app instance reaches RUNNING and the second drive shows the persistent volume attached at /data.

5. Step-by-step: Grow a volume

Important: this is not a live/hot resize. Applying a manifest change that alters an images entry triggers a purge/redeploy of the app. Data on that drive is lost unless preserve/ignorepurge are set appropriately on that entry.

Via Terraform

  1. Raise maxsize on the relevant images block (or size_bytes on the standalone zedcloud_volume_instance, if the volume is standalone rather than inline).
  2. Bump user_defined_version on the zedcloud_application resource — this is the confirmed mechanism (see the real example's own comment: *“bump to force a new app version”*) for pushing the manifest change out.
  3. terraform apply.
  4. Confirm the app redeploys and the drive reflects the new size.
images {
  volumelabel = zedcloud_volume_instance.tf_demo_vol1_cont_persist.label
  imageformat = "QCOW2"
  mountpath   = "/data"
  cleartext   = false
  drvtype     = "HDD"
  ignorepurge = true
  preserve    = true
  maxsize     = 2147483648   # was 1 GiB, now 2 GiB
  target      = "Disk"
}
# ...
user_defined_version = "2"   # bumped from "1" to push this change out

If the volume is instead a standalone zedcloud_volume_instance (not inline in the app's images list), raise size_bytes on that resource directly and apply — no app-side version bump needed in that case, since the volume object is independent of the app manifest.

Guest-side step

Mounting the volume at mountpath is automatic for both VM and container apps (see section 2). What is not automatic is growing the filesystem living on that volume after a maxsize increase:

Reference commands if a manual resize turns out to be needed:

6. Other use case: HDD_EMPTY (blank data disk, no attached content)

HDD_EMPTY is the drive-type equivalent of a standalone VOLUME_INSTANCE_TYPE_BLOCKSTORAGE volume, but declared inline in an app's images block instead of as a standalone object — no image, no content tree, no volumelabel reference, just a blank disk sized by maxsize.

7. Open questions / to verify before relying on this in production