Guía de despliegue

Alojar MySQL en un VPS: guía completa

Desplegar en un VPS Cloud →

Tutorial

Alojar MySQL en un VPS: guía completa

Bases de datos9 min de lectura6 pasos

MySQL hace funcionar una inmensa parte de la web: WordPress, Magento, Laravel y miles de aplicaciones PHP. Autoalojarlo en un VPS le ofrece un rendimiento dedicado, un control completo de la configuración de InnoDB y unos costes controlados, lejos de los límites de los alojamientos compartidos.

Contenido· Por qué autoalojar MySQL en un VPS1/9
  1. 01Por qué autoalojar MySQL en un VPS
  2. 02Los beneficios concretos de un MySQL autoalojado
  3. 03Requisitos concretos antes de empezar
  4. 04Desplegar MySQL 8.4 con Docker en producción
  5. 05Seguridad: los gestos imprescindibles
  6. 06Optimizar el rendimiento de InnoDB
  7. 07Acceso remoto seguro: el túnel SSH
  8. 08Copias de seguridad automatizadas: estrategia completa
  9. 09Resolución de errores: 5 errores frecuentes

Por qué autoalojar MySQL en un VPS

En un alojamiento compartido, su base MySQL comparte la RAM y la I/O con cientos de otros sitios, y usted no puede modificar ni innodb_buffer_pool_size, ni los modos SQL, ni la versión del motor. En un VPS dispone de recursos dedicados, elige entre MySQL 8.4 LTS y MariaDB, y accede al archivo my.cnf para optimizar cada parámetro. Es la opción adecuada para una tienda de comercio electrónico, un CMS con mucho tráfico o una API que necesita transacciones rápidas y un buffer pool generoso. También gestiona sus propios usuarios, sus grants a nivel de tabla y su estrategia de replicación maestro-esclavo.

Los beneficios concretos de un MySQL autoalojado

  • Recursos dedicados: todo el buffer pool de InnoDB para sus datos, sin vecinos ruidosos.
  • Acceso completo a my.cnf: innodb_buffer_pool_size, max_connections, modos SQL estrictos.
  • Elección del motor: MySQL 8.4 LTS para la estabilidad a largo plazo, o MariaDB por sus motores adicionales.
  • Replicación maestro-esclavo configurable para derivar las lecturas o para la recuperación ante incidentes.
  • Copias de seguridad lógicas (mysqldump) o físicas (xtrabackup) según su volumen.
  • Sin cuota arbitraria sobre el tamaño de las bases de datos ni sobre el número de conexiones simultáneas.

Requisitos concretos antes de empezar

Versión recomendada: MySQL 8.4 LTS (versión actual: 8.4.11), la rama LTS oficial, con soporte hasta 2032. Evite MySQL 8.0 en instalaciones nuevas: el soporte activo termina en 2025.

RAM: 1 GB como mínimo para un entorno de prueba o un blog ligero. Planifique 2 GB para un WordPress o una pequeña aplicación en producción, y 4 a 8 GB para Magento, PrestaShop o una API de alto tráfico. La regla base: asigne el 60-70% de la RAM disponible a innodb_buffer_pool_size.

CPU: 2 vCPU cubren la mayoría de los casos. Escale a 4 vCPU cuando las consultas sean frecuentes o active la replicación.

Almacenamiento: 30 GB de SSD NVMe como punto de partida; reserve espacio adicional para los binlogs si activa la replicación o la restauración point-in-time.

Puertos: MySQL escucha por defecto en el puerto 3306. Este puerto nunca debe estar abierto en la interfaz pública. El parámetro bind-address en my.cnf debe apuntar a 127.0.0.1 o a la dirección de red privada, no a 0.0.0.0.

Sistema: Ubuntu 22.04 o 24.04 LTS, Docker y Docker Compose v2, un volumen persistente dedicado.

Desplegar MySQL 8.4 con Docker en producción

  1. Asegurar el VPS

    Actualice el sistema (apt update && apt upgrade -y), instale Docker, desactive la conexión SSH por contraseña en favor de las claves (PasswordAuthentication no en sshd_config) y cierre el puerto 3306 hacia el exterior:

    ufw deny 3306
    ufw allow OpenSSH
    ufw enable

    MySQL solo debe escuchar en la red interna del contenedor Docker.

  2. Definir el docker-compose.yml

    Declare un servicio mysql:8.4, monte un volumen en /var/lib/mysql y pase las variables de entorno mediante un archivo .env:

    # .env
    MYSQL_ROOT_PASSWORD=contraseña_root_fuerte
    MYSQL_DATABASE=mi_base
    MYSQL_USER=app_user
    MYSQL_PASSWORD=contraseña_app
    # docker-compose.yml
    services:
      mysql:
        image: mysql:8.4
        restart: unless-stopped
        env_file: .env
        volumes:
          - mysql_data:/var/lib/mysql
          - ./conf.d:/etc/mysql/conf.d:ro
        ports:
          - "127.0.0.1:3306:3306"
    volumes:
      mysql_data:

    Nótese el bind en 127.0.0.1 en ports: el puerto solo será accesible desde el propio VPS.

  3. Crear una base de datos y un usuario dedicado

    Tras docker compose up -d, conéctese al contenedor:

    docker exec -it mysql mysql -u root -p

    Cree la base de datos y el usuario de aplicación:

    CREATE DATABASE mi_base CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    CREATE USER 'app_user'@'%' IDENTIFIED BY 'contraseña_app';
    GRANT SELECT, INSERT, UPDATE, DELETE ON mi_base.* TO 'app_user'@'%';
    FLUSH PRIVILEGES;

    Limite los grants al mínimo necesario: nunca use GRANT ALL PRIVILEGES para una cuenta de aplicación.

  4. Endurecer la instalación

    Ejecute el equivalente de mysql_secure_installation manualmente:

    -- Eliminar usuarios anónimos
    DELETE FROM mysql.user WHERE User='';
    -- Prohibir el inicio de sesión root remoto
    DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
    -- Eliminar la base de datos de prueba
    DROP DATABASE IF EXISTS test;
    FLUSH PRIVILEGES;

    No utilice nunca la cuenta root desde su aplicación. Verifique que bind-address = 127.0.0.1 esté presente en su conf.d/custom.cnf.

  5. Exponer en TLS si es necesario

    Si una aplicación externa debe conectarse de forma remota, nunca exponga el puerto 3306 directamente en Internet. Utilice un túnel SSH:

    ssh -L 3306:127.0.0.1:3306 [email protected] -N

    Conecte su cliente MySQL a 127.0.0.1:3306 — el tráfico viaja cifrado por SSH. Para conexiones permanentes entre VPS, configure MySQL para escuchar solo en la interfaz de red privada y permita solo la IP del servidor de aplicación en UFW.

  6. Planificar las copias de seguridad

    Programe un mysqldump --single-transaction diario mediante cron para obtener dumps coherentes sin bloqueo:

    # /etc/cron.d/mysql-backup
    0 2 * * * root docker exec mysql mysqldump -u root -p"${MYSQL_ROOT_PASSWORD}" --single-transaction --all-databases | gzip > /backups/mysql-$(date +\%Y\%m\%d).sql.gz

    Conserve al menos 7 días de retención y archive los dumps fuera del VPS. Pruebe la restauración una vez al mes: un backup no probado no es un backup. Para bases de datos grandes, pase a Percona XtraBackup.

Seguridad: los gestos imprescindibles

mysql_secure_installation en un contenedor. La imagen Docker oficial no lo ejecuta automáticamente: hágalo manualmente tras el primer arranque (paso 4 anterior).

Eliminación del root remoto. La cuenta root solo debe ser accesible desde localhost. Una cuenta root accesible desde % es la primera vulnerabilidad explotada durante un escaneo de puertos.

Usuario dedicado por aplicación. Cada aplicación conectada recibe su propia cuenta MySQL con derechos restringidos a su propia base de datos. Una aplicación comprometida no podrá leer ni modificar los datos de las demás.

UFW + bind-address. La doble protección es obligatoria: ufw deny 3306 a nivel del firewall del sistema, y bind-address = 127.0.0.1 en my.cnf del lado de MySQL. Uno no sustituye al otro.

Contraseñas fuertes. Use caching_sha2_password (plugin por defecto en MySQL 8.x) y contraseñas generadas (mínimo 20 caracteres). Guárdelas en secretos gestionados, nunca en el código fuente.

Optimizar el rendimiento de InnoDB

Estos parámetros se añaden en su archivo conf.d/custom.cnf montado en solo lectura en el contenedor.

innodb_buffer_pool_size: el parámetro más importante. Configúrelo al 60-70% de la RAM del VPS. En un VPS de 4 GB, eso supone entre 2,5 y 2,8 GB. Demasiado pequeño = lecturas de disco frecuentes; demasiado grande = swap.

innodb_flush_log_at_trx_commit: el valor 2 ofrece un buen equilibrio rendimiento/durabilidad para la mayoría de aplicaciones. El valor 1 (por defecto) es más seguro pero más lento; el 0 es el más rápido pero arriesgado en caso de fallo.

max_connections: ajústelo según su carga real. El valor por defecto (151) suele ser demasiado bajo para una aplicación de alto tráfico. Cada conexión consume ~1 MB de RAM.

slow_query_log: actívelo con slow_query_log = 1 y long_query_time = 1 para capturar todas las consultas que superen 1 segundo. Es su primera herramienta de optimización.

Ejemplo de custom.cnf para un VPS de 4 GB:

[mysqld]
innodb_buffer_pool_size = 2G
innodb_flush_log_at_trx_commit = 2
max_connections = 200
slow_query_log = 1
long_query_time = 1
log_bin = /var/lib/mysql/binlog
binlog_format = ROW

Acceso remoto seguro: el túnel SSH

Exponer el puerto 3306 en la interfaz pública de su VPS es un grave error de seguridad: los escáneres automáticos intentan ataques de fuerza bruta sobre este puerto continuamente. La buena práctica es el túnel SSH.

Conexión puntual desde su equipo:

ssh -L 3306:127.0.0.1:3306 [email protected] -N

Abra su cliente MySQL sobre 127.0.0.1:3306 — el tráfico viaja cifrado por SSH.

Conexión permanente entre VPS: si su aplicación corre en un segundo VPS de la misma red privada, configure MySQL para escuchar solo en la dirección de red privada (bind-address = 10.0.0.X) y permita solo la IP del servidor de aplicación en UFW:

ufw allow from 10.0.0.5 to any port 3306

A evitar: bind-address = 0.0.0.0 sin firewall, puerto 3306 abierto públicamente, o cuentas MySQL con Host='%' sin restricción de IP.

Copias de seguridad automatizadas: estrategia completa

Una estrategia de copia de seguridad MySQL se apoya en dos niveles complementarios.

Nivel 1 — dump lógico diario (mysqldump): consistente gracias a --single-transaction (sin bloqueo de lectura en InnoDB), portable entre versiones. Adecuado para bases de hasta unas decenas de GB.

Nivel 2 — binlogs para restauración point-in-time: active log_bin desde el principio. Combinados con un dump completo semanal, los binlogs permiten reproducir cada transacción hasta el segundo anterior a un incidente.

Para bases grandes — Percona XtraBackup: copia de seguridad física sin interrumpir el servicio, restauración rápida (copia de archivos en lugar de re-ejecución SQL).

Retención y archivado: no almacene las copias de seguridad únicamente en el VPS. Un VPS corrupto se lleva sus propios backups. Exporte a almacenamiento de objetos o a un servidor SFTP remoto, y conserve al menos 7 días.

Probar la restauración: un backup no probado no es un backup. Planifique una prueba mensual: restaure en un contenedor temporal y verifique que sus datos están intactos.

# Restauración desde un dump comprimido
zcat /backups/mysql-20260917.sql.gz | docker exec -i mysql mysql -u root -p

Resolución de errores: 5 errores frecuentes

Access denied for user 'app'@'...'
La cuenta existe pero el Host no coincide. MySQL verifica la IP de origen exacta. Si la aplicación se conecta desde un contenedor Docker, la IP de origen puede ser 172.17.0.x y no localhost. Compruebe con:

SELECT User, Host FROM mysql.user;

Solución: cree la cuenta con 'app_user'@'%' (o la IP exacta del contenedor) y ejecute FLUSH PRIVILEGES.

Too many connections
Se ha alcanzado max_connections. Compruebe el número de conexiones activas:

SHOW STATUS LIKE 'Threads_connected';

Aumente max_connections en custom.cnf y verifique que su aplicación usa un pool de conexiones.

Table '...' is marked as crashed
Corrupción de InnoDB, a menudo tras un cierre brusco. Intente la reparación:

mysqlcheck -u root -p --auto-repair --all-databases

Si la tabla no puede repararse, restaure desde su último backup consistente.

Can't connect to MySQL server on '127.0.0.1' (111)
MySQL no está escuchando en la interfaz esperada. Compruebe bind-address en custom.cnf y reinicie el contenedor. Verifique también que el contenedor está en marcha con docker compose ps.

ERROR 1819 (HY000): Your password does not satisfy the current policy
El plugin de validación de contraseñas de MySQL 8 rechaza las contraseñas simples. Use una contraseña de al menos 8 caracteres con mayúsculas, dígitos y símbolos, o establezca validate_password.policy = LOW en custom.cnf solo para entornos de desarrollo.

Active los binlogs (log_bin + binlog_format=ROW) desde el principio: son la clave de la restauración point-in-time. Combinados con un mysqldump completo semanal, permiten reproducir todas las transacciones hasta el segundo anterior a un incidente. Y si monta un esclavo en replicación en un segundo VPS, esos mismos binlogs alimentan la réplica en tiempo real para sus lecturas derivadas.

Aloje su MySQL en un VPS de alto rendimiento

El VPS Cloud de ServOrbit con plantilla Docker lista para usar le ofrece SSD y recursos garantizados para ejecutar MySQL sin concesiones.

¿Necesita ayuda?

Consulte nuestro centro de ayuda y nuestra FAQ, o contacte con nuestro equipo: llamada, WhatsApp o correo electrónico. Soporte en francés, inglés y árabe.

Escribir por WhatsAppse abre en una pestaña nueva