--- title: "Cómo migrar un sitio web a otro servidor sin downtime" description: "Migra un sitio con Nginx, PHP y MySQL a un servidor nuevo sin cortes: baja el TTL, copia con rsync, prueba antes de cambiar el DNS y reenvía el tráfico." category: "Migración de Servidores" language: es source: https://cubepath.com/es/docs/migración-de-servidores/migracion-de-sitio-web-sin-tiempo-de-inactividad updated: 2026-09-25 --- # Cómo migrar un sitio web a otro servidor sin downtime Migrar un sitio web a otro servidor sin que los visitantes lo noten depende menos de herramientas especiales que del orden de los pasos: preparar el DNS con antelación, tener el servidor nuevo funcionando y probado antes de tocar nada, y cubrir el periodo en que parte del tráfico sigue llegando al servidor antiguo. En este tutorial migrarás un sitio con Nginx, PHP y MySQL (por ejemplo WordPress) de un servidor Ubuntu a otro con Ubuntu 24.04, copiando archivos con rsync, la base de datos con mysqldump y el certificado TLS, y harás el cambio final con el servidor antiguo reenviando el tráfico al nuevo. ## Requisitos previos - **Servidor antiguo**: el que sirve hoy el sitio, con Nginx, PHP-FPM y MySQL o MariaDB. En esta guía su IP es `old_server_ip`. - **Servidor nuevo**: Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con IP `new_server_ip` y con la misma pila instalada (Nginx, PHP-FPM con las mismas extensiones y MySQL). Si es posible, usa la misma versión mayor de PHP y de la base de datos. - Un usuario no root con `sudo` en ambos servidores (`your_user`) y acceso SSH por clave desde el servidor nuevo al antiguo. - Acceso al panel de tu proveedor DNS para cambiar el registro A de `your_domain`. - El sitio en `/var/www/your_domain` y la base de datos `your_db` con el usuario `your_db_user`. Sustituye estos nombres por los tuyos. Si el sitio no tiene base de datos (un sitio estático), sáltate los pasos 3 y 6. ## Paso 1: Bajar el TTL del DNS El TTL indica cuántos segundos pueden guardar en caché los resolvedores la IP de tu dominio. Si es de 86400 (un día), tras cambiar el registro A algunos visitantes seguirán yendo al servidor antiguo durante un día. Consulta el TTL actual: ```bash dig +noall +answer your_domain A ``` ```output your_domain. 3600 IN A old_server_ip ``` En el panel de tu proveedor DNS, baja el TTL del registro A (y del `www` si existe) a 300 segundos. Hazlo al menos con tanta antelación como el TTL antiguo: si era de 3600, espera como mínimo una hora antes del cambio; si era de 86400, un día. Así, cuando cambies la IP, casi todos los clientes la verán en menos de cinco minutos. ## Paso 2: Copiar los archivos con rsync Desde el servidor nuevo, trae los archivos del sitio. Los archivos suelen pertenecer a `www-data` y algunos pueden no ser legibles por tu usuario, así que rsync necesita ejecutarse con `sudo` en ambos extremos. En el servidor **antiguo**, permite temporalmente que tu usuario ejecute rsync y mysqldump con sudo sin contraseña, ya que se lanzarán por SSH sin terminal: ```bash echo 'your_user ALL=(root) NOPASSWD: /usr/bin/rsync, /usr/bin/mysqldump' | sudo tee /etc/sudoers.d/migracion-rsync sudo chmod 440 /etc/sudoers.d/migracion-rsync sudo visudo -c ``` ```output /etc/sudoers: parsed OK /etc/sudoers.d/migracion-rsync: parsed OK ``` En el servidor **nuevo**, lanza la primera copia. Las opciones `-aHAX` conservan permisos, propietarios, enlaces duros, ACL y atributos extendidos, y `--numeric-ids` mantiene los UID/GID tal cual (`www-data` es el 33 en ambos servidores Ubuntu): ```bash sudo mkdir -p /var/www/your_domain sudo rsync -aHAX --numeric-ids --info=progress2 \ -e "ssh -i /home/your_user/.ssh/id_ed25519" \ --rsync-path="sudo rsync" \ your_user@old_server_ip:/var/www/your_domain/ /var/www/your_domain/ ``` La barra final en `your_domain/` es importante: copia el contenido del directorio y no el directorio dentro de sí mismo. Como el comando se ejecuta con `sudo`, indica la clave SSH de tu usuario con `-i`. Esta primera copia puede tardar; las siguientes solo transfieren los cambios. Comprueba que el tamaño coincide en ambos servidores: ```bash sudo du -sh /var/www/your_domain ``` Copia también el bloque de servidor de Nginx del sitio y revísalo antes de activarlo, porque la ruta del socket de PHP-FPM puede cambiar entre versiones (`/run/php/php8.3-fpm.sock` en Ubuntu 24.04): ```bash sudo rsync -a --rsync-path="sudo rsync" -e "ssh -i /home/your_user/.ssh/id_ed25519" \ your_user@old_server_ip:/etc/nginx/sites-available/your_domain /etc/nginx/sites-available/ ``` ## Paso 3: Migrar la base de datos En el servidor **nuevo**, crea la base de datos y el usuario de la aplicación con la misma contraseña que usa la configuración del sitio (por ejemplo `wp-config.php`): ```bash sudo mysql ``` ```sql CREATE DATABASE your_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'your_db_user'@'localhost' IDENTIFIED BY 'your_db_password'; GRANT ALL PRIVILEGES ON your_db.* TO 'your_db_user'@'localhost'; EXIT; ``` Ejecuta antes `sudo -v` para que `sudo` no pida la contraseña en mitad de la tubería. Después transfiere la base de datos en un solo paso: el volcado se genera en el servidor antiguo, viaja comprimido por SSH y se importa directamente en el nuevo. `--single-transaction` obtiene una copia coherente de tablas InnoDB sin bloquear el sitio: ```bash ssh your_user@old_server_ip \ "sudo mysqldump --single-transaction --quick --routines --triggers --events your_db | gzip" \ | gunzip | sudo mysql your_db ``` Si en el servidor antiguo `sudo mysqldump` pide contraseña (instalaciones en las que root no usa autenticación por socket), usa `mysqldump -u your_db_user -p` y añade `--no-tablespaces`. Comprueba que el número de tablas coincide en ambos servidores: ```bash sudo mysql -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='your_db'" ``` Esta importación sirve para las pruebas. Los datos que cambien a partir de ahora (comentarios, pedidos, usuarios) se volverán a copiar en el paso 6. ## Paso 4: Copiar el certificado TLS Let's Encrypt no puede emitir un certificado nuevo por HTTP mientras el dominio apunte al servidor antiguo. Lo más sencillo es copiar el directorio `/etc/letsencrypt` completo; `rsync -a` conserva los enlaces simbólicos de `live/` hacia `archive/`, de los que depende Certbot: ```bash sudo apt install certbot python3-certbot-nginx sudo rsync -a --rsync-path="sudo rsync" -e "ssh -i /home/your_user/.ssh/id_ed25519" \ your_user@old_server_ip:/etc/letsencrypt/ /etc/letsencrypt/ sudo certbot certificates ``` ```output Found the following certs: Certificate Name: your_domain Domains: your_domain www.your_domain Expiry Date: 2026-11-30 10:12:44+00:00 (VALID: 66 days) ``` Activa el sitio en Nginx y comprueba la configuración: ```bash sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` ```output nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` Abre los puertos web en el firewall si usas UFW: ```bash sudo ufw allow 'Nginx Full' ``` ## Paso 5: Probar el servidor nuevo antes del cambio Prueba el sitio en el servidor nuevo sin tocar el DNS. `curl --resolve` fuerza la resolución del dominio a la IP nueva y usa el certificado y el nombre reales: ```bash curl -sI --resolve your_domain:443:new_server_ip https://your_domain ``` ```output HTTP/2 200 server: nginx content-type: text/html; charset=UTF-8 ``` Para revisarlo en el navegador, añade temporalmente una línea al archivo `hosts` de **tu ordenador** (`/etc/hosts` en Linux y macOS, `C:\Windows\System32\drivers\etc\hosts` en Windows): ```text new_server_ip your_domain www.your_domain ``` Recorre las páginas principales, inicia sesión en el panel de administración, sube una imagen y prueba los formularios. Mientras tanto, vigila los errores en el servidor nuevo: ```bash sudo tail -f /var/log/nginx/error.log ``` Cuando termines, elimina la línea del archivo `hosts`. No sigas hasta que todo funcione: hasta ahora no has cambiado nada para los visitantes. ## Paso 6: Sincronización final y cambio de tráfico Esta es la única fase en la que hay que coordinar los dos servidores. Para un sitio con base de datos, la forma segura de no perder escrituras es congelarlas durante unos minutos: 1. **Pon el sitio en modo lectura o mantenimiento en el servidor antiguo.** En WordPress, por ejemplo, crea el archivo `.maintenance` en la raíz del sitio o usa un plugin de mantenimiento. Los visitantes siguen viendo el sitio; solo se bloquean los cambios durante la ventana. 2. **Repite la copia de archivos desde el servidor nuevo.** Esta vez añade `--delete` para eliminar lo que se borró en el origen. Solo transfiere los cambios, así que tarda poco: ```bash sudo rsync -aHAX --numeric-ids --delete \ -e "ssh -i /home/your_user/.ssh/id_ed25519" \ --rsync-path="sudo rsync" \ your_user@old_server_ip:/var/www/your_domain/ /var/www/your_domain/ ``` 3. **Vuelve a importar la base de datos.** El volcado incluye `DROP TABLE IF EXISTS` para cada tabla, así que reemplaza los datos de prueba: ```bash ssh your_user@old_server_ip \ "sudo mysqldump --single-transaction --quick --routines --triggers --events your_db | gzip" \ | gunzip | sudo mysql your_db ``` 4. **Haz que el servidor antiguo reenvíe el tráfico al nuevo.** Así, los visitantes que aún tengan la IP antigua en caché llegan al servidor nuevo. En el servidor **antiguo**, sustituye el contenido de los bloques `location /` del sitio por un proxy hacia la IP nueva: ```bash sudo nano /etc/nginx/sites-available/your_domain ``` ```nginx location / { proxy_pass https://new_server_ip; proxy_ssl_server_name on; proxy_ssl_name $host; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } ``` Si el bloque tiene otros `location` (por ejemplo `location ~ \.php$`), coméntalos para que todas las peticiones pasen por el proxy. Comprueba y recarga Nginx: ```bash sudo nginx -t && sudo systemctl reload nginx ``` 5. **Cambia el registro A** de `your_domain` (y `www`) a `new_server_ip` en tu proveedor DNS. 6. **Desactiva el modo mantenimiento** en el servidor nuevo, si se copió con los archivos (por ejemplo, borra el archivo `.maintenance`). Desde el paso 4 de esta lista, todas las peticiones terminan en el servidor nuevo, lleguen por DNS nuevo o antiguo. La ventana sin escrituras dura lo que tardan la copia incremental y la importación, normalmente unos minutos. > Nota: si tu aplicación registra la IP de los visitantes, las peticiones que pasen por el proxy llegarán con la IP del servidor antiguo. Es temporal; si lo necesitas, configura el módulo `realip` de Nginx en el servidor nuevo para confiar en la cabecera `X-Real-IP` que envía `old_server_ip`. ## Paso 7: Verificar la propagación y retirar el servidor antiguo Comprueba qué IP devuelven varios resolvedores públicos: ```bash dig +short your_domain @1.1.1.1 dig +short your_domain @8.8.8.8 ``` ```output new_server_ip new_server_ip ``` En el servidor antiguo, el log de acceso muestra cuánto tráfico sigue llegando por la IP antigua. Debería caer casi a cero en pocos minutos, aunque algunos clientes y bots tardan más: ```bash sudo tail -f /var/log/nginx/access.log ``` Confirma también en el servidor nuevo que la renovación del certificado funcionará ahora que el dominio apunta a él: ```bash sudo certbot renew --dry-run ``` ```output Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/your_domain/fullchain.pem (success) ``` Elimina la regla temporal de sudo en el servidor antiguo: ```bash sudo rm /etc/sudoers.d/migracion-rsync ``` Mantén el servidor antiguo encendido, con el proxy activo, al menos unos días. Después, haz una última copia de seguridad y apágalo. Cuando todo esté estable, puedes volver a subir el TTL del DNS a 3600. ## Plan de vuelta atrás Si aparece un problema grave tras el cambio y no puedes corregirlo rápido en el servidor nuevo: 1. Restaura en el servidor antiguo la configuración de Nginx original (quita el proxy) y recárgalo. 2. Devuelve el registro A a `old_server_ip`. Con el TTL a 300, el cambio se propaga en minutos. 3. Si el servidor nuevo recibió escrituras importantes (pedidos, registros), cópialas al antiguo antes de reabrirlo, o vuelve a poner el sitio en modo mantenimiento mientras lo haces. Mientras no hayas retirado el servidor antiguo, volver atrás es cuestión de minutos. ## Solución de problemas - **`rsync: connection unexpectedly closed` y `sudo: a terminal is required`**: la regla de `sudoers.d` no está en el servidor antiguo o las rutas no son `/usr/bin/rsync` y `/usr/bin/mysqldump`. Compruébalas con `command -v rsync mysqldump`. - **`502 Bad Gateway` en el servidor nuevo**: el socket de PHP-FPM de la configuración copiada no existe. Revisa `ls /run/php/` y corrige `fastcgi_pass` en el bloque de servidor. - **`Error establishing a database connection`**: el usuario de la base de datos no existe en el servidor nuevo o su contraseña no coincide con la del archivo de configuración de la aplicación. - **El proxy del servidor antiguo devuelve `502`**: el servidor nuevo no acepta conexiones en el puerto 443 desde `old_server_ip`. Revisa `sudo ufw status` en el servidor nuevo. - **Bucle de redirecciones tras el cambio**: la aplicación no sabe que la petición original era HTTPS. Comprueba que el bloque de servidor del servidor nuevo escucha en 443 con el certificado y no redirige de nuevo a sí mismo. ## Conclusión Has migrado el sitio a un servidor nuevo preparándolo y probándolo antes de tocar el DNS, con una ventana corta sin escrituras y con el servidor antiguo reenviando el tráfico residual, de modo que ningún visitante encuentra el sitio caído. Como siguientes pasos, configura copias de seguridad en el servidor nuevo, revisa el rendimiento con la carga real y, si la base de datos es grande o no admite una ventana de solo lectura, consulta la guía de migración de bases de datos con replicación.