Tutoriales de VPS
Ejecutar MySQL Docker en un VPS de poca memoria (2026): OOM 137, bloqueo de ibdata1 y ajuste de Swap
Para VPS de 1 núcleo y 1G/2G que ejecutan MySQL 8 Docker, se abordan el Exit Code 137, el error 11 de ibdata1 y los reinicios del contenedor, con pasos de diagnóstico y ajuste de dmesg, lsof, límites de memoria de Docker, buffer pool de InnoDB, Performance Schema y Swap.

Lecciones aprendidas: guía completa de diagnóstico y ajuste de MySQL Docker en servidores de poca memoria
Al ejecutar la imagen Docker de MySQL 8.0 en servidores de bajas prestaciones (como servidores en la nube de 1 núcleo y 1G / 1 núcleo y 2G), es frecuente encontrar problemas ocultos como contenedores que se reinician sin fin y archivos de datos bloqueados. Este artículo documenta un proceso real de diagnóstico, desde el fallo por OOM (Exit Code 137) hasta el conflicto de bloqueo de archivo (Error 11), cubriendo la ruta completa de errores y soluciones.
Tras iniciar el contenedor de MySQL, revisa los registros con docker logs -f mysql-docker y verás que se interrumpe en el paso de inicialización de InnoDB:
mysql-docker | ... [System] [MY-013576] [InnoDB] InnoDB initialization has started.
mysql-docker exited with code 137 (restarting)
Exit Code 137 corresponde a la señal de Linux 128 + 9 (SIGKILL), lo que significa que el proceso fue terminado a la fuerza por el OOM Killer (mecanismo de terminación por falta de memoria) del kernel de Linux.
La configuración de arranque predeterminada de MySQL 8.0 exige bastante memoria y preasigna un Buffer Pool grande al inicializar InnoDB. Cuando la memoria disponible del host es insuficiente, el kernel mata directamente el proceso mysqld que más memoria consume para evitar que el sistema se caiga.
Ejecuta el siguiente comando en el host para confirmar si es un problema de memoria:
# 查看宿主机系统内核日志
dmesg -T | grep -i oom
# 查看 Docker 是否记录了 OOM
docker inspect mysql-docker --format='{{.State.OOMKilled}}'
Si la salida muestra true o contiene Killed process ... (mysqld), se confirma que es falta de memoria.
Después de limitar la memoria del contenedor o de matarlo a la fuerza y reiniciarlo, los registros pueden mostrar un nuevo error:
[ERROR] [MY-012574] [InnoDB] Unable to lock ./ibdata1 error: 11
- Error 11 (
EAGAIN / Resource temporarily unavailable): indica que el archivo de datosibdata1ya tiene un Write Lock (bloqueo de escritura) puesto por otro proceso. - Por qué ocurre: cuando el contenedor anterior se cayó de repente por OOM, pueden haber quedado procesos huérfanos que no terminaron de salir; o, aunque el contenedor antiguo se haya detenido, el sistema sigue manteniendo descriptores de archivo sobre el volumen montado a bajo nivel.
Usa lsof para ver quién está ocupando ibdata1:
# 查出占用 ibdata1 的进程
lsof /var/lib/mysql/ibdata1
Ejemplo de salida:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 429692 systemd-coredump 9uW REG 253,1 12582912 31823670 /var/lib/mysql/ibdata1
- Interpretación de las marcas: el
Wen9uWrepresenta Write Lock (bloqueo de escritura), lo que demuestra que el PID429692tiene el archivo en exclusiva. - Explicación del usuario:
systemd-coredumpse debe a que el UID del usuariomysqldentro del contenedor Docker (por ejemplo, 999) se asigna al nombre de cuenta correspondiente en el host, lo que confirma además que se trata de un proceso de contenedor residual del fallo anterior.
Para los dos problemas anteriores, el orden correcto de actuación es: limpiar los procesos de bloqueo residuales -> optimizar la configuración de MySQL -> configurar la memoria del contenedor y el Swap.
Mata a la fuerza el proceso mysqld residual en el host:
# 1. 强杀残留进程
kill -9 429692
# 2. 清理旧容器
docker stop mysql-docker && docker rm mysql-docker
Ejecutar MySQL 8.0 en un entorno de poca memoria obliga a limitar el consumo de memoria. Se recomienda reducir el buffer pool de InnoDB y desactivar Performance Schema:
--innodb-buffer-pool-size=128M(o 256M): reduce el buffer pool a un rango razonable (el valor predeterminado suele ser 1G o más).--performance-schema=OFF: desactiva la monitorización de rendimiento, lo que puede liberar directamente entre 200MB y 400MB de memoria.
docker run -d \
--name mysql-docker \
--restart=always \
--memory=1g \
-v /var/lib/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=your_password \
mysql:8.0 \
--innodb-buffer-pool-size=128M \
--performance-schema=OFF
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: mysql-docker
restart: always
deploy:
resources:
limits:
memory: 1024M # 限制容器最大内存
command:
- --innodb-buffer-pool-size=128M
- --performance-schema=OFF
environment:
MYSQL_ROOT_PASSWORD: your_password
volumes:
- ./data:/var/lib/mysql
Si la memoria física es de solo 1GB, se recomienda añadir 2GB de Swap (partición de intercambio) para evitar que los picos de memoria sean terminados por OOM:
# 创建并启用 2GB Swap 文件
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# 永久生效(追加到 /etc/fstab)
echo '/swapfile swap swap defaults 0 0' | sudo tee -a /etc/fstab
- Código de salida 137 = el kernel lo mata por falta de memoria (OOM); al diagnosticar, revisa primero
dmesg. - Error 11 (Unable to lock) = proceso residual o bloqueo no liberado; usa primero
lsofpara encontrar el PID y matarlo a la fuerza. - Configuración imprescindible en servidores de bajas prestaciones: reducir
innodb-buffer-pool-size+ desactivarperformance-schema+ activar elSwapdel sistema; las tres medidas juntas permiten que MySQL funcione de forma estable en un servidor en la nube ligero de 1G de memoria.