Guía
Puerto 3306 de MySQL ocupado: Homebrew, MAMP, Docker y la confusión del socket
14 de septiembre de 20263 min de lectura
Un 3306 ocupado casi nunca es un proceso misterioso. Es un segundo MySQL que olvidaste que instalaste, y la pregunta útil es cuál, porque la forma de detenerlo depende por completo de qué lo levantó.
Primero, encuentra al dueño
lsof -i :3306
El nombre del comando te apunta al caso:
mysqlden un Mac con Homebrew, normalmente un servicio de brew.mysqldcon MAMP o XAMPP instalado, el MySQL propio de ese paquete.com.dockeodocker-proxy, un contenedor.
Esto importa más que el PID, porque matar el proceso no funciona cuando un gestor es el dueño. Homebrew registra un trabajo de launchd cuyo propósito completo es reiniciar lo que se detiene. Lo matas y vuelve, lo que parece que el kill falló en silencio.
Homebrew
brew services list
brew services stop mysql
Usa el nombre exacto que imprime la lista. Si tienes instalados mysql y mysql@8.0, son servicios separados y los dos quieren el 3306.
MAMP y XAMPP
Detén MySQL desde el panel de control, no desde una terminal. Estos paquetes gestionan sus propios procesos y los reinician según su propio criterio.
Vale la pena saberlo: MAMP históricamente usa el 8889 para MySQL en vez del 3306, así que si tienes MAMP instalado y un conflicto en el 3306, probablemente no es MAMP. Revisa el panel antes de asumir.
Docker
docker ps --filter "publish=3306"
docker stop <contenedor>
Matar docker-proxy no logra nada. Docker vuelve a publicar el puerto, y un contenedor con política de reinicio vuelve incluso después de reiniciar el equipo.
El error de socket es otro problema
Este se confunde con los conflictos de puerto constantemente, y no es lo mismo:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock'
Eso no es “el puerto está ocupado”. Normalmente significa que MySQL no está corriendo, o que está corriendo en un lugar que tu cliente no alcanza por un socket Unix.
La razón por la que confunde es que mysql en macOS se conecta por un archivo de socket y no por TCP cuando le das localhost. Así que el cliente nunca toca el puerto 3306, y un listado de puertos no te dice nada sobre por qué falló.
Para forzarlo por TCP, que es la forma más rápida de distinguir los dos problemas:
mysql -h 127.0.0.1 -P 3306 -u root -p
localhost significa el socket, 127.0.0.1 significa TCP. Si la versión por TCP conecta y la del socket no, tu problema es la ruta del socket, no el puerto.
Correr dos versiones a propósito
Mantener un MySQL antiguo para un proyecto heredado es común y está bien, siempre que los dos no quieran el 3306. Dale al segundo otro puerto en su configuración:
[mysqld]
port = 3307
Y conéctate con -P 3307. A propósito es mejor que por accidente, y dejas de tener que detener uno para levantar el otro.
Cuándo sí corresponde matarlo
Si levantaste mysqld a mano y no por un gestor, terminarlo normalmente es lo correcto:
lsof -ti :3306 | xargs kill
Un kill normal manda SIGTERM y MySQL cierra limpio. Evita kill -9 en una base de datos. Un apagado sucio significa trabajo de recuperación en el próximo arranque, y en el peor de los casos es así como aparece la corrupción.
Relacionado: el mismo patrón en el 5432 de PostgreSQL, donde el dueño también importa más que el PID.
Bosun muestra qué ocupa cada puerto desde la barra de menú, con los contenedores de Docker nombrados como corresponde, así puedes ver en qué caso estás antes de decidir qué detener. macOS 14 o posterior, prueba de 14 días.
Ve esto en vez de escribirlo
Bosun vive en tu barra de menú y muestra cada puerto abierto en tu Mac, en vivo, mapeado al proceso detrás. Un clic para matarlo, SIGTERM primero. Útil la primera vez que pasa esto. Realmente útil la quinta vez que pasa en una misma tarde.