Entras a un servidor Ubuntu 24.04 porque “la aplicación va lenta”. Encuentras la RAM al 97%, swap trabajando sin descanso y el kernel ya ha empezado a elegir víctimas. Entonces notas algo incómodo: nadie “fugó” memoria. Un tmpfs lo hizo.
tmpfs es maravilloso—hasta que no lo es. Hace que los archivos se sientan como RAM, porque efectivamente lo son. Cuando se hincha, hace lo que hace la RAM cuando se llena: arruina tu tarde. Vamos a impedir que devore la máquina manteniendo felices a las aplicaciones y el arranque aburrido.
Qué estás viendo (y por qué confunde)
tmpfs es un sistema de archivos que guarda datos en memoria (page cache / shmem) y puede paginar a swap bajo presión. Ubuntu monta varias instancias tmpfs por defecto: /run, /dev/shm, a veces /tmp (según la configuración), además de varios directorios de tiempo de ejecución por servicio.
La parte confusa es que las herramientas muestran un “Size” de tmpfs que parece enorme—a menudo la mitad de la RAM—y los administradores lo interpretan como memoria asignada. No lo es. Ese número es un límite, no uso. El uso real es la columna “Used”, y es memoria real (o swap) que puede empujarte a OOM si crece.
Cuando tmpfs “se descontrola”, las causas habituales son aburridas:
- Una aplicación escribe archivos temporales grandes en
/dev/shmo/runporque es “rápido”. - Un directorio de colas o spool está en tmpfs y hay picos de carga.
- Contenedores montan tmpfs para
/tmpo un volumenemptyDiry los logs/métricas se disparan. - Un servicio mal configurado genera artefactos de tiempo de ejecución y nunca los limpia.
- Alguien “optimizó” convirtiendo
/tmpen tmpfs en un servidor con poca RAM.
Arreglar esto no es un solo sysctl mágico. Es (1) medir quién escribe, (2) fijar límites que coincidan con la realidad, y (3) asegurarse de que la carga vuelva al disco o falle de forma elegante en lugar de tumbar el host.
Hechos y breve historia que realmente ayudan
- tmpfs lleva décadas en Linux y sustituyó la vieja mentalidad de “ramdisk por todas partes” porque puede crecer/encogerse y puede paginar a swap bajo presión.
- Los ramdisks clásicos preasignan: un dispositivo de bloque respaldado por RAM con tamaño fijo. tmpfs es orientado a archivos y usa memoria bajo demanda.
- En muchas distribuciones, el tamaño por defecto de tmpfs aparece como ≈50% de la RAM (o similar), pero eso solo es el máximo permitido antes de ENOSPC.
- Los datos de tmpfs viven en el mismo pool de memoria que todo lo demás. Compiten con el heap de tus aplicaciones, la caché del sistema de archivos y la memoria del kernel.
- Escribir en tmpfs incrementa “Shmem” y “Cached” de forma diferente según el mapeo y la contabilidad; leer
/proc/meminfocorrectamente importa. - systemd hizo tmpfs más gestionado centralmente: montajes como
/runy directorios de tiempo de ejecución por unidad son parte del arranque ahora, no un montón de scripts init ad hoc. /runreemplazó a/var/runen sistemas modernos: es tmpfs por diseño, así el estado de ejecución no persiste entre reinicios./dev/shmes memoria compartida POSIX, no “un buen directorio scratch”. Algunas aplicaciones lo tratan como un área temporal rápida y te comerse la RAM sin problema.- La presión de memoria puede empujar páginas tmpfs a swap, lo cual “está bien” hasta que no lo está: los picos de latencia y las tormentas de swap pueden ser peores que usar disco para archivos temporales.
Una idea parafraseada de Werner Vogels (CTO de Amazon) sigue vigente en ops: “todo falla todo el tiempo—diseña y opera con esa suposición” (idea parafraseada). tmpfs no está exento; planea que se llene.
Guía rápida de diagnóstico
Este es el orden que te lleva al culpable más rápido, sin perderte en teoría.
1) Confirma que es tmpfs y no “memoria misteriosa”
- Revisa
df -hTpara ver qué montaje tmpfs se está llenando. - Chequea
/proc/meminfoporShmem,MemAvailabley uso de swap. - Busca señales recientes de OOM o presión de memoria en
journalctl.
2) Encuentra qué directorio está creciendo
- Usa
du -xen el montaje afectado (se mantiene dentro del sistema de archivos). - Busca archivos borrados pero abiertos con
lsof +L1.
3) Identifica el proceso y decide: limitarlo, moverlo o arreglarlo
- Si es un servicio: inspecciona la unidad, directorios de tiempo de ejecución y variables de entorno que apunten a
/run,/tmp,/dev/shm. - Si es un contenedor: comprueba montajes y volúmenes tmpfs; aplica límites de memoria y límites de tamaño para tmpfs.
- Si es una caché conocida: configura retención/TTL y aplica limpieza.
4) Aplica la mitigación menos peligrosa primero
- Fija un límite sensato
size=para el tmpfs arriesgado. - Mueve rutas de scratch grandes al disco (p. ej.,
/var/tmpo un directorio dedicado en SSD). - Solo entonces considera ajustes agresivos del kernel; la mayoría de los desbordes de tmpfs son crecimiento de archivos.
Broma #1: tmpfs no es “memoria libre”. Es la misma memoria, solo que con sombrero de sistema de archivos.
Tareas prácticas (comandos, salidas, decisiones)
A continuación hay comandos probados en campo. Para cada uno: qué te dice y qué decisión tomas.
Task 1: Lista montajes tmpfs y localiza el hinchado
cr0x@server:~$ df -hT | awk 'NR==1 || $2=="tmpfs" || $2=="devtmpfs"'
Filesystem Type Size Used Avail Use% Mounted on
tmpfs tmpfs 3.2G 1.9G 1.3G 60% /run
/dev/nvme0n1p2 ext4 80G 22G 54G 29% /
tmpfs tmpfs 16G 8.1G 7.9G 51% /dev/shm
tmpfs tmpfs 5.0M 44K 5.0M 1% /run/lock
tmpfs tmpfs 3.2G 4.0K 3.2G 1% /run/user/1000
Significado: /dev/shm está consumiendo 8.1G. Eso es presión real si el host tiene 16G de RAM.
Decisión: Enfócate en ese montaje primero. No toques /run/lock; es pequeño y no está relacionado.
Task 2: Confirma presión real de memoria (no solo un gran “Size” de tmpfs)
cr0x@server:~$ grep -E 'MemTotal|MemAvailable|Shmem|SwapTotal|SwapFree|Cached' /proc/meminfo
MemTotal: 16329640 kB
MemAvailable: 1128400 kB
Cached: 2413720 kB
Shmem: 8551120 kB
SwapTotal: 4194300 kB
SwapFree: 112340 kB
Significado: Shmem es ~8.1G, que coincide con /dev/shm. MemAvailable es bajo y el swap está casi agotado.
Decisión: Trata esto como un incidente en vivo: detén el crecimiento y/o expulsa la carga. No “simplemente añadas swap” como única solución.
Task 3: Revisa si hubo kills por OOM o logs de presión de memoria
cr0x@server:~$ journalctl -k -b | egrep -i 'oom|out of memory|memory pressure' | tail -n 8
kernel: Memory cgroup out of memory: Killed process 27144 (python3) total-vm:9652144kB, anon-rss:5132140kB, file-rss:11320kB, shmem-rss:2048kB
kernel: oom_reaper: reaped process 27144 (python3), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
kernel: Out of memory: Killed process 30911 (node) total-vm:7621400kB, anon-rss:2874100kB, file-rss:9020kB, shmem-rss:1780kB
Significado: El kernel ya mató procesos. Aunque shmem-rss en procesos matados parezca pequeño, el shmem a nivel de sistema es enorme.
Decisión: Reduce el crecimiento de tmpfs ahora (cap, limpiar, reiniciar autores), luego arregla la causa raíz.
Task 4: Identifica qué hay realmente en el montaje tmpfs
cr0x@server:~$ sudo du -xh --max-depth=1 /dev/shm | sort -h
0 /dev/shm/snap.lxd
12K /dev/shm/systemd-private-2d3c...-chrony.service-...
20M /dev/shm/app-cache
8.1G /dev/shm/feature-store
8.1G /dev/shm
Significado: Un directorio domina: /dev/shm/feature-store.
Decisión: Encuentra qué proceso lo posee/usa; ese es tu punto de palanca. Limpiar directorios systemd-private al azar no es la solución.
Task 5: Mapea el directorio a un proceso (rápido y suele ser suficiente)
cr0x@server:~$ sudo lsof +D /dev/shm/feature-store 2>/dev/null | head -n 10
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python3 18422 svc 12w REG 0,37 536870912 131112 /dev/shm/feature-store/chunk-0001.bin
python3 18422 svc 13w REG 0,37 536870912 131113 /dev/shm/feature-store/chunk-0002.bin
python3 18422 svc 14w REG 0,37 536870912 131114 /dev/shm/feature-store/chunk-0003.bin
Significado: PID 18422 está escribiendo archivos de cientos de MB en memoria compartida.
Decisión: Esto es comportamiento de la aplicación, no “Ubuntu usando RAM aleatoriamente”. Arregla la configuración de la app o cambia su ruta de scratch.
Task 6: Detectar archivos borrados pero abiertos (la trampa “df dice lleno, du no coincide”)
cr0x@server:~$ sudo lsof +L1 | head -n 10
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
node 30911 svc 28w REG 0,37 1073741824 0 140221 /dev/shm/feature-store/tmp.log (deleted)
Significado: Un archivo de 1G fue borrado del árbol de directorios pero aún lo mantiene abierto un proceso. du no lo contará; df sí.
Decisión: Reinicia o envía la señal adecuada al proceso para que cierre/reabra logs; de lo contrario el espacio no volverá.
Task 7: Inspecciona opciones de montaje para entender límites actuales
cr0x@server:~$ findmnt -no TARGET,FSTYPE,OPTIONS /dev/shm
/dev/shm tmpfs rw,nosuid,nodev,size=16329640k,inode64
Significado: El límite de tamaño es básicamente del tamaño de la RAM. Eso es permisivo; permite que una app intente consumir todo el host.
Decisión: Fija un límite menor, pero solo después de verificar qué necesita memoria compartida y cuánto.
Task 8: Revisa la vista de systemd sobre montajes tmpfs y quién los posee
cr0x@server:~$ systemctl status dev-shm.mount --no-pager
● dev-shm.mount - POSIX Shared Memory
Loaded: loaded (/usr/lib/systemd/system/dev-shm.mount; static)
Active: active (mounted) since Mon 2025-12-29 08:31:12 UTC; 3h 12min ago
Where: /dev/shm
What: tmpfs
Significado: El montaje lo gestiona systemd. Esto importa porque deberías configurarlo con un drop-in, no editando archivos al azar que serán sobrescritos.
Decisión: Usa un override de systemd para las opciones de montaje si quieres que sea persistente y seguro frente a actualizaciones.
Task 9: Comprueba si /tmp es tmpfs (podría serlo, según tus elecciones)
cr0x@server:~$ findmnt /tmp
TARGET SOURCE FSTYPE OPTIONS
/tmp /dev/nvme0n1p2[/tmp] ext4 rw,relatime
Significado: Aquí /tmp está en disco. Si fuera tmpfs, verías tmpfs y opciones de montaje incluyendo size=.
Decisión: Si tu incidente está en /tmp y está en disco, tmpfs no es el villano.
Task 10: Mide el uso de memoria compartida por proceso (cuando /dev/shm es el punto caliente)
cr0x@server:~$ ps -eo pid,comm,rss,shr,%mem --sort=-shr | head -n 8
PID COMMAND RSS SHR %MEM
18422 python3 6210040 2101240 38.0
19211 python3 1180240 922120 7.2
1123 gnome-shell 412320 210800 2.5
Significado: SHR es grande para el proceso python. No es un proxy perfecto, pero da una señal direccional buena.
Decisión: Enfócate en ese servicio. Si es un problema de flota, ahora tienes una consulta rápida “top talkers” para automatizar.
Task 11: Revisa límites de cgroups de memoria (común en contenedores y servicios systemd)
cr0x@server:~$ systemctl show myapp.service -p MemoryMax -p MemoryHigh -p MemoryCurrent
MemoryMax=infinity
MemoryHigh=infinity
MemoryCurrent=9312415744
Significado: El servicio no tiene techo de memoria. Si usa tmpfs intensamente, puede comerse el host.
Decisión: Considera establecer MemoryHigh y MemoryMax para que el servicio sea limitado/terminado antes de que muera todo el nodo.
Task 12: Comprueba si el swap está enmascarando el problema (hasta que explote)
cr0x@server:~$ swapon --show
NAME TYPE SIZE USED PRIO
/dev/sda2 partition 4G 3.9G -2
Significado: El swap está casi totalmente usado; el rendimiento será pésimo y tu próxima asignación puede disparar OOM.
Decisión: Detén el crecimiento de tmpfs. Añadir swap puede comprar tiempo, pero no es una solución responsable a largo plazo.
Task 13: Identifica directorios bajo /run que abusan de tmpfs (clásico: archivos runtime descontrolados)
cr0x@server:~$ sudo du -xh --max-depth=1 /run | sort -h | tail -n 8
4.0M /run/systemd
12M /run/udev
64M /run/user
1.2G /run/myapp
1.9G /run
Significado: /run/myapp es enorme. Algo trata el estado de ejecución como almacenamiento.
Decisión: Arregla la app: los directorios de tiempo de ejecución deben estar acotados, rotados o movidos al disco si son grandes.
Task 14: Verifica si tmpfs está limitado por agotamiento de inodos (sí, pasa)
cr0x@server:~$ df -ih /run
Filesystem Inodes IUsed IFree IUse% Mounted on
tmpfs 800K 792K 8.0K 99% /run
Significado: Te has quedado sin inodos en tmpfs. Puedes estar “lleno” aunque haya bytes disponibles.
Decisión: Encuentra y borra el spam de archivos. Si es comportamiento esperado, aumenta el límite de inodos vía opciones de montaje o mueve la carga fuera de tmpfs.
Task 15: Remonta un tmpfs con un tope temporal (mitigación de incidente)
cr0x@server:~$ sudo mount -o remount,size=4G /dev/shm
cr0x@server:~$ findmnt -no TARGET,OPTIONS /dev/shm
/dev/shm rw,nosuid,nodev,size=4G,inode64
Significado: Acabas de fijar un techo estricto en 4G. Las escrituras más allá de eso fallarán con ENOSPC.
Decisión: Haz esto solo cuando entiendas el impacto en las aplicaciones. Es una barandilla, no una cura.
Task 16: Haz persistente el tope con un drop-in de systemd para el montaje
cr0x@server:~$ sudo systemctl edit dev-shm.mount
# editor opens; add:
# [Mount]
# Options=rw,nosuid,nodev,size=4G,mode=1777
cr0x@server:~$ sudo systemctl daemon-reload
cr0x@server:~$ sudo systemctl restart dev-shm.mount
cr0x@server:~$ findmnt -no TARGET,OPTIONS /dev/shm
/dev/shm rw,nosuid,nodev,size=4G,mode=1777,inode64
Significado: El límite sobrevive reinicios. mode=1777 mantiene el comportamiento típico de permisos de memoria compartida.
Decisión: Pasa esto por control de cambios si tu parque incluye apps que dependen de segmentos grandes de memoria compartida (bases de datos, navegadores, pipelines ML).
Cómo tmpfs usa realmente la memoria (y por qué “Size” engaña)
tmpfs guarda el contenido de archivos en páginas de memoria gestionadas por el kernel. Eso suena a “se reserva RAM”, pero la asignación ocurre conforme escribes. El size= que ves en df es un máximo, aplicado como una cuota. No reserva memoria por adelantado.
¿Dónde aparece la memoria? Normalmente en una mezcla de:
- Shmem en
/proc/meminfo(memoria compartida y páginas tmpfs). - Cached puede seguir siendo significativo, porque las páginas tmpfs son objetos parecidos a la caché de páginas, pero contadas de forma distinta que la caché de archivos en dispositivos de bloque.
- Swap, si el sistema está bajo presión y esas páginas son intercambiables.
Ese último punto es donde la gente se engaña. “tmpfs puede paginar” suena reconfortante. En la práctica, si tu tmpfs tiene datos de trabajo calientes y empieza a paginar, tienes:
- una aplicación que esperaba semánticas a velocidad de RAM,
- un kernel que intentará mantenerla viva paginando,
- y un disco haciendo su mejor imitación de una tostadora llorando.
Broma #2: Si tu plan de respuesta es “dejar que tmpfs pagine”, tu disco acaba en el turno de guardia.
tmpfs vs “ramdisk” en Ubuntu
La gente dice “ramdisk” como término genérico. Hay dos cosas muy diferentes:
- tmpfs: dinámico, basado en archivos, puede paginar, usa memoria según sea necesario hasta un límite.
- brd/ramdisk: un dispositivo de bloque en RAM con tamaño fijo (menos común en la operación diaria de Ubuntu).
La mayoría de los “problemas de ramdisk” en Ubuntu 24.04 son problemas de tmpfs: /dev/shm, /run o un tmpfs montado por systemd o por el runtime de contenedores.
Cuando el crecimiento de tmpfs es “normal”
Algunas cargas legítimamente quieren archivos en memoria:
- IPC de alta frecuencia usando SHM POSIX.
- Sistemas de compilación que crean árboles temporales masivos y se benefician de la velocidad de RAM (en builders con mucha memoria).
- Algunos feature stores ML y caches de serving (si están bien dimensionados y acotados).
“Normal” tiene dos propiedades: es limitado y es recuperable. Un tmpfs sin límites es básicamente una fuga de memoria con pasos extra.
Establecer límites seguros sin romper aplicaciones
Tu objetivo no es “hacer tmpfs pequeño”. Tu objetivo es “localizar la falla”. Cuando tmpfs es ilimitado (o efectivamente lo parece), un único componente con comportamiento inesperado puede dejar al host sin recursos. Cuando tmpfs está razonablemente capado, el componente recibe ENOSPC y falla de forma que tu monitorización y la lógica de reintento pueden manejar.
Elige el objetivo correcto para capar
No limites todo al azar. Comienza por los montajes que son tanto intensivos en escritura como fáciles de usar mal:
/dev/shm: frecuentemente se abusa como directorio scratch rápido. También se usa para IPC legítimo; ten cuidado./run: no debería contener datos enormes. Si lo hace, algo anda mal. Caparlo puede ayudar, pero arreglar al escritor importa más.- Montajes tmpfs personalizados creados para caches de aplicación: estos siempre deberían tener tamaño e inodos explícitos.
Reglas empíricas de dimensionado (opinadas)
- /run: usualmente unos cientos de MB bastan en servidores típicos. Si necesitas más de 1–2G, tu diseño de directorios de tiempo de ejecución es cuestionable.
- /dev/shm: dimensiona según el mayor usuario legítimo de memoria compartida. Si no tienes uno, capalo. Topes comunes: 1G–4G en hosts pequeños/medianos.
- tmpfs específico de app: ponle un tamaño que puedas permitirte perder sin pagar con paginación del host. Luego implementa limpieza/TTL en la app.
Prefiere “mover a disco” sobre “hacer tmpfs infinito”
Si una app quiere escribir 20GB de datos “temporales”, eso no es caso de uso de tmpfs. Pónlo en disco. Usa:
/var/tmppara datos temporales que pueden sobrevivir reinicios y pueden ser grandes.- Un directorio dedicado en almacenamiento rápido (NVMe) y cuotas si es necesario.
- Directorios por servicio con ownership y modo creados por systemd (
StateDirectory=,CacheDirectory=,RuntimeDirectory=).
Modo de fallo: ENOSPC es una característica, no un bug
Cuando limitas tmpfs, la app eventualmente verá “No space left on device”. Eso es bueno: es explícito. La alternativa “mala” es “el kernel mató tu base de datos porque un sidecar llenó /dev/shm”.
Tu trabajo es asegurarte de que la app reaccione sensatamente a ENOSPC: retroceder, rotar, purgar caché o fallar rápido con una alerta clara. Si se cae, eso puede ser todavía una mejora frente a la caída del host—dependiendo de tu redundancia.
Perillas de systemd que importan en Ubuntu 24.04
Ubuntu 24.04 es systemd-prioritario. Eso es bueno: la configuración está centralizada y los montajes son unidades con propiedad clara. También es malo si sigues editando /etc/fstab por costumbre y te preguntas por qué tu cambio no aplica a un montaje gestionado por systemd.
Controlar /dev/shm vía dev-shm.mount
/dev/shm suele estar gestionado por dev-shm.mount. Usa un override drop-in para fijar opciones como size=, mode= y potencialmente nr_inodes= si el agotamiento de inodos es un problema real.
Si lo capás, hazlo intencionalmente:
- Sé quiénes usan SHM POSIX (extensiones de PostgreSQL, herramientas basadas en Chromium, algunos patrones JVM IPC, frameworks ML).
- Prueba bajo carga. Los fallos de memoria compartida pueden ser sutiles.
- Despliega gradualmente en la flota. Los topes de tmpfs son el tipo de cambio que “funciona en staging” y luego se enfrenta al tráfico de producción con diferente churn de archivos.
Controlar /run: usualmente arregla el escritor, no el montaje
Que /run esté lleno casi siempre es mala conducta: archivos de log, spools de datos o caches en un directorio de tiempo de ejecución. Puedes capar /run, pero podrías romper el arranque si servicios críticos no pueden crear sockets y archivos PID.
Mejor: identifica el subdirectorio grande (Task 13), luego arregla el servicio. Correcciones comunes:
- Mover artefactos grandes de tiempo de ejecución a
/var/lib/myapp(estado) o/var/cache/myapp(cache). - Usar directivas de unidad systemd:
StateDirectory=,CacheDirectory=,RuntimeDirectory=, y establecerLogsDirectory=si es relevante. - Auditar limpieza en reinicio: archivos de runtime obsoletos pueden acumularse si la app solo hace append.
Protecciones por servicio: MemoryMax, MemoryHigh y compañía
Si el uso de tmpfs lo causa un solo servicio, puedes evitar que se coma todo el host colocándolo en un cgroup de memoria con límites.
Postura práctica:
- MemoryHigh es un umbral de presión; restringe asignaciones y hace que el servicio sienta el dolor primero.
- MemoryMax es el techo duro; cruzarlo puede desencadenar kills.
Esto no “arregla tmpfs”, pero limita el radio de explosión. En producción, el radio de explosión es gran parte del juego.
Contenedores: trampas tmpfs en Docker y Kubernetes
tmpfs se complica dentro de contenedores porque añades al menos dos capas de malentendidos:
- El sistema de archivos del contenedor puede ser overlayfs, y los montajes tmpfs pueden ser inyectados por contenedor.
- La contabilidad de memoria es basada en cgroups; el host y el contenedor pueden discrepar sobre “memoria libre”.
- En Kubernetes,
emptyDirconmedium: Memoryes literalmente tmpfs. No es “disco efímero rápido”. Es RAM.
Docker: los montajes tmpfs necesitan dimensionado explícito
Si usas montajes tmpfs de Docker (p. ej., --tmpfs /tmp), especifica size. De lo contrario permites implícitamente crecimiento restringido en gran parte por límites del host y límites de memoria del contenedor.
Además, recuerda: los límites de memoria del contenedor pueden convertir escrituras a tmpfs en OOM inmediatos dentro del contenedor. Eso es mejor que matar el host, pero sigue siendo un incidente de servicio.
Kubernetes: emptyDir en memoria no es gratis
Kubernetes facilita crear volúmenes en memoria. También es fácil olvidar establecer:
- Requests/limits de memoria para el contenedor.
- Límites de tamaño para el volumen (según políticas y versión del clúster).
- Umbrales de evacuación para que los nodos no queden bloqueados.
El patrón que confío: si usas volúmenes en memoria, trátalos como caches. Acótalos, ponles TTL y acepta que habrá evicción.
Swap, zram y la ilusión “tmpfs se comió la RAM”
Ubuntu 24.04 puede desplegarse con o sin swap, y algunos entornos usan zram. Esto cambia cómo se sienten los incidentes tmpfs:
- Sin swap: el crecimiento de tmpfs golpea OOM más rápido, pero al menos no pasas 30 minutos en swap-muerte antes de que ocurra.
- Swap en disco: las páginas tmpfs pueden paginarse. Esto puede mantener el box “vivo” mientras se vuelve inútilmente lento.
- zram: swap en RAM comprimida puede absorber algo de presión. También puede ocultar la tmpfs descontrolada por más tiempo y luego fallar de manera más confusa.
Si tu tmpfs se usa para archivos de trabajo “calientes”, paginarlos es contraproducente. Esa carga debería estar en disco (disco rápido, pero disco) o explícitamente acotada para que no fuerce paginación.
Regla operativa: si el uso de swap sube a la par con el crecimiento de tmpfs, no estás “ahorrando memoria”. Estás pagando intereses por ella.
Tres micro-historias corporativas desde el terreno
Micro-historia 1: El incidente causado por una suposición equivocada
Tenían una flota de servidores Ubuntu ejecutando un servicio de procesamiento de datos. Un integrante leyó que tmpfs “usa hasta la mitad de la RAM”, vio /dev/shm mostrando un Size grande en df y asumió que estaba preasignado. Abrieron un ticket: “Ubuntu está perdiendo 32GB en /dev/shm”.
El “arreglo” fue rápido y confiado: capar /dev/shm a algo pequeño en toda la flota. Se desplegó en horario laboral porque el cambio parecía inofensivo. La siguiente ejecución por lotes comenzó, y un componente que usaba memoria compartida POSIX empezó a fallar intermitentemente. No se caía limpiamente—simplemente corrompía su propio estado de trabajo cuando las asignaciones de memoria compartida fallaban a mitad de vuelo.
El incidente no fue que se usara memoria compartida. Fue la suposición de que el Size mostrado era memoria desperdiciada. En realidad, el servicio había estado usando memoria compartida responsablemente en condiciones normales; el tope lo rompió al hacer fallar cargas legítimas.
Se recuperaron revirtiendo el tope, luego hicieron el trabajo aburrido: medir el Shmem real durante la carga pico, identificar la marca alta legítima y fijar un tope justo por encima. Luego añadieron manejo a nivel de app para fallos de asignación para evitar corrupción silenciosa.
La lección del postmortem fue simple: df muestra límites, no asignaciones. Los incidentes de memoria rara vez se resuelven con una sola “optimización global”.
Micro-historia 2: La optimización que salió mal
Otra organización tenía una API sensible a latencias. Alguien decidió acelerar el manejo de peticiones moviendo una caché JSON y un directorio temporal de renderizado a tmpfs. Funcionó en benchmarks: p99 mejoró, los discos se tranquilizaron, las gráficas se veían hermosas.
Entonces una campaña de marketing golpeó, el tráfico se triplicó y la caché pasó de “útil” a “sin límites”. La tmpfs se llenó. La caché no tenía evicción; tenía esperanza. La presión de memoria subió, el swap se llenó y el nodo se convirtió en una falla a cámara lenta donde los health checks fallaban y el orquestador empezó a reemplazar instancias.
La ironía: la ola de reemplazos amplificó el problema. Las instancias recién arrancadas rellenaron la caché, llenando tmpfs otra vez, lo que causó un brownout rodante en vez de un fallo limpio. El equipo pasó horas persiguiendo “problemas de red” porque todo se volvió lento a la vez.
La solución real fue poco sexy: mover la caché al disco (SSD local rápido), implementar evicción LRU y fijar límites explícitos de tamaño. Dejaron un tmpfs pequeño para datos verdaderamente calientes—kilobytes a pocos megabytes—no gigabytes. El rendimiento se mantuvo y el modo de fallo pasó a ser manejable.
Micro-historia 3: La práctica aburrida pero correcta que salvó el día
Un servicio adyacente a pagos corría en Ubuntu con requisitos estrictos de fiabilidad. El equipo tenía una checklist para cada clase de nodo: verificar opciones de montaje tmpfs, capar /dev/shm según la carga y fijar límites de memoria por servicio con systemd. Era higiene que nadie tuitea.
Una tarde, una nueva versión de un worker empezó a escribir artefactos de debug en /run. Un flag de feature se aplicó mal; en vez de muestrear 1% del tráfico, muestreaba casi todo. Los archivos eran pequeños, pero la cantidad fue enorme y el consumo de inodos se disparó.
En un setup menos disciplinado, /run habría llenado y hubiera tumbado funciones básicas del sistema. En sus nodos, /run tenía monitorización de uso de inodos y el servicio tenía un techo de memoria. El servicio empezó a fallar, saltaron alertas claras de “presión de inodos en /run” y el host se mantuvo lo bastante sano para que los operadores entraran sin pelear contra un sistema medio muerto.
La remediación fue rápida: desactivar el flag, desplegar un parche y purgar el directorio de runtime. Sin corte en cascada. La práctica aburrida no evitó el bug, pero sí lo contuvo.
Errores comunes: síntoma → causa raíz → arreglo
1) “df muestra /dev/shm grande, así que Ubuntu está desperdiciando RAM”
Síntoma: df -h muestra tmpfs Size = la mitad (o más) de la RAM.
Causa raíz: Confundir el tamaño máximo de tmpfs con la asignación real.
Arreglo: Comprueba Used en df y Shmem en /proc/meminfo. Actúa solo si el uso crece.
2) “El host hace OOM, pero du muestra tmpfs pequeño”
Síntoma: df reporta un tmpfs lleno; du no suma.
Causa raíz: Archivos borrados pero abiertos en tmpfs.
Arreglo: Usa lsof +L1, reinicia el proceso culpable y confirma que el espacio se libera.
3) “Capatamos /dev/shm y ahora apps aleatorias fallan”
Síntoma: Fallos intermitentes, errores IPC, crashes extraños tras el cambio.
Causa raíz: Un consumidor legítimo de SHM POSIX excedió el nuevo tope.
Arreglo: Mide el uso pico de SHM, sube el tope apropiadamente o reconfigura la app para usar almacenamiento en disco cuando sea aceptable.
4) “/run se llenó y la máquina se comportó como embrujada”
Síntoma: Servicios no arrancan, archivos PID faltan, sockets fallan, inicios de sesión raros.
Causa raíz: Algo escribió datos grandes o demasiados archivos en /run (bytes o inodos).
Arreglo: Identifica subdirs grandes (du -x, df -ih), mueve el escritor a /var/lib o /var/cache, añade limpieza y rotación.
5) “Pusimos /tmp en tmpfs para velocidad y ahora compilaciones fallan”
Síntoma: Compiladores y herramientas de paquetes fallan con ENOSPC u OOM durante builds grandes.
Causa raíz: Los archivos temporales de build son grandes; tmpfs fue una mala elección para la RAM disponible.
Arreglo: Mantén /tmp en disco, o proporciona un camino de disco rápido dedicado y apunta las herramientas de build a él.
6) “Pods de Kubernetes OOMKilled tras cambiar emptyDir a memoria”
Síntoma: Pods se reinician bajo carga; la memoria del nodo se ve ajustada.
Causa raíz: emptyDir con medium: Memory consume memoria contada contra los límites del pod/contenedor.
Arreglo: Establece requests/limits de memoria, capa tmpfs o vuelve a emptyDir en disco y optimiza la carga.
7) “Remonté tmpfs más pequeño y ahora las escrituras fallan”
Síntoma: ENOSPC inmediato tras la mitigación.
Causa raíz: El working set ya excedía tu nuevo límite; remontar no reduce mágicamente las páginas usadas.
Arreglo: Primero reduce el uso (borra archivos, reinicia procesos que mantienen archivos borrados), luego remonta con un límite menor.
Listas de verificación / plan paso a paso
Contención del incidente (15–30 minutos)
- Localiza el montaje: ejecuta
df -hTy encuentra el tmpfs con alto Used%. - Confirma presión de memoria: revisa
/proc/meminfoporMemAvailable,Shmemy swap. - Encuentra el directorio grande:
du -xh --max-depth=1en ese montaje. - Encuentra el escritor:
lsof +Dolsof/fuserdirigidos. - Comprueba archivos borrados pero abiertos:
lsof +L1. - Detén el crecimiento: pausa el job, reduce la escala del deployment o reinicia el servicio que mantiene espacio.
- Barandilla si hace falta: remonta tmpfs con un tope temporal
size=solo si puedes tolerar ENOSPC. - Estabiliza: asegúrate de que el swap no esté al máximo; si lo está, considera reiniciar a los peores culpables para recuperar memoria rápido.
Arreglo permanente (mismo día)
- Decide qué datos pertenecen en tmpfs: solo IPC y caches calientes pequeños.
- Mueve scratch grande al disco: cambia la configuración de la app para usar
/var/tmpo una ruta dedicada. - Establece límites tmpfs explícitos: usa overrides de systemd para
/dev/shmo tus montajes personalizados. - Añade limpieza: TTL, evicción por tamaño o rotación. “Lo limpiaremos después” es cómo tmpfs se vuelve un arma.
- Añade monitorización: alerta sobre Used% de tmpfs y uso de inodos (
df -ih), más MemAvailable y swap del host. - Añade controles de radio de explosión:
MemoryHigh/MemoryMaxde systemd para los peores culpables.
Endurecimiento de la flota (próximo sprint)
- Estandariza políticas de montaje: por clase de nodo, define topes tmpfs y documenta qué apps necesitan más memoria compartida.
- Política de contenedores: exige límites de memoria explícitos y prohíbe volúmenes en memoria sin límites para datos que no sean cache.
- Prueba modos de fallo: fuerza ENOSPC en staging y asegúrate de que las apps degraden de forma predecible (no corrompan, no se cuelguen).
- Audita uso de /run: los directorios de runtime no deben contener datasets grandes; haz cumplir en code review y empaquetado.
FAQ
1) ¿tmpfs realmente usa RAM o solo “memoria virtual”?
Usa páginas de memoria reales a medida que se escribe. Esas páginas pueden paginarse a swap bajo presión, pero igual cuentan en la contabilidad de memoria del sistema.
2) ¿Por qué tmpfs muestra un Size igual a la mitad de mi RAM?
Ese es un máximo por defecto. No está preasignado. La columna Used es la que importa, junto con Shmem en /proc/meminfo.
3) ¿Debería montar /tmp como tmpfs en Ubuntu 24.04?
Sólo si lo has dimensionado y probado con tus cargas. En servidores de propósito general, mantener /tmp en disco es más seguro. Si usas tmpfs, capalo y planifica para ENOSPC.
4) ¿Puedo simplemente aumentar el swap para solucionar picos de tmpfs?
Aumentar swap compra tiempo, no corrección. También puede convertir un fallo limpio en un brownout lento. Arregla al escritor y pon límites.
5) ¿Cuál es el lugar más seguro para poner archivos temporales grandes?
Usa disco: /var/tmp o un directorio dedicado en almacenamiento rápido. Si necesitas rendimiento, usa NVMe y reserva tmpfs para datos pequeños y calientes.
6) ¿Por qué “df dice lleno” pero “du dice pequeño” en tmpfs?
Normalmente archivos borrados pero abiertos. Las entradas de directorio desaparecieron, pero el proceso aún mantiene el descriptor. Usa lsof +L1 y reinicia el proceso.
7) Si capeo /dev/shm, ¿romperé cosas?
Posiblemente. Algunos programas usan legítimamente memoria compartida. Mide el uso real en pico, fija un tope por encima y prueba. Elegir al azar “512M parece bien” es la ruta rápida a trabajo de fin de semana.
8) ¿Cómo capeo tmpfs persistentemente en Ubuntu 24.04?
Para montajes gestionados por systemd como /dev/shm, crea un override drop-in vía systemctl edit dev-shm.mount y fija Options=...size=....
9) ¿/run se supone que sea tmpfs?
Sí. Es para estado de ejecución que no debe persistir entre reinicios. Si está enorme, algo lo está abusando.
10) ¿tmpfs también tiene límites de inodos?
Sí. Puedes quedarte sin inodos antes que sin bytes. Comprueba con df -ih. Si el uso de inodos es alto, encuentra el spam de archivos y arregla el generador.
Próximos pasos que puedes hacer hoy
tmpfs no es un bug en Ubuntu 24.04. Es una herramienta afilada. Las fallas ocurren cuando la tratas como espacio scratch infinito y olvidas que comparte la misma RAM que tus aplicaciones necesitan para respirar.
Haz esto en orden:
- Identifica qué tmpfs está creciendo (
df -hT), luego verifica que sea presión real (/proc/meminfo). - Encuentra el directorio y proceso responsable (
du,lsof,lsof +L1). - Mueve datos temporales grandes al disco y capa tmpfs donde el abuso sea probable (especialmente
/dev/shm), usando overrides de systemd. - Añade monitorización de bytes e inodos de tmpfs, más memoria/swap del host, para que esto sea una página que ves venir—no una caída misteriosa.
- Contén el radio de explosión con límites de memoria por servicio donde tenga sentido.
Si no haces nada más: capea intencionalmente los montajes tmpfs riesgosos y haz que las aplicaciones manejen ENOSPC como adultos. Tu kernel te lo agradecerá por no elegir víctimas a las 3 a.m.