Key concepts
Volume
A disk. You can create one directly, independent of any instance, resize it, and delete it when you’re done.
Snapshot
A point-in-time copy of a volume. Snapshots are what let a volume’s data become reusable — most notably, as the source for a new image (see Volumes, snapshots, and images).
Storage class
A label describing a volume’s intended performance tier — gp1 (general purpose, SSD-backed) or st1 (throughput-optimized, HDD-backed). io1 (I/O-intensive) is named by the API but is not currently available; asking for it is not a supported configuration. The choice is not cosmetic: gp1 and st1 volumes really are placed on different physical media. There’s currently no origin CLI flag to choose one: every CLI-driven volume create/import always gets the default (gp1). Setting it explicitly needs the GraphQL API directly (pvs.createVolume/importVolume’s storageClass input field).
Managing volumes directly
You can create a volume on its own — specifying just a size, or optionally a snapshot to create it from — list your volumes, resize one, and delete it when you no longer need it. A volume created this way is always the durable (pvs) backend — host_local storage only ever exists as part of an instance’s own automatic volumes, never as something you create standalone.
A host_local volume can’t be snapshotted or resized, and asking to is refused immediately, naming the volume. Its data lives on the compute host running its instance rather than in durable storage, so there is nothing there to copy or grow — that’s a permanent property of the backend, not something to wait for or work around (see the Compute guide for what each backend is for). You will meet this if you take your own instance’s root volume id from its volume attachments and pass it to resize-volume or create-snapshot. Nothing about the volume changes when it’s refused. If you need a disk you can snapshot or grow, use a pvs volume.
Creating a volume from a snapshot holds that snapshot to the same rules as using it for an image (see Volumes, snapshots, and images): the snapshot must report status: COMPLETED and protectionStatus: PROTECTED, and the size you ask for must be at least the size of the volume the snapshot was taken from — asking for more is fine and gives you the extra space. If any of the three doesn’t hold, the request is refused immediately, naming which one: nothing is created, and no work is sent to the storage layer.
A volume can’t be deleted while it still has any snapshot — the underlying storage refuses to remove a disk with a snapshot on it. Delete the volume’s snapshot(s) first (unprotecting first if needed, see below), then the volume. Once deleted, a volume immediately drops out of your volume list; you can still look it up directly by id afterward, with no time limit — a deleted volume or snapshot stays resolvable indefinitely.
Resizing a volume
Volumes only grow. A resize must ask for a size larger than the volume’s current sizeInGib; a smaller size is rejected outright, and so is the volume’s current size. There is no flag or confirmation that unlocks shrinking — growing the disk is safe, shrinking it is not. The platform would have to truncate the block device, and it can’t shrink the filesystem you put on it first, so everything past the new end would be lost and the filesystem would be corrupt. Every other block-storage service works this way for the same reason.
If you need less storage, create a new volume at the smaller size — from a snapshot of the current one if you need its data — move across, and delete the old volume. That way the copy is yours to verify before anything is thrown away.
Resizing works on a volume that is AVAILABLE and on one that is INUSE (attached to a running instance); growing an attached volume enlarges the disk, and the guest still has to grow its own partition and filesystem to use the extra space. A volume that is still CREATING, or is DELETING/DELETED/ERROR, can’t be resized: wait for it to become AVAILABLE, or in the ERROR case delete it and start again.
Quota and account limits
Provisioned volume storage is capped per organisation and per project (maxVolumeGib on organisationQuota/projectQuota). Growing an existing volume counts against that cap exactly like creating a new one — creating, importing and resizing are all held to the same limits, so you can’t get past your ceiling by resizing instead of creating. A resize is charged only for the growth: taking a 100 GiB volume to 120 GiB needs 20 GiB of headroom, not 120.
Being over a limit is not something a resize can undo. Volumes only grow (see Resizing a volume above), so if a limit is lowered below what you already use, no resize brings you back under it — and creating a smaller replacement won’t either, because a new volume is charged its full size against the same ceiling you are already over. Deleting a volume you no longer need is what frees the space, and a delete is never held to a quota. Once the deletion completes, that volume stops counting against your limits.
The same two account-level checks that apply to creating a volume apply to resizing one: your organisation must be active, and the project’s billing account must be valid. If either fails, the resize is refused up front — nothing is changed, sizeInGib and modificationStatus stay as they were, and no work is sent to the storage layer.
When provisioning fails
Creating, resizing and deleting a volume are all asynchronous: the request returns immediately and the underlying storage does the work afterwards. If that work fails, the volume says so rather than pretending it succeeded:
- A volume that couldn’t be created ends up
ERROR, notAVAILABLE, withstatusReasonexplaining what failed. It has no usable disk behind it — delete it and create a new one. (Deleting a volume inERRORis allowed, and is the way to clean one up.) - A volume that couldn’t be deleted stays in your list as
ERRORrather than disappearing asDELETED, again with astatusReason. Its underlying disk may still exist, so it still counts against your quota; retry the delete once whatever blocked it is resolved (most often a snapshot still on the volume). - A resize that couldn’t be applied leaves the volume itself untouched — its data is intact, and
sizeInGibstill reports the size it really has. The failure shows up asmodificationStatus: FAILEDwith amodificationStatusReason; you can request the resize again. A resize the platform can already tell won’t work — a shrink, or a volume in a state that can’t be resized (see Resizing a volume) — is refused immediately with an error saying why, and never reaches this state at all: nothing about the volume changes, not evenmodificationStatus.
While a resize is in flight, modificationStatus is RESIZING and pendingSizeInGib shows the size being applied. sizeInGib only changes once the storage layer confirms the new size, so it is always a size the disk actually has.
A status that has settled stays settled. A volume that reached DELETED is gone for good — it never reappears in your list, and it never goes back to ERROR — and a FAILED resize keeps its modificationStatusReason until you ask for another resize. So a status you were shown is safe to act on: it will only change because of something you did next.
A volume created this way can’t currently be attached to a running instance. There’s no way today to take a volume you created directly and attach it to an instance, and an instance’s own volumes always come from its image, not from a volume you point it at. In practice, a standalone volume is mainly useful as a step toward capturing a snapshot (see below), not as directly-usable instance storage on its own.
Volumes, snapshots, and images
Snapshots are the bridge between a volume and an image: you take a snapshot of a volume, and can then reference that snapshot when creating a new image’s block devices. Launching an instance from that image clones the snapshot into a fresh volume for the new instance.
You can only snapshot a volume that reports AVAILABLE or INUSE. Those are the two states in which the volume’s disk really exists, and snapshotting an attached one is the normal case — you don’t have to detach anything first (the copy is taken while the instance runs, so it captures the disk as a sudden power-off would; quiesce the filesystem inside the guest first if that matters to you). A volume that is still CREATING is refused rather than queued: ask again once it reports AVAILABLE. A volume that is DELETING, DELETED or ERROR is refused too — there is nothing left to copy. In every case the request is refused immediately, naming the volume: no snapshot is created, and no work is sent to the storage layer.
Before a snapshot can be used this way, it needs to be protected — an extra explicit step, separate from creating it. An unprotected snapshot can still be listed and inspected, just not used as a clone source yet. The same applies wherever a snapshot is cloned, including creating a volume directly from one — there is no path that skips the check.
Protecting a snapshot needs it to report COMPLETED — the copy has to have finished before there is anything to protect. Asking earlier, while it is still PENDING, is refused rather than queued; wait for COMPLETED and ask again. Protecting a snapshot that is DELETING, DELETED or ERROR is refused for the same reason: there is nothing there to protect. Unprotecting works on a COMPLETED snapshot and on an ERROR one, the second deliberately, because unprotecting is how you get a protected snapshot into a state you can delete — an ERROR snapshot you can’t delete would be stuck for good. Asking for the protection state a snapshot is already in stays a no-op, as before. A refused protect or unprotect changes nothing about the snapshot.
A snapshot can be deleted once you’re done with it — it must be unprotected first if it was protected (the same underlying-storage constraint that blocks deleting a volume with a snapshot on it also blocks deleting a protected snapshot directly). Once deleted, a snapshot immediately drops out of your snapshot list; you can still look it up directly by id afterward, with no time limit — a deleted volume or snapshot stays resolvable indefinitely.
When a snapshot operation fails
Creating, protecting, unprotecting and deleting a snapshot are all asynchronous, the same as the volume operations above, and they report failure the same way rather than pretending to have succeeded:
- A snapshot that couldn’t be created ends up
ERROR, notCOMPLETED, withstatusReasonexplaining what failed. There is no usable snapshot behind it — delete it and take a new one. - A snapshot that couldn’t be deleted stays in your list as
ERRORrather than disappearing asDELETED, again with astatusReason. Its underlying snapshot may still exist, which will also block deleting the volume it was taken from; retry the delete once whatever blocked it is resolved.
Snapshot statuses settle the same way a volume’s do: a COMPLETED snapshot stays usable as a clone source, and a DELETED one stays gone.
- A protect or unprotect that couldn’t be applied leaves the snapshot’s data untouched and shows up as
protectionStatus: FAILED, with aprotectionStatusReason.FAILEDmeans the snapshot’s real protection state is unknown, so it is treated as neither: it can’t be used as an image’s clone source (that needsPROTECTED), and it can’t be deleted yet (that needsUNPROTECTED). Retry the protect, or unprotect it — unprotecting is also how you get aFAILEDsnapshot back to a state you can delete.
A snapshot only becomes usable as a clone source — for an image, for an instance’s volumes, or for a volume you create directly from it — once it reports status: COMPLETED and protectionStatus: PROTECTED. Both are set only after the storage layer confirms the work, so neither can report success for something that didn’t happen.
Importing an existing image
Instead of creating an empty volume, you can import an existing disk image you built yourself as a volume’s initial content. The source must be reachable over HTTP(S) — the platform downloads it, converts it to its own on-disk format if needed, and imports the result as a real volume. The common disk image formats are accepted, including compressed and sparse ones. From there, the normal snapshot → protect → image flow above turns it into something you can launch instances from.
sizeInGib is the size you get, and the image has to fit in it. The volume is created at the size you ask for — that is what the disk really ends up being, what quota is charged and what you are billed for — so an image whose virtual size is smaller than sizeInGib simply leaves you the extra room, exactly as if you had created the volume at that size and written the image into it. An image that is larger than sizeInGib is refused: the import fails and the volume ends up ERROR with a reason naming the size (ERR_IMPORT_IMAGE_TOO_LARGE), because the platform cannot shrink your image to fit without destroying part of it. Set sizeInGib to at least the image’s virtual size — the virtual size, meaning the size of the disk the image represents, not the size of the file you are downloading, which for a compressed or sparse image is usually much smaller — and delete the failed volume and re-import if you get it wrong.
Using Storage
- The GraphQL API — the primary interface. Look under the
pvsnamespace for volumes and snapshots. - The
originCLI — covers creating, importing, listing, getting, resizing, and deleting volumes, and creating/listing/getting/protecting/unprotecting/deleting snapshots:create-volume,import-volume,list-volumes,get-volume,resize-volume,delete-volume,create-snapshot,list-snapshots,get-snapshot,protect-snapshot,unprotect-snapshot,delete-snapshot.
Creating a volume
mutation CreateVolume($input: PvsCreateVolumeInput!) { pvs { createVolume(input: $input) { urn volumeId availabilityZone sizeInGib storageClass status createdBy } }}{ "input": { "projectUrn": "urn:origin:cloud:admin::7a07d9ec7ba2::proj-4f21c9a03b17", "sizeInGib": 100, "storageClass": "st1" }}origin storage create-volume \ --project-urn urn:origin:cloud:admin::7a07d9ec7ba2::proj-4f21c9a03b17 \ --size 100Pass --snapshot-urn instead to create the volume from a snapshot’s contents.