Podman tmpfs and Read-Only Filesystems
`--opt type=tmpfs --opt o=size=8m` makes a volume that lives in RAM and enforces its size - a 16 MB write stops at 8. Plus `--mount type=tmpfs` for the same thing without a volume, and `--read-only` for none at all.
Storage Guide 35 of 47 Advanced
- OSUbuntu 26.04 LTS (resolute)
- Podman5.7.0
- Runtimecrun 1.21
- Networknetavark 1.16.1
- TimeAbout 12 min
- Reviewed22 August 2026
Written against the versions above. Podman follows the distribution here rather than a vendor repository, so the version you get is the one Ubuntu shipped. The commands are stable across 5.x.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| PODMAN01 | 192.168.0.24 | Ubuntu 26.04 LTS | Primary Container Host | 2 Core | 4 GB | 50 GB |
Before you start
- guide 60 for the default behaviour these options change.
-
A tmpfs volume with a size limit
The
localdriver takes mount options, and with them a volume stops being a directory in your store:podman volume create --opt type=tmpfs --opt device=tmpfs \ --opt o=size=8m scratchinspectshows the driver is stilllocal- the options are what changed, not the driver:driver=local opts=map[SIZE:8m device:tmpfs o:size=8m type:tmpfs]Inside the container,
dfreports a tmpfs of 8.0M rather than the host filesystem's size, which is the first useful thing here: a container that readsdfto decide how much it may cache now gets an honest answer scoped to its own storage rather than the whole disk.A 4 MB write succeeds.
bash Example session podman volume create --opt type=tmpfs --opt device=tmpfs --opt o=size=8m scratchscratchpodman volume inspect scratch --format 'driver={{.Driver}} opts={{.Options}}'driver=local opts=map[SIZE:8m device:tmpfs o:size=8m type:tmpfs]podman run --rm -v scratch:/scratch docker.io/library/alpine sh -c 'df -h /scratch; dd if=/dev/zero of=/scratch/big bs=1M count=4 2>&1 | tail -1'Filesystem Size Used Available Use% Mounted ontmpfs 8.0M 0 8.0M 0% /scratch4194304 bytes (4.0MB) copied, 0.001031 seconds, 3.8GB/sExpected resultA local volume with tmpfs options,
dfreporting 8.0M, and a successful 4 MB write.Success conditionYou have a volume whose size the container can see.
-
The limit is real
Ask for 16 MB into an 8 MB volume:
8+0 records out 8388608 bytes (8.0MB) copied8 MB written of 16 requested. The write stopped at the limit rather than filling the host's disk.
That is the operational reason to care. A container with an ordinary volume can write until the host runs out of space, and a runaway log or cache taking the machine down with it is a common way for this to go wrong. A sized tmpfs volume turns that into the container's own problem, which is where it belongs.
The cost is that tmpfs is RAM. Eight megabytes is eight megabytes of memory when full, it counts against the container's memory limit if it has one, and everything in it is gone when the container stops. Use it for scratch space, caches and sockets - never for anything you would miss.
bash Example session podman run --rm -v scratch:/scratch docker.io/library/alpine sh -c 'dd if=/dev/zero of=/scratch/toobig bs=1M count=16 2>&1 | tail -2'8+0 records out8388608 bytes (8.0MB) copied, 0.001979 seconds, 3.9GB/sExpected result8 MB copied where 16 was asked for.
Success conditionYou have seen a volume refuse to grow past its size.
-
The same thing without a volume, and no writes at all
If nothing needs to be shared or named, skip the volume entirely:
podman run --mount type=tmpfs,destination=/tmpfs,tmpfs-size=4m ...dfreports 4.0M. Nothing is created in your store, nothing to clean up, and the mount exists only for the container's lifetime. This is the right shape for a container that needs somewhere writable and nothing more.Which pairs with the other end of the scale -
--read-only:$ podman run --rm --read-only docker.io/library/alpine sh -c 'touch /nope' touch: /nope: Read-only file systemThe entire root filesystem is immutable. Combine the two and you get the hardened shape used in production: a read-only root, plus a small tmpfs at each path the process genuinely needs to write. The application cannot modify itself, cannot be modified by anything that gets in, and its scratch space is bounded and disappears on exit.
--read-onlyon its own breaks a surprising number of images, because plenty write a pid file or a cache without saying so. Thepodman difffrom guide 13 is how you find out which paths those are: run it normally, read the list, then mount a tmpfs at each one.bash Example session podman run --rm --mount type=tmpfs,destination=/tmpfs,tmpfs-size=4m docker.io/library/alpine df -h /tmpfsFilesystem Size Used Available Use% Mounted ontmpfs 4.0M 0 4.0M 0% /tmpfspodman run --rm --read-only docker.io/library/alpine sh -c 'touch /nope'touch: /nope: Read-only file system[exit 1]podman volume rm dbdata dbrestore scratchdbdatadbrestorescratchExpected resultA 4.0M tmpfs, a read-only filesystem error, and the volumes removed.
Success conditionYou can build a container with a read-only root and bounded scratch space.
Troubleshooting
--opt type=tmpfsaccepted butdfshows the host filesystem.Why:
device=tmpfsis also required - the options are passed tomount, which needs both.Fix:All three:
--opt type=tmpfs --opt device=tmpfs --opt o=size=8m.The container fails with
Read-only file systemafter adding--read-only.Why: The image writes somewhere you did not provide for.
Fix:
podman diffon a normally-running container lists every path it writes. Mount a tmpfs at each.Data in a tmpfs volume disappears.
Why: It is RAM. That is the design.
Fix:Use an ordinary volume for anything that must persist.
A sized tmpfs fills faster than expected.
Why:
size=8mis 8 mebibytes, and filesystem overhead counts.Fix:
podman exec <c> df -h <path>for what the container actually sees.