Guía9 min

shm_size para un agente IA con Chromium: 64MB no alcanza

Resumen

Cómo no crashear Playwright en Docker: shm_size 1gb en Compose, /dev/shm distinto de mem_limit del cgroup. Si omites --shm-size el daemon usa 64m. ipc shareable no es host. Docs Compose y Engine, verificados con curl el 3 de septiembre de 2026.

Docker
Agente con Chromium y /dev/shm dimensionado frente al default 64MB

Qué resuelve

Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.

Chromium usa /dev/shm. En Docker el default es 64MB. Playwright “Target closed” / crashpad. shm_size: 1gb (o --shm-size). Distinto de límites de RAM: eso es cgroup; esto es el tmpfs de shm. Fuentes oficiales consultadas el 3 de septiembre de 2026.

La regla

shm_size: "1gb"

solo en el servicio que lanza browser. El webhook Node puro no lo necesita. Un worker de screenshots sí.

--disable-dev-shm-usage es parche: escribe a /tmp. Si readonly + tmpfs chico, igual explota. Prefiere shm real.

Relación con RAM

1gb shm cuenta hacia presión de memoria. Si mem_limit: 512m y shm 1gb, el cgroup gana: OOM. Sube RAM o baja shm. Mide tabs concurrentes.

cpu no arregla shm.

Tabla

Default 64MB versus 1gb shm

ServicioshmPor qué
agent HTTPdefaultsin browser
agent+playwright1gbChromium
postgresdefaultno usa shm de Chrome
migratedefaultone-shot

Errores comunes

Crash de Chromium por shm

SíntomaCausaFix
Target closed64MBshm_size 1gb
OOM a los 2sshm > mem_limitsube mem o baja shm
Fly sin campocopy yamlmounts / machine size
Workersno Dockerno aplica
disable-dev-shm + read_only/tmp llenoshm o tmpfs mayor

Relación con el resto

Checklist

  • ¿El agente abre Chromium? Si no, skip
  • shm_size ≥ 512mb, típico 1gb
  • mem_limit > shm + Node heap
  • Ensayo: 1 screenshot sin crash
  • No copies 1gb a postgres
  • CI usa el mismo yaml

FAQ

¿Puppeteer igual? Sí. Misma shm.

¿headless shell? Menos, igual 256mb mínimo.

¿K8s? emptyDir medium: Memory sizeLimit.

El ensayo: df -h /dev/shm dentro del container. 64M = mal. 1G = ok. Un page.goto pesado no mata el browser.

No pongas 8gb “por si acaso” en un VPS de 2gb. Techo: tabs × ~100–200MB + margen.

Qué no hacer

No subas shm y dejes 10 browsers en paralelo en 1 CPU. cpu y cola de jobs. No uses privileged para “arreglar shm”.

Un tmpfs en /dev/shm a mano pisa shm_size. Un knob.

--ipc=host comparte shm del host: no en prod compartido.

Techos oficiales (curl 2026-09-03)

Compose shm_size (spec de servicios, hoy): configura el tamaño de memoria compartida — la partición /dev/shm en Linux — que el contenedor del servicio puede usar. Se expresa como byte value (entero o string tipo 1gb). No hay default escrito en esa página; el default lo pone el runtime.

docker run --shm-size (Engine, resource constraints, hoy):

  • Formato <number><unit>. number mayor que 0.
  • Unidad opcional: b, k, m, g. Sin unidad = bytes.
  • Si omites el tamaño, el sistema usa 64m.

CLI docker container run lista --shm-size como “Size of /dev/shm”. El 64m no vive en el yaml de Compose; vive en el daemon si no pones el campo.

mem_limit (Compose, hoy): tope de memoria que el contenedor puede allocar, byte value. Si lo pones, debe ser consistente con deploy.resources.limits.memory. mem_reservation es reserva, no tope. mem_swappiness 0–100; 0 apaga swap anónimo; 100 deja todo swappeable; default específico de plataforma. memswap_limit solo tiene sentido si también hay memory: es memoria + swap total. Ejemplo oficial: memory=300m y memswap_limit=1g → 300m RAM + el resto swap. shm de 1gb cuenta hacia esa presión; no es un cgroup aparte.

Resource constraints (Engine, hoy): hard limit = no más de X; soft = usa lo que necesite salvo presión del host. Sufijos b/k/m/g. No documentan /dev/shm en esa página: shm es el flag --shm-size, no -m.

Concurrencia

Un browser por job, cierra el context. 4 tabs × 1gb shm no es 4gb si son secuenciales. Si replicas cada replica tiene su shm.

El ensayo extra: compose exec agent df -h /dev/shm en CI. Assert ≥ 512M.

Imagen de browser

No metas Chromium en el mismo stage que el webhook si el 90% de mensajes no scrapean. profiles browser o un servicio agent-browser con shm. El HTTP queda liviano.

Playwright channel o executablePath de la imagen mcr.microsoft.com/playwright ya trae deps; shm sigue siendo tuyo.

Logs: no imprimas el dump entero de crashpad. json-file con max-size.

pids: Chromium spawnea procesos. pids_limit 64 puede matar tabs; sube si usas browser, no copies el valor del webhook.

USER 1000 debe poder escribir shm. Default sí. no-new-privileges no bloquea shm.

Si el crash es “page crashed” a los 30s, mira también stop_grace: un deploy mata el browser a media captura. Drain + shm juntos.

ipc y tmpfs: no son shm_size

Compose ipc (hoy): modo de aislamiento IPC.

  • shareable: namespace IPC propio, compartible con otros contenedores.
  • service:{name}: se une al IPC shareable de otro servicio.

No hay ipc: host en el ejemplo oficial de Compose. El CLI sí tiene --ipc. Compartir IPC del host (--ipc=host) mezcla semáforos y shm del host con el bot: no en VPS compartido. Si dos sidecars de browser necesitan el mismo /dev/shm, el patrón documentado es shareable + service:agent-browser, no host.

Compose tmpfs (hoy): monta un tmpfs. Lista de paths. Opciones: mode, uid, gid. Ejemplo oficial: /data:mode=755,uid=1009,gid=1009 y /run. Un tmpfs en /dev/shm pisa el tamaño que puso shm_size. Un knob. Si readonly montas /tmp chico y Chromium usa --disable-dev-shm-usage, el crash se mueve a /tmp.

CLI --tmpfs monta un directorio tmpfs; no sustituye --shm-size.

privileged no “arregla” shm

Compose privileged (hoy): el contenedor corre con privilegios elevados. Soporte e impacto específicos de plataforma. Engine: --privileged da todas las caps, acceso a devices del host y relaja AppArmor/SELinux. No agranda /dev/shm. Si un gist de Playwright dice privileged: true para “arreglar Chrome”, es el peor trade. El knob correcto es shm_size.

--device da un device concreto sin privileged. /dev/shm ya es un tmpfs del runtime, no un device que montes a mano.

Fly / Railway / serverless

Fly y Railway no copian el campo shm_size de Compose. El tamaño de Machine/RAM es el techo; el default de shm del runtime puede seguir en 64m. Si el crashpad aparece ahí, mira el equivalente del runtime (docker extra en Fly, o un tmpfs explícito), no copies el yaml. Workers: isolates, sin /dev/shm. Vercel Functions: igual. No traduzcas el campo.

Cómo verificar

docker compose exec agent df -h /dev/shm
docker inspect --format '{{.HostConfig.ShmSize}}' "$(docker compose ps -q agent)"

df en 64M = el yaml no se aplicó (servicio mal nombrado, override, docker run sin flag). ShmSize del inspect está en bytes: 1073741824 ≈ 1g. CI: un page.goto pesado + screenshot; si “Target closed”, primero shm, después OOM.

Un solo screenshot de prueba en CI vale más que un wiki.

Siguiente paso: si shm está bien y igual OOM, memoria. Sin runtime: curso.