Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

tmpfs

Introduction

tmpfs is a Linux filesystem that stores all files in virtual memory — specifically, in the kernel’s page cache and anonymous memory allocations. Unlike traditional filesystems backed by block devices (disk or SSD), tmpfs has no persistent backing store. Its contents live entirely in RAM (and swap, if available) and are lost on unmount or reboot.

Originally derived from the older shm filesystem (POSIX shared memory), tmpfs was generalized in Linux 2.4 to serve multiple purposes: /tmp, /run, /dev/shm, container overlays, and more. It is one of the most frequently mounted filesystems on any modern Linux system.


1. Architecture Overview

flowchart TB
    subgraph "User Space"
        APP["Application"]
        SHM["shm_open() / mmap()"]
    end
    subgraph "VFS Layer"
        VFS["Virtual Filesystem"]
    end
    subgraph "tmpfs (shmem)"
        TMPFS["tmpfs superblock<br>(shmem_sb_info)"]
        INODE["shmem_inode_info"]
        FOLIO["Page Cache<br>(address_space / xarray)"]
    end
    subgraph "Memory Subsystem"
        ANON["Anonymous Pages"]
        SWAP["Swap Subsystem"]
        PCP["Per-CPU Page Cache"]
    end
    subgraph "Hardware"
        RAM["Physical RAM"]
        SWAP_DEV["Swap Device"]
    end

    APP -->|"write()/mmap()"| VFS
    SHM --> VFS
    VFS --> TMPFS
    TMPFS --> INODE
    INODE --> FOLIO
    FOLIO --> ANON
    ANON --> RAM
    ANON -->|"memory pressure"| SWAP
    SWAP --> SWAP_DEV

2. How tmpfs Works

2.1 Virtual Memory Backend

tmpfs does not allocate a fixed RAM block. Instead, each page written to tmpfs becomes an anonymous page mapped by the kernel’s page management subsystem. This means:

  • Pages can be swapped out — if swap is configured, tmpfs pages can be evicted to disk under memory pressure, just like any anonymous memory.
  • No block device required — tmpfs doesn’t need a partition, loop device, or disk image.
  • Dynamic sizing — the filesystem grows and shrinks as files are created and deleted, up to a configurable limit.
  • Shared memory semantics — tmpfs is the implementation behind POSIX shared memory (shm_open), visible at /dev/shm.

2.2 Data Structures

Internally, tmpfs uses shmem_inode_info structures that extend the standard VFS inode:

/* Simplified from include/linux/shmem_fs.h */
struct shmem_inode_info {
    struct inode        vfs_inode;      /* Standard VFS inode */
    spinlock_t          lock;           /* Protects fields below */
    unsigned long       flags;          /* SHMEM_PAGE_SHIFT, etc */
    struct shared_policy policy;        /* NUMA memory policy */
    struct timespec64   i_crtime;       /* Creation time */
    struct address_space i_mapping;     /* Page cache mapping */
    unsigned int        seals;          /* F_SEAL_* for memfd */
};

struct shmem_sb_info {
    unsigned long max_blocks;           /* Max blocks (from size= option) */
    unsigned long free_inodes;          /* Remaining inode slots */
    spinlock_t    stat_lock;            /* Protects counters */
    kuid_t        uid;                  /* Default owner UID */
    kgid_t        gid;                  /* Default owner GID */
    umode_t       mode;                 /* Default permissions */
    struct list_head swaplist;          /* List of shmem_swaplist_entry */
    /* Huge page tracking */
    unsigned long huge;                 /* Huge page policy */
};

2.3 VFS Operations

tmpfs implements standard VFS interfaces:

/* mm/shmem.c — key operations */
static const struct super_operations shmem_ops = {
    .alloc_inode   = shmem_alloc_inode,
    .destroy_inode = shmem_destroy_inode,
    .statfs        = shmem_statfs,
    .show_options  = shmem_show_options,
    .free_inode    = shmem_free_in_core_inode,
};

static const struct inode_operations shmem_inode_ops = {
    .create        = shmem_create,
    .lookup        = simple_lookup,
    .link          = shmem_link,
    .unlink        = shmem_unlink,
    .symlink       = shmem_symlink,
    .mkdir         = shmem_mkdir,
    .rmdir         = shmem_rmdir,
    .mknod         = shmem_mknod,
    .rename        = shmem_rename2,
    .setattr       = shmem_setattr,
    .getattr       = shmem_getattr,
    .get_acl       = shmem_get_acl,
};

static const struct address_space_operations shmem_aops = {
    .writepage     = shmem_writepage,
    .dirty_folio   = noop_dirty_folio,
    .write_begin   = shmem_write_begin,
    .write_end     = shmem_write_end,
};

3. Mount Options

tmpfs supports the following mount options:

OptionDescriptionDefault
size=<bytes>Maximum filesystem size. Accepts K, M, G suffixes.50% of RAM
nr_inodes=<count>Maximum number of inodesHalf of available RAM in pages
mode=<octal>Default permissions for the root directory0777
uid=<id>Default owner of the root directoryMounting user’s UID
gid=<id>Default group of the root directoryMounting user’s GID
huge=<policy>Transparent huge page policynever
mpol=<policy>Default NUMA memory policyCurrent process policy
inode64Use 64-bit inode numbersArchitecture dependent

3.1 Huge Page Policies

The huge= option controls transparent huge page (THP) behavior on tmpfs:

PolicyBehavior
neverNever use huge pages (default)
alwaysAttempt huge pages for all allocations
within_sizeOnly use huge pages within size= limit
adviseUse huge pages only for MADV_HUGEPAGE regions
denyExplicitly disable huge pages, even if requested
# Mount with huge pages for large working sets
mount -t tmpfs -o size=10G,huge=always tmpfs /mnt/huge_tmpfs

# Verify huge page usage
cat /sys/kernel/mm/transparent_hugepage/shmem_enabled
# always inherit within_size advise never deny force

3.2 NUMA Memory Policies

The mpol= option controls how tmpfs pages are distributed across NUMA nodes:

PolicyBehavior
defaultAllocate on the current CPU’s node
bind:<nodes>Only allocate on specified nodes
interleaveRound-robin across all nodes
preferred:<node>Prefer specified node, fall back to others
localAlways allocate on the local node
# Interleave across all NUMA nodes (good for shared data)
mount -t tmpfs -o size=4G,mpol=interleave tmpfs /mnt/numa_tmpfs

# Bind to specific nodes (0 and 1)
mount -t tmpfs -o size=4G,mpol=bind:0-1 tmpfs /mnt/bound_tmpfs

3.3 Common Mount Examples

# Mount a 2GB tmpfs at /tmp with restrictive permissions
mount -t tmpfs -o size=2G,mode=1777 tmpfs /tmp

# Mount for POSIX shared memory (done automatically by systemd)
mount -t tmpfs tmpfs /dev/shm -o size=512M

# Mount with NUMA interleave policy
mount -t tmpfs -o size=4G,mpol=interleave tmpfs /mnt/numa_tmpfs

# Mount with transparent huge pages enabled
mount -t tmpfs -o size=10G,huge=always tmpfs /mnt/huge_tmpfs

4. Use Cases

4.1 /tmp and /run

Most modern distributions mount /tmp and /run as tmpfs:

  • /tmp: Temporary files benefit from RAM speed. Cleaning on reboot avoids stale temp file accumulation.
  • /run: Runtime data (PID files, sockets, D-Bus) needs fast, volatile storage. Systemd mounts this early in boot.
# Check tmpfs mounts
$ df -h /tmp /run /dev/shm
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           2.0G  1.2M  2.0G   1% /tmp
tmpfs           800M  1.5M  799M   1% /run
tmpfs            64M  4.0K   64M   1% /dev/shm

4.2 POSIX Shared Memory (/dev/shm)

The POSIX shared memory API uses tmpfs as its backing store:

#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main() {
    /* Create shared memory object */
    int fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0666);
    ftruncate(fd, 4096);

    /* Map it */
    void *ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    /* Write data — this goes to tmpfs */
    sprintf(ptr, "Hello from shared memory!");

    /* Another process can open and mmap the same name */
    /* Data persists until shm_unlink() and all mappings are closed */

    munmap(ptr, 4096);
    close(fd);
    shm_unlink("/my_shm");
    return 0;
}

4.3 Container tmpfs Mounts

Containers often use tmpfs for sensitive or ephemeral data:

# Docker: mount a tmpfs inside a container
docker run --tmpfs /app/cache:rw,noexec,nosuid,size=100m myimage

# Podman: equivalent
podman run --tmpfs /app/cache:rw,size=100m myimage

# Kubernetes: emptyDir with medium=Memory (see YAML below)

4.4 Build Directories and Test Environments

Compiling on tmpfs avoids disk I/O entirely:

# Use tmpfs for a kernel build
mount -t tmpfs -o size=10G tmpfs /usr/src/linux/build
cd /usr/src/linux
make O=build -j$(nproc)

4.5 memfd_create — Anonymous tmpfs Files

memfd_create() creates anonymous tmpfs files that can be sealed:

#include <sys/mman.h>
#include <stdio.h>

int main() {
    /* Create an anonymous tmpfs file */
    int fd = memfd_create("my_memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING);

    /* Write data */
    write(fd, "secret data", 11);

    /* Seal the file — prevents further modification */
    fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW |
                           F_SEAL_WRITE | F_SEAL_SEAL);

    /* Map it */
    void *ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0);
    printf("Data: %s\n", (char *)ptr);

    /* File is automatically cleaned up when fd is closed */
    return 0;
}

5. Size Limits and Memory Management

5.1 Default Size

Without an explicit size= option, tmpfs defaults to 50% of total physical RAM. Each mount has its own limit, but they all compete for the same memory pool.

# Check RAM and tmpfs usage
$ free -h
              total        used        free      shared  buff/cache   available
Mem:           16Gi       4.2Gi       8.1Gi       128Mi       3.7Gi        11Gi
Swap:         8.0Gi          0B       8.0Gi

5.2 What Happens When tmpfs is Full?

When a tmpfs mount reaches its size= limit, writes fail with ENOSPC:

$ mount -t tmpfs -o size=100M tmpfs /mnt/test
$ dd if=/dev/zero of=/mnt/test/fill bs=1M count=99
99+0 records in
99+0 records out
$ dd if=/dev/zero of=/mnt/test/more bs=1M count=2
dd: error writing '/mnt/test/more': No space left on device

Important: tmpfs does NOT enforce a hard memory reservation. The size= limit only caps the filesystem’s own usage. If the system runs low on memory overall, the OOM killer may still be invoked for other processes, even if tmpfs holds memory that could theoretically be freed (since tmpfs pages can be swapped).

5.3 Swap Interaction

tmpfs pages participate in normal memory reclaim:

flowchart TD
    A["tmpfs page created"] --> B{"Memory pressure?"}
    B -->|No| C["Page stays in RAM"]
    B -->|Yes| D{"Swap available?"}
    D -->|Yes| E["Page swapped out"]
    D -->|No| F["Page must stay in RAM"]
    E --> G["Page read back on access"]
    G --> C
    F --> H["May trigger OOM if severe"]

6. tmpfs vs Other RAM-backed Filesystems

Featuretmpfsramfsdevtmpfs
Swap supportYesNoYes
Size limitConfigurableUnlimited (grows until OOM)Limited like tmpfs
Can be remountedYesNoNo
Typical use/tmp, /run, /dev/shmEarly bootDevice nodes
Sourcemm/shmem.cfs/ramfs/drivers/base/devtmpfs.c
Huge pagesYesNoNo
NUMA policiesYesNoNo

7. Implementation Details

7.1 Key Source Files

  • mm/shmem.c — Core tmpfs implementation (~4000 lines)
  • include/linux/shmem_fs.h — Header with data structures
  • mm/shmem_swap.c — Swap support for tmpfs

7.2 Page Allocation Flow

When a file on tmpfs is written:

sequenceDiagram
    participant App as Application
    participant VFS as VFS
    participant SHM as shmem
    participant PC as Page Cache
    participant MM as Memory Allocator

    App->>VFS: write(fd, data)
    VFS->>SHM: shmem_write_begin()
    SHM->>PC: shmem_get_folio_gfp()
    PC->>MM: alloc_folio(GFP_KERNEL)
    MM-->>PC: folio allocated
    PC-->>SHM: folio attached to address_space
    SHM-->>VFS: page is "written"
    VFS-->>App: write() returns

    Note over SHM,PC: No block I/O occurs!

7.3 inode Allocation

tmpfs dynamically allocates inodes using shmem_alloc_inode(). The nr_inodes mount option controls how many can exist simultaneously. When the limit is hit, ENOSPC is returned even if size= has room.

# Check inode usage
$ df -i /tmp
Filesystem      Inodes   IUsed   IFree IUse% Mounted on
tmpfs          2097152     128 2097024    1% /tmp

# Set custom inode limit
mount -t tmpfs -o nr_inodes=100000,size=1G tmpfs /mnt/test

7.4 shmem_swaplist

When memory pressure occurs, the kernel needs to know which tmpfs pages can be swapped out. The swaplist in shmem_sb_info tracks tmpfs inodes that have swap-backed pages:

/* mm/shmem_swap.c — simplified */
static int shmem_swaplist_add(struct shmem_inode_info *info)
{
    /* Add inode to the global shmem swaplist */
    spin_lock(&sbinfo->swaplist_lock);
    if (list_empty(&info->swaplist)) {
        list_add_tail(&info->swaplist, &sbinfo->swaplist);
    }
    spin_unlock(&sbinfo->swaplist_lock);
    return 0;
}

8. Performance Considerations

8.1 tmpfs Performance Characteristics

OperationTypical LatencyNotes
Sequential read~10 GB/sMemory bandwidth limited
Sequential write~8 GB/sMemory bandwidth limited
Random 4K read~1 µsNo disk seek
Random 4K write~1 µsNo disk seek
File creation~5 µsinode allocation + dentry
File deletion~3 µsCleanup + page release

8.2 When NOT to Use tmpfs

  • Large datasets that exceed RAM — tmpfs will swap, killing performance
  • Data that must survive reboots — tmpfs is volatile
  • Workloads with heavy random I/O on large files — page cache pressure
  • Systems without swap — tmpfs pages can never be evicted

8.3 Monitoring tmpfs Usage

# Per-mount usage
$ df -h /tmp /dev/shm /run

# Detailed memory accounting
$ cat /proc/meminfo | grep -E "Shmem|Cached"
Shmem:          131072 kB    # Total shared memory (tmpfs + shmem)
Cached:        4194304 kB    # Page cache (includes tmpfs)

# Per-process tmpfs usage
$ cat /proc/<pid>/status | grep -i shmem
ShmemPmdMapped:         0 kB
Shmem:               4096 kB

9. Kubernetes tmpfs Example

apiVersion: v1
kind: Pod
metadata:
  name: tmpfs-demo
spec:
  containers:
    - name: app
      image: nginx:latest
      volumeMounts:
        - mountPath: /cache
          name: cache-volume
  volumes:
    - name: cache-volume
      emptyDir:
        medium: Memory    # This creates a tmpfs mount
        sizeLimit: 256Mi  # Optional: enforce size limit

10. systemd tmpfiles.d

systemd uses tmpfiles.d configuration to manage tmpfs mounts and temporary files at boot:

# /etc/tmpfiles.d/myapp.conf
# Type  Path          Mode  User  Group  Age  Argument
d       /run/myapp    0755  root  root   -    -
f       /run/myapp/pid 0644 root root   -    -
# Apply tmpfiles.d configuration
systemd-tmpfiles --create /etc/tmpfiles.d/myapp.conf

# Clean up old files
systemd-tmpfiles --clean /etc/tmpfiles.d/myapp.conf

11. Troubleshooting

11.1 Common Issues

SymptomCauseSolution
ENOSPC on tmpfssize= limit reachedIncrease size= or free space
OOM killer invokedtmpfs consuming too much RAMReduce tmpfs size=, add swap
Slow tmpfs performanceSwapping to diskIncrease RAM or reduce tmpfs usage
Missing /dev/shmNot mountedAdd to fstab or systemd unit
Permission deniedWrong uid/gid on mountSet uid=/gid= options

11.2 Debugging

# Check all tmpfs mounts and their options
$ mount | grep tmpfs

# Check memory pressure on tmpfs
$ cat /proc/pressure/memory
some avg10=0.00 avg60=0.00 avg300=0.00 total=0
full avg10=0.00 avg60=0.00 avg300=0.00 total=0

# Monitor swap activity for tmpfs pages
$ vmstat 1
procs -----------memory---------- ---swap--
 r  b   swpd   free   buff  cache   si   so
 0  0      0 8388608 524288 4194304   0    0

# Check if a specific file's pages are in swap
$ cat /proc/<pid>/smaps | grep -A 5 "/dev/shm"

12. References

  • devtmpfs — A specialized tmpfs variant for device nodes
  • mounting — How tmpfs is mounted via mount(2) and mount namespaces
  • buffer-cache — How tmpfs interacts with the page cache
  • numa — NUMA policies for tmpfs (mpol= option)
  • Page Cache — Underlying caching mechanism
  • Swap — Swap subsystem for tmpfs pages
  • Huge Pages — THP support in tmpfs