Skip to main content
RunBook Academy

CephI · Storage FundamentalsStorage Fundamentals

Storage primitives — why the primitive is the contract

Foundation⏱ ~14 min

What you'll learn

  • Distinguish the three storage primitives by the contract each offers the client
  • Choose the correct primitive for a given workload and defend the choice
  • Identify which Ceph service implements each primitive
  • Recognise the cost of getting the primitive wrong and what it takes to change it later

Prerequisites

None — start here.

Verified against Ceph Tentacle 20.2.x · Ceph Squid 19.2.x (supported previous) · cephadm matches the verified Ceph release · podman 4.x · csi-rbd and csi-cephfs current · RBD / CephFS / RGW current (matches Ceph release) · Linux kernel 5.15+ (5.10 minimum) · Ubuntu 24.04 LTS (Ceph host baseline) · Debian 12 (Bookworm) (Ceph host baseline) · Rocky Linux / RHEL / AlmaLinux 9.x (Ceph host baseline) · Proxmox VE 9.x (cross-course integration) · Kubernetes 1.31+ (cross-course integration) · 2026-08-18

Not yet marked complete on this device.

Why this matters in production

Almost every difficult storage conversation is really an argument about the primitive. Somebody wants the archive tier on RBD because “it is already there”. Somebody else wants the database on CephFS because “a filesystem is simpler”. Both are choosing a primitive for a reason that has nothing to do with what the workload needs, and both decisions are expensive to reverse once data is in them.

The primitive is a contract: it fixes what operations exist, what they cost, and who owns the consistency. Everything downstream — latency budget, failure behaviour, how you back it up, how many clients can write at once — follows from that contract. Ceph is unusual in serving all three primitives from one RADOS cluster, which makes the choice feel low-stakes. It is not. The pools, the CRUSH rules, the client libraries, and the recovery characteristics all differ.

The three primitives

flowchart TD
  A[Client workload] --> B{What does it need?}
  B -->|"Byte-range random access,<br/>owns its filesystem"| C["Block<br/>RBD"]
  B -->|"Shared POSIX namespace,<br/>many concurrent writers"| D["File<br/>CephFS"]
  B -->|"Whole immutable blobs,<br/>HTTP clients, huge scale"| E["Object<br/>RGW / S3"]
  C --> F[RADOS]
  D --> F
  E --> F

Block exposes a flat, fixed-size address space. You read and write byte ranges by offset. The storage knows nothing about files — the client puts a filesystem on top and owns every question about consistency, journalling, and crash recovery. One writer at a time.

File exposes a hierarchical namespace with POSIX semantics: directories, permissions, locks, rename() that is atomic. Many clients can mount it and write concurrently, and the storage is responsible for making that safe. That responsibility is why file storage needs a metadata service, and why it is the primitive with the most moving parts.

Object exposes a flat namespace of opaque blobs addressed by key, over HTTP. Objects are written whole and replaced whole; there is no byte-range write, no rename, no directory. In exchange you get an API that scales to billions of objects and clients that need nothing but a network connection and credentials.

What each contract actually costs you

Block (RBD)File (CephFS)Object (RGW)
Accessbyte range by offsetpath + POSIX opswhole object by key
Concurrent writersonemanymany, last-write-wins
Who owns consistencythe client’s filesystemthe MDSthe application
Typical latencylowest of the threemiddlehighest
Extra daemons needednoneMDSRGW
Natural unit of backupimage + snapshotsubvolume snapshotbucket replication

Read the “extra daemons” row carefully, because it is the row operators forget. CephFS availability depends on an MDS being up and having replayed its journal. RGW availability depends on gateways being up behind a load balancer. RBD needs neither — a client with a keyring and a route to the OSDs can do I/O. That difference shows up in every outage.

Where the primitives are not interchangeable

The classic mistakes, each of which reaches production regularly:

  • A database on object storage. PostgreSQL wants 8 KiB page writes at a bounded latency and its own WAL semantics. An object API gives whole-object PUT at tens of milliseconds. There is no tuning that closes that gap.
  • A shared web-asset tree on RBD. RBD is single-writer. Mounting the same image read-write on two hosts corrupts the filesystem — not slowly and not subtly.
  • Backups on CephFS because it is convenient. It works, and it puts your backups behind an MDS, in the same cluster, in the same failure domain as the thing they protect. Object storage in a different cluster is the honest answer.

Choosing, in one page

Ask the workload four questions:

  1. Does one client own the data, or many? Many concurrent writers rules out block immediately.
  2. Are writes partial or whole? Byte-range updates rule out object. Appending to a 4 GiB log file is a partial write.
  3. What is the latency budget per operation? Under a few milliseconds means block, and probably NVMe-backed.
  4. How many things will there be? Hundreds of millions of items means object; a filesystem with that many inodes is a metadata problem you will spend years on.

If the answers conflict — and for real workloads they often do — the resolution is usually to split the data, not to compromise on one primitive. A media platform puts its originals in RGW, its database on RBD, and its render scratch on CephFS. That is three pools with three CRUSH rules in one cluster, and it is the design Ceph exists to make possible.

Quiz

Knowledge check · 4 questions

  1. Q1. A team needs storage for twelve application servers that must all read and write the same directory tree of shared configuration and uploaded files. Which primitive fits, and why?

  2. Q2. Because RBD, CephFS, and RGW all store their data in RADOS, a workload can be moved between them without changing the application.

  3. Q3. A team has run their document archive on RBD for two years. The image is 40 TiB, mounted on one server that re-exports it over NFS. They now need three sites to read it and want you to advise. Walk the assessment.

    Single 40 TiB RBD image, ext4, mounted on one VM that runs an NFS server. Two years of growth from 4 TiB. Documents are written once and read many times, averaging 2 MiB. Roughly 18 million files. The NFS server is a single point of failure and its CPU saturates during the nightly index run. Two new sites, 40 ms and 90 ms away, now need read access.

  4. Q4. Name the four questions that decide the primitive, and say which primitive each answer rules out.

Passing score: 75%. Answers are checked in this browser.

Production discipline

Decide the primitive from the access pattern before anything else, and write the decision down with its reasoning. It is the least reversible choice in a storage design: pools, CRUSH rules, client libraries, backup strategy, and failure behaviour all follow from it, and changing it later means migrating data and rewriting clients. When one workload’s answers conflict, split the data across primitives rather than forcing a compromise — one Ceph cluster serving RBD, CephFS, and RGW side by side is the design the system was built for.

Cross-course references

  • Linux: Part XIII (Disks and Block Devices) and Part XIV (Filesystems) cover the block and POSIX layers this lesson sits above.
  • Proxmox: Part V (Storage Fundamentals) and Part VIII (Ceph) for the hypervisor view of the block primitive.
  • Kubernetes: Parts XLVIII-LV (storage and CSI) for how the primitives reach a Pod.