CertGrid CertGrid
Configuration·Podman

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

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.

Every command on this page ran on podman01.
Server NameIP AddressOSRolesCPURAMHDD
PODMAN01192.168.0.24Ubuntu 26.04 LTSPrimary Container Host2 Core4 GB50 GB

Before you start

  1. A tmpfs volume with a size limit

    The local driver 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 scratch

    inspect shows the driver is still local - the options are what changed, not the driver:

    driver=local opts=map[SIZE:8m device:tmpfs o:size=8m type:tmpfs]

    Inside the container, df reports a tmpfs of 8.0M rather than the host filesystem's size, which is the first useful thing here: a container that reads df to 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/s

    Expected resultA local volume with tmpfs options, df reporting 8.0M, and a successful 4 MB write.

    Success conditionYou have a volume whose size the container can see.

  2. The limit is real

    Ask for 16 MB into an 8 MB volume:

    8+0 records out
    8388608 bytes (8.0MB) copied

    8 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/s

    Expected result8 MB copied where 16 was asked for.

    Success conditionYou have seen a volume refuse to grow past its size.

  3. 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 ...

    df reports 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 system

    The 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-only on its own breaks a surprising number of images, because plenty write a pid file or a cache without saying so. The podman diff from 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 scratchdbdatadbrestorescratch

    Expected 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

Official sources