Cómo instalar MySQL en Ubuntu y crear una base de datos (paso a paso)

Aprende a instalar MySQL en Ubuntu y crear una base de datos desde cero: configuración segura, usuario dedicado, permisos y prueba de conexión remota.

MySQL sigue siendo la base de datos relacional más usada en aplicaciones web — Laravel, Django, Rails, Node.js, todos tienen soporte de primera clase. Pero la instalación por defecto en Ubuntu deja cabos sueltos: usuario root sin contraseña fuerte, ausencia de una base de datos para la aplicación y una configuración de red que o bloquea conexiones legítimas o expone el puerto 3306 al mundo.

Esta guía cubre el camino completo en una VPS Ubuntu 24.04 LTS: instalación de los paquetes oficiales, hardening básico con mysql_secure_installation, creación de una base de datos dedicada para tu aplicación, creación de un usuario con los permisos mínimos necesarios y validación de la conexión. Persona objetivo: developer que necesita un MySQL local funcional para correr la aplicación en staging o producción pequeña.

Tiempo estimado de ejecución: 15 a 20 minutos contando los reinicios del servicio y la prueba de conexión desde la aplicación.

Prerrequisitos

Antes de empezar, confirma que tu VPS cumple con el mínimo de abajo. Si todavía no tienes el servidor configurado, el tutorial asume acceso SSH funcional como usuario con sudo.

Prerrequisitos

Ubuntu 24.04 LTS (también funciona en 22.04), acceso SSH con usuario sudo, mínimo 1 GB de RAM (MySQL 8 consume ~400 MB en reposo) y ~500 MB libres en disco para los paquetes y el data directory inicial en /var/lib/mysql.

Versión objetivo MySQL 8.0.x
Puerto por defecto 3306
Data dir /var/lib/mysql
Config principal /etc/mysql/mysql.conf.d/mysqld.cnf

Si todavía necesitas decidir entre correr todo en una VPS o separar la base de datos en un servidor dedicado, la regla práctica es: hasta ~5k usuarios activos/día, MySQL y aplicación en la misma VPS ahorran latencia de red y simplifican el deploy. Por encima de eso, sepáralo en instancias dedicadas.

Instalación de MySQL Server

Ubuntu 24.04 ya incluye MySQL 8.0 en los repositorios oficiales — no hace falta añadir un PPA externo. Esta sección cubre la instalación de los paquetes, el primer start del servicio y la verificación de que el daemon arrancó correctamente.

01

Actualiza el índice de paquetes de APT:

sudo apt update

Este comando sincroniza la lista local con los repositorios configurados. Saltarse este paso puede resultar en la instalación de una versión antigua o un error “package not found” incluso estando disponible el paquete.

02

Instala el paquete mysql-server:

sudo apt install -y mysql-server

El instalador descarga el servidor, el cliente CLI (mysql) y dependencias como libmysqlclient21. En una VPS con enlace de 100 Mbps, la instalación completa tarda ~30 segundos.

03

Verifica que el servicio está corriendo:

sudo systemctl status mysql

La salida debe mostrar active (running) en verde. Si aparece inactive o failed, ejecuta sudo journalctl -u mysql -n 50 para ver el error — en el 90% de los casos es falta de memoria (matar procesos innecesarios o aumentar swap lo resuelve).

04

Habilita el arranque automático en el boot:

sudo systemctl enable mysql

Sin esto, MySQL no arranca después de reiniciar la VPS — un bug común que solo aparece en el primer reboot de mantenimiento, normalmente meses después.

Hardening inicial con mysql_secure_installation

MySQL recién instalado tiene varias configuraciones inseguras para facilitar el primer uso: base de datos de test pública, usuarios anónimos, root accesible remotamente. El script mysql_secure_installation lo resuelve todo de una vez.

05

Ejecuta el script interactivo:

sudo mysql_secure_installation

Te preguntará secuencialmente:

  • Validate password component: responde Y y elige nivel 2 (STRONG) — fuerza contraseñas con 8+ caracteres, mezcla de mayúsculas/minúsculas, números y símbolos.
  • Set root password: define una contraseña fuerte y guárdala en tu gestor (1Password, Bitwarden, KeePassXC). La vas a necesitar en los próximos pasos.
  • Remove anonymous users: Y.
  • Disallow root login remotely: Y — root solo debe acceder vía socket local.
  • Remove test database: Y.
  • Reload privilege tables: Y.
Sobre el auth_socket de root

En MySQL 8 de Ubuntu, incluso después de definir la contraseña para root, el login sudo mysql sigue funcionando sin prompt — esto es porque root usa auth_socket por defecto, que confía en el UID del sistema operativo. Comportamiento esperado y seguro para administración local.

Creación de la base de datos y del usuario de la aplicación

Nunca uses el usuario root para las conexiones de tu aplicación. La buena práctica es crear una base de datos dedicada y un usuario con permisos restringidos solo a esa base — así, un compromiso de la aplicación no se convierte en un compromiso de todo MySQL.

06

Conéctate a MySQL como root:

sudo mysql

Verás el prompt mysql>. Todos los comandos a continuación son SQL — terminan con punto y coma.

07

Crea la base de datos con encoding UTF-8 completo:

CREATE DATABASE mi_app
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

Usa siempre utf8mb4 (no utf8 puro) — el utf8 del MySQL legacy almacena solo 3 bytes por carácter y se rompe con emojis y caracteres CJK. El utf8mb4_unicode_ci hace un ordenamiento case-insensitive correcto para español, portugués y demás idiomas latinos.

08

Crea el usuario de la aplicación con una contraseña fuerte:

CREATE USER 'app_user'@'localhost'
  IDENTIFIED BY 'ContrasenaFuerteAqui123!@#';

Sustituye ContrasenaFuerteAqui123!@# por una contraseña generada aleatoriamente — openssl rand -base64 24 en la terminal genera una cadena adecuada. El @'localhost' restringe a ese usuario a conexiones locales (vía socket Unix). Si tu aplicación corre en otro servidor, cámbialo por @'10.0.0.5' (IP específica) o en último caso @'%'.

09

Concede permisos solo en la base de datos de la aplicación:

GRANT ALL PRIVILEGES ON mi_app.*
  TO 'app_user'@'localhost';

FLUSH PRIVILEGES;

ALL PRIVILEGES ON mi_app.* da control total dentro de esa base (CREATE, ALTER, INSERT, UPDATE, DELETE, SELECT, INDEX) pero cero acceso a otras bases. Para deploys donde la aplicación no crea tablas en runtime (las migraciones corren por separado), puedes restringir aún más: GRANT SELECT, INSERT, UPDATE, DELETE ON mi_app.* TO ....

10

Sal del prompt de MySQL:

EXIT;
Principio del menor privilegio

En producción crítica, considera tener dos usuarios: uno para migraciones (con ALL PRIVILEGES, usado solo en el deploy) y otro para el runtime de la aplicación (solo SELECT/INSERT/UPDATE/DELETE). Limita el blast radius si una SQL injection se cuela.

Verificación de la configuración

Antes de apuntar la aplicación a la base, valida todo vía CLI. Si falla aquí, fallará allá también — ahorra tiempo de debug.

11

Prueba el login del usuario de la aplicación:

mysql -u app_user -p mi_app

Escribe la contraseña cuando te la pidan. Si entra y muestra mysql> sin error, la autenticación y los permisos están correctos.

12

Crea una tabla de prueba e inserta un registro:

CREATE TABLE health_check (
  id INT AUTO_INCREMENT PRIMARY KEY,
  checked_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO health_check () VALUES ();
SELECT * FROM health_check;

La salida debe mostrar una fila con id=1 y la fecha/hora actual. Si da error de permiso, vuelve al Step 09 y confirma el GRANT.

13

Limpia la tabla de prueba:

DROP TABLE health_check;
EXIT;

Configurando el acceso desde la aplicación

Con la base de datos y el usuario creados, actualiza el archivo de configuración de la aplicación. Los ejemplos cubren los formatos más comunes.

Para Laravel (.env):

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=mi_app
DB_USERNAME=app_user
DB_PASSWORD=ContrasenaFuerteAqui123!@#

Para Node.js con mysql2:

DATABASE_URL=mysql://app_user:ContrasenaFuerteAqui123!%40%[email protected]:3306/mi_app

Fíjate en el URL-encoding de la contraseña: ! se convierte en %21, @ en %40, # en %23. Olvidarlo da un error de autenticación confuso.

Nunca commits contraseñas

Añade .env al .gitignore antes del primer commit. Una contraseña de base de datos filtrada en un repositorio público es una de las causas más comunes de intrusión — los bots rastrean GitHub Search constantemente buscando cadenas tipo DB_PASSWORD=.

Resolución de problemas

Error “Access denied for user ‘app_user’@‘localhost’”

La contraseña tecleada no coincide con el hash almacenado. Causas comunes:

  • Carácter especial sin escapar en el .env (usa comillas dobles alrededor de la contraseña).
  • El usuario fue creado con @'127.0.0.1' pero la aplicación se conecta como localhost (que va por socket) — o viceversa. Crea un usuario para cada host que vaya a conectarse.
  • La contraseña se cambió vía SET PASSWORD en lugar de ALTER USER, que en algunas versiones mantiene el hash antiguo.

Resolución rápida:

ALTER USER 'app_user'@'localhost'
  IDENTIFIED BY 'NuevaContrasena';
FLUSH PRIVILEGES;

MySQL no acepta conexión remota

Por defecto MySQL 8 escucha solo en 127.0.0.1. Para aceptar conexiones de otras VMs en la misma red privada, edita /etc/mysql/mysql.conf.d/mysqld.cnf:

bind-address = 0.0.0.0

Reinicia el servicio y libera el puerto en el firewall solo para las IPs autorizadas:

sudo systemctl restart mysql
sudo ufw allow from 10.0.0.0/24 to any port 3306

Nunca abras 3306 a 0.0.0.0/0 sin TLS configurado.

”Too many connections”

El default de max_connections es 151. Para aplicaciones con un pool grande, súbelo en mysqld.cnf:

max_connections = 500

Cada conexión consume ~12 MB de RAM — calcula según el tamaño de la VPS antes de subir ese número.

Próximos pasos

Con MySQL funcionando, vale la pena considerar:

  • Backup automático: configura mysqldump en cron diario y replica el .sql a un almacenamiento externo (S3, Backblaze B2). Sin backup off-site, un disco corrupto borra todo.
  • Monitorización: instala mysqltuner (sudo apt install mysqltuner) y ejecútalo tras 24h de uso para recomendaciones de tuning basadas en la carga real.
  • TLS para conexiones remotas: si vas a exponer 3306 a otra VPS, configura certificados — MySQL 8 soporta TLS nativamente.
  • Replicación read-only: para aplicaciones con lectura intensiva, configura una réplica secundaria y dirige los SELECTs a ella.
  • VPS dedicada para la base de datos: si tu aplicación creció, separar MySQL en una VPS Hostini dedicada con SSD NVMe y backup snapshot reduce la contención de I/O entre app y base de datos.

Preguntas frecuentes

¿MySQL o MariaDB en Ubuntu 24.04?

Para proyectos nuevos, MySQL 8.0 incorpora funcionalidades más recientes (CTEs, window functions, JSON nativo optimizado) y es lo que la mayoría de los ORMs documenta primero. MariaDB es un fork compatible y tiene un rendimiento ligeramente mejor en algunas cargas de lectura, pero si tu aplicación habla MySQL, quédate con MySQL para evitar incompatibilidades sutiles en funciones nuevas.

¿Puedo conectarme al MySQL desde otro servidor sin exponer el puerto 3306?

Sí, y es el enfoque recomendado. Usa un túnel SSH (ssh -L 3306:localhost:3306 user@servidor) o deja MySQL escuchando en 127.0.0.1 y coloca la aplicación en la misma VPS. Exponer 3306 públicamente solo tiene sentido con un firewall restrictivo por IP y TLS obligatorio.

¿Cuál es la diferencia entre el usuario 'root'@'localhost' y 'root'@'%'?

El sufijo después de @ define desde dónde puede conectarse ese usuario. 'root'@'localhost' solo acepta conexión vía socket local; 'root'@'%' acepta desde cualquier IP. Para root usa siempre localhost. Para la aplicación, crea un usuario con host específico ('app'@'10.0.0.5') o '%' únicamente cuando el firewall ya restringe las IPs.

mysql_secure_installation eliminó el usuario anónimo, pero todavía puedo entrar sin contraseña. ¿Por qué?

Probablemente estás conectado como root en el sistema operativo, y MySQL 8 usa auth_socket por defecto para el root local — no pide contraseña porque confía en el usuario del SO. Para forzar contraseña, modifícalo con ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'contraseña'.

¿Cómo hago un backup de la base de datos después de crearla?

Usa mysqldump -u root -p nombre_de_la_base > backup.sql para un dump SQL completo, o mysqldump --single-transaction para bases InnoDB sin bloquear tablas. Para restaurar: mysql -u root -p nombre_de_la_base < backup.sql. En producción, automatízalo con cron diario y mantén al menos 7 días de retención en un almacenamiento separado.

¿Necesito reiniciar MySQL cada vez que cambio my.cnf?

Sí, los cambios en /etc/mysql/mysql.conf.d/mysqld.cnf requieren sudo systemctl restart mysql para tener efecto. Algunas variables son dinámicas y se pueden ajustar en tiempo de ejecución con SET GLOBAL nombre = valor, pero vuelven al valor por defecto en el próximo restart si no están en el archivo.

Temas:
Próximos pasos Cloud Ryzen con NVMe y protección DDoS siempre activa.Pon en producción en un VPS Hostini →
¿Te resultó útil este tutorial?
Hablar por WhatsApp