Configurar un servidor root nuevo: checklist para los primeros 30 minutos
Los primeros 30 minutos en un servidor root nuevo son los que deciden. Nueve pasos en el orden correcto, con las trampas de Debian 13 y Ubuntu 24.04 que faltan en la mayoría de las guías.
Un servidor root nuevo es accesible desde el primer segundo y recibe escaneos desde el primer minuto. Por experiencia, los intentos de inicio de sesión automatizados contra el puerto 22 empiezan antes de que tú mismo hayas entrado por primera vez. Esta lista deja un servidor recién entregado, en aproximadamente media hora, en un estado en el que puedes dejarlo funcionando con tranquilidad.
Todos los comandos dan por supuesto que trabajas como root, tal como ocurre justo después del aprovisionamiento. En cuanto hayas iniciado sesión con tu nuevo usuario, antepón un sudo a cada comando. El orden está probado en Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS y Ubuntu 22.04 LTS. Donde los cuatro sistemas se diferencian, se indica.
Paso 0: asegura la vía de vuelta antes de cambiar nada
El único error de esta lista que no puedes reparar por SSH es justamente el que te quita el SSH. Por eso, durante los próximos 30 minutos rige una única regla de hierro: abre una segunda ventana de terminal con una conexión SSH activa y no la cierres. Una sesión SSH ya establecida sobrevive tanto a un reinicio del servicio SSH como a la activación del firewall. Si tras un cambio deja de funcionar una conexión nueva, deshaz ese cambio en la sesión que sigue abierta.
Además conviene que sepas dónde encontrar el acceso por consola a tu servidor antes de necesitarlo. En KernelHost lo tienes en el área de cliente, independiente de SSH e independiente del firewall. Quien empieza a buscar ese camino cuando ya está bloqueado fuera, pierde tiempo.
Hay dos mensajes de error que deberías saber distinguir, porque apuntan a causas completamente distintas:
ssh: connect to host 203.0.113.10 port 22: Connection refusedsignifica que el paquete llegó, pero nadie escucha en ese puerto. El servicio SSH no está en marcha o escucha en otro puerto.ssh: connect to host 203.0.113.10 port 22: Connection timed outsignifica que el paquete se descartó. Casi siempre es el firewall.Permission denied (publickey)significa que el servicio funciona y el firewall te deja pasar, solo que tu clave no encaja.
Paso 1: actualizar el sistema
Una imagen recién instalada rara vez está al día. Entre la creación de la imagen y tu pedido suelen pasar semanas en las que han aparecido actualizaciones de seguridad.
cat /etc/os-release
apt update
apt full-upgrade -y
Usar full-upgrade en lugar de upgrade es una decisión consciente: en un sistema recién instalado, apt puede eliminar paquetes si una dependencia lo exige. En un sistema en producción ya en marcha, primero comprobarías qué se va a eliminar.
En Ubuntu 22.04 y 24.04, needrestart viene preinstalado e interrumpe la actualización con una pantalla completa de colores que pregunta qué servicios hay que reiniciar. Si no quieres eso, por ejemplo dentro de un script:
NEEDRESTART_MODE=a DEBIAN_FRONTEND=noninteractive apt full-upgrade -y
Después toca limpiar y comprobar si hace falta un reinicio:
apt autoremove --purge -y
apt list --upgradable
test -f /var/run/reboot-required && echo "reinicio necesario" || echo "no se requiere reinicio"
Diferencia entre distribuciones: El archivo /var/run/reboot-required solo lo crea Ubuntu de forma fiable, procede del paquete update-notifier-common. Debian, por defecto, no avisa en absoluto de que haga falta un reinicio. En Debian instala para eso needrestart, que al ejecutarlo te dice si hay un kernel nuevo esperando. Debian 13 trae además una generación más reciente de apt con salida en color y formateada por columnas, lo cual no es un fallo, solo resulta poco habitual.
Comprobación: apt list --upgradable ya no muestra nada aparte de la línea de cabecera Listing.... Si durante la actualización aparece el mensaje Release file for ... is not valid yet, el reloj de tu servidor va mal: salta al paso 5 y repite después el paso 1.
Más sobre el tema, también sobre el manejo de paquetes retenidos y repositorios de terceros: Actualizar un servidor Linux con apt.
Paso 2: crear un usuario en lugar de trabajar como root
Como root no se trabaja, porque cualquier errata afecta de inmediato a todo el sistema y porque cualquier atacante ya conoce el nombre de usuario root. En las imágenes mínimas de Debian, sudo a menudo ni siquiera está instalado:
apt install -y sudo
adduser --disabled-password --gecos "" kernel
usermod -aG sudo kernel
La opción --disabled-password crea el usuario sin contraseña, que es justo lo correcto para una autenticación solo por clave. Si además quieres asignar una contraseña, por ejemplo para usar sudo desde la consola, hazlo con passwd kernel.
Diferencia entre distribuciones: En Debian y Ubuntu, el grupo de administración se llama sudo. Solo si vienes de un sistema tipo RHEL buscarás wheel, que aquí no existe.
Ahora la clave pública. Crea el directorio con los permisos correctos, porque unos permisos incorrectos son la causa más frecuente de que se rechace la autenticación por clave:
mkdir -p /home/kernel/.ssh
chmod 700 /home/kernel/.ssh
touch /home/kernel/.ssh/authorized_keys
chmod 600 /home/kernel/.ssh/authorized_keys
chown -R kernel:kernel /home/kernel/.ssh
El contenido de tu clave pública lo añades a authorized_keys, aunque resulta más cómodo hacerlo desde tu equipo de trabajo con ssh-copy-id kernel@203.0.113.10.
Comprobación, y además antes de tocar la configuración de SSH:
id kernel
sudo -l -U kernel
La segunda línea tiene que contener (ALL : ALL) ALL. Inicia sesión después en una tercera ventana como kernel y ejecuta una vez sudo -v. Solo cuando eso funcione, sigue adelante. Detalles: Crear un usuario y configurar sudo y Crear y registrar claves SSH.
Paso 3: asegurar SSH
En las imágenes de Debian muy ligeras, el servidor SSH ni siquiera está instalado, así que primero tendrás que instalarlo:
apt install -y openssh-server
Los cuatro sistemas tratados aquí leen configuración adicional desde /etc/ssh/sshd_config.d/. Así que no edites el gran sshd_config, crea tu propio archivo. Ese sobrevive a las actualizaciones de paquetes sin preguntar nada.
Un detalle que casi todas las guías hacen mal: en la configuración de SSH gana el primer valor encontrado, no el último. En Debian y Ubuntu, la línea Include /etc/ssh/sshd_config.d/*.conf está justo al principio, y los archivos de ese directorio se leen en orden alfabético. En las imágenes de Ubuntu suele haber ya un 50-cloud-init.conf con PasswordAuthentication yes. Un archivo llamado 99-... quedaría por tanto sin ningún efecto. Mira primero qué hay:
ls -l /etc/ssh/sshd_config.d/
cat > /etc/ssh/sshd_config.d/10-kernelhost.conf <<'EOF'
PermitRootLogin prohibit-password
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
EOF
prohibit-password en lugar de no es una decisión consciente: root sigue pudiendo entrar con clave, pero nunca con contraseña. Eso te salva si algo sale mal con el usuario sudo. Quien quiera asegurar el sistema con más dureza pone no, pero entonces debería haber probado de verdad el acceso por consola.
Antes de cada reinicio del servicio, comprueba la sintaxis:
ssh-keygen -A
sshd -t && echo "configuración ok"
sshd -T | grep -E "^(permitrootlogin|passwordauthentication|pubkeyauthentication|port) "
sshd -T muestra los valores realmente efectivos, tras resolver todos los archivos incluidos. Es la única prueba fiable de que tu cambio ha llegado. Si el comando informa de sshd: no hostkeys available -- exiting, faltan las claves de host y ssh-keygen -A las genera. Si en cambio la comprobación se interrumpe con Missing privilege separation directory: /run/sshd, el servicio no se ha ejecutado ni una sola vez desde el arranque del sistema y falta el directorio de tiempo de ejecución. Un mkdir -p /run/sshd o un systemctl restart ssh lo crea, y después sshd -t vuelve a evaluar tu configuración.
Hay una salida que despista con frecuencia: para PermitRootLogin prohibit-password, sshd -T devuelve la línea permitrootlogin without-password. Es el mismo valor con su nombre antiguo y no indica que tu ajuste no haya surtido efecto.
Solo después de eso:
systemctl restart ssh
La trampa del socket en Ubuntu 24.04 y Debian 13
Desde Ubuntu 22.10, y por tanto también en 24.04, SSH se arranca mediante activación por socket. La consecuencia: una línea Port en la configuración de sshd se ignora, el puerto viene de ssh.socket. Quien quiera cambiar el puerto necesita un override de systemd:
systemctl edit ssh.socket
Ahí dentro va lo siguiente, teniendo en cuenta que la primera línea vacía borra el valor predeterminado:
[Socket]
ListenStream=
ListenStream=0.0.0.0:2222
ListenStream=[::]:2222
Después, systemctl daemon-reload y systemctl restart ssh.socket. Ubuntu 22.04 todavía no conoce este mecanismo, allí basta con la línea Port en la configuración.
En Debian 13 aparece una curiosidad emparentada. Algunas imágenes nuevas tienen ssh.socket activo, y los sistemas actualizados desde Debian 12 no. Si ambos están activos a la vez, un reload falla con fatal: Cannot bind any address., porque el servicio y el socket se pelean por el puerto 22. Comprueba y, en caso de duda, decide:
systemctl is-enabled ssh.socket
systemctl disable --now ssh.socket
systemctl enable --now ssh.service
Comprobación: ss -tlnp | grep ssh muestra el puerto esperado, y un nuevo intento de conexión desde una ventana recién abierta funciona. En detalle: Asegurar SSH y desactivar el acceso root y Cambiar el puerto SSH.
Paso 4: activar el firewall
En Ubuntu, ufw está instalado pero inactivo. En las imágenes mínimas de Debian falta por completo. Aquí el orden es vital: primero permitir SSH, después activar.
apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
El último comando es el más importante de toda la lista. Las reglas y las políticas por defecto, por sí solas, no filtran nada: solo ufw enable pone el firewall en marcha. Quien se lo salta acaba con un firewall completamente configurado, pero sin ningún efecto.
Al activarlo, ufw pregunta: Command may disrupt existing ssh connections. Proceed with operation (y|n)?. Esa advertencia va en serio, pero si la regla para el puerto 22 (o para el puerto que hayas cambiado) está antes, no pasa nada, y justo por eso ufw allow 22/tcp aparece en la lista por encima de ufw enable. Si en el paso 3 cambiaste el puerto, aquí tiene que ir ufw allow 2222/tcp, o te quedarás fuera. En un script o en una sesión no interactiva usa ufw --force enable, que se salta la pregunta.
Comprobación:
ufw status verbose
Se espera Status: active, debajo Default: deny (incoming), allow (outgoing) y, en la lista de reglas, una línea 22/tcp ALLOW IN para tu puerto SSH. Si ahí sigue poniendo Status: inactive, falta ufw enable y no hay nada protegido, por completas que parezcan las reglas. Abre después una ventana nueva y conéctate antes de cerrar la antigua.
A esto hay que sumar un mecanismo de bloqueo contra los intentos de inicio de sesión:
apt install -y fail2ban python3-systemd
cat > /etc/fail2ban/jail.local <<'EOF'
[DEFAULT]
backend = systemd
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
EOF
Por qué backend = systemd: Debian ya no instala rsyslog desde la versión 12, así que no existe ningún /var/log/auth.log. El valor por defecto backend = auto busca exactamente ese archivo y entonces fail2ban ni siquiera arranca, con el mensaje Failed during configuration: Have not found any log file for sshd jail. El paquete python3-systemd es el requisito para que el acceso al journal funcione siquiera. En Ubuntu 22.04 y 24.04, el archivo todavía existe gracias a rsyslog, pero la variante de systemd funciona allí igual de bien y es la opción con futuro. Puedes comprobarlo así:
test -f /var/log/auth.log && echo "auth.log presente" || echo "sin auth.log, hace falta backend systemd"
fail2ban-client -t
Comprobación: fail2ban-client status sshd muestra una línea Currently banned. Si en su lugar aparece Sorry but the jail 'sshd' does not exist, la configuración no se ha cargado. Más en Configurar el firewall UFW y Configurar Fail2ban.
Paso 5: zona horaria y hora del sistema
Un reloj mal ajustado deja los logs sin valor, hace fracasar las comprobaciones de certificados y puede bloquear apt con Release file is not valid yet. En un servidor real:
timedatectl set-timezone Europe/Vienna
timedatectl status
En la salida tienen que cuadrar dos líneas: Time zone: Europe/Vienna y System clock synchronized: yes, además de NTP service: active. Si ahí pone NTP service: inactive, no hay ninguna sincronización horaria en marcha. Ubuntu trae systemd-timesyncd de serie, las imágenes mínimas de Debian a menudo no:
DEBIAN_FRONTEND=noninteractive apt install -y systemd-timesyncd tzdata
date
Muchos operadores dejan sus servidores en UTC a propósito, para que los logs de distintas ubicaciones sigan siendo comparables. Ambas opciones son defendibles, lo decisivo es que tú lo sepas. Si timedatectl no está disponible en un entorno de contenedores, también se puede hacer a la manera clásica:
ln -sf /usr/share/zoneinfo/Europe/Vienna /etc/localtime
dpkg-reconfigure -f noninteractive tzdata
Para profundizar: Configurar la zona horaria y la sincronización de hora.
Paso 6: definir el hostname
El hostname aparece en los logs, en los correos salientes y en los avisos de monitorización. Defínelo pronto, o más adelante todos tus servidores se llamarán igual.
hostnamectl set-hostname srv01.tu-dominio.com
hostname -f
Después hace falta la entrada correspondiente en /etc/hosts, o cada llamada a sudo te recibirá con el mensaje sudo: unable to resolve host srv01: Name or service not known y un retardo perceptible. La línea es, en esencia, 127.0.1.1 srv01.tu-dominio.com srv01.
La trampa: En las imágenes con cloud-init, que es lo habitual en Ubuntu, el hostname se restablece en el siguiente reinicio. El interruptor para evitarlo:
command -v cloud-init || echo "cloud-init no instalado"
mkdir -p /etc/cloud/cloud.cfg.d
printf 'preserve_hostname: true\n' > /etc/cloud/cloud.cfg.d/99_hostname.cfg
Comprobación: Después de un reinicio, hostnamectl sigue devolviendo tu nombre. Detalles: Cambiar el hostname de forma permanente en Linux.
Paso 7: actualizaciones de seguridad automáticas
El servidor más peligroso es el que ya nadie toca. Las actualizaciones de seguridad automáticas son el paso individual más eficaz de esta lista.
apt install -y unattended-upgrades
cat > /etc/apt/apt.conf.d/20auto-upgrades <<'EOF'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
EOF
Instalar el paquete por sí solo no basta en todas partes, solo este archivo activa la ejecución diaria. Los ajustes propios van en un archivo con un número más alto que el 50unattended-upgrades incluido, para que ganen y no se sobrescriban en una actualización del paquete:
cat > /etc/apt/apt.conf.d/52unattended-upgrades-local <<'EOF'
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::MinimalSteps "true";
EOF
Por defecto, la herramienta solo toma paquetes del repositorio de seguridad en ambas distribuciones, no las actualizaciones normales. Es intencionado y para sistemas en producción suele ser justo lo adecuado. Si pones Automatic-Reboot "true", define sin falta también una hora, o el servidor se reiniciará cuando le venga bien al temporizador.
Comprobación: Una ejecución de prueba muestra qué paquetes entrarían en juego, sin instalar nada. Fíjate en el singular del nombre del comando:
unattended-upgrade --dry-run --debug
apt-config dump | grep -iE "unattended|periodic"
En el registro bajo /var/log/unattended-upgrades/ tiene que haber algo después de la primera ejecución. Si sigue vacío, la configuración no está surtiendo efecto. En detalle: Configurar actualizaciones de seguridad automáticas.
Paso 8: monitorización
Monitorización, en los primeros 30 minutos, no significa montar un Grafana. Significa que te enteres cuando el servidor se caiga o cuando el disco se llene.
apt install -y htop tmux curl
df -h /
free -m
Con tres cosas basta para empezar. Primera, una prueba de disponibilidad externa que compruebe desde fuera y te avise, porque un servidor que se ha caído ya no manda ninguna alerta sobre sí mismo. Segunda, un aviso de espacio en disco, porque un sistema de archivos lleno es la causa más frecuente de caídas que nadie vio venir. Tercera, una mirada al journal cuando algo resulte extraño:
journalctl -p 3 -b --no-pager | tail -n 30
systemctl --failed
systemctl --failed debería devolver 0 loaded units listed. Cada línea que aparezca ahí es un servicio que no arranca y que te conviene reparar ahora, no dentro de tres meses. Más sobre esto: Configurar la monitorización del servidor.
Paso 9: backup antes de que haya algo que perder
El mejor momento para el primer backup es antes de que haya datos. Así ensayas el procedimiento sin presión. Hay dos cosas que merece la pena guardar de inmediato, porque reconstruirlas es lo que más tiempo cuesta: la configuración bajo /etc y la lista de paquetes instalados.
tar -czf /root/etc-backup-$(date +%F).tar.gz /etc
dpkg --get-selections > /root/paquetes.txt
tar -tzf /root/etc-backup-$(date +%F).tar.gz | wc -l
Con eso todavía no tienes un backup, solo una copia en el mismo disco. Un backup está en otro sistema, idealmente en otra ubicación. Una herramienta con cifrado y deduplicación de bloques idénticos merece la pena desde el primer día:
apt install -y borgbackup
borg --version
La verdad incómoda: un backup del que nunca se ha restaurado nada es solo una suposición. Reserva una cita para la primera restauración y recupera un único archivo. El camino está en Estrategia de backup para servidores root.
Si te has quedado fuera
Pasa, casi siempre en el paso 3 o en el 4. La vuelta es siempre la misma: entrar por la consola del área de cliente, allí con nombre de usuario y contraseña en lugar de con clave. Después, según la causa:
- Firewall demasiado estricto:
ufw disable, corregir la regla,ufw enable. - Configuración de SSH rota:
rm /etc/ssh/sshd_config.d/10-kernelhost.conf, despuéssshd -tysystemctl restart ssh. - Puerto equivocado tras cambiar el socket:
systemctl revert ssh.socketdeshace el override, despuéssystemctl daemon-reloadysystemctl restart ssh.socket. - Bloqueado por fail2ban:
fail2ban-client set sshd unbanip 203.0.113.10. Para que no te vuelva a pasar, añade tu dirección fija bajoignoreipen eljail.local. - La clave se rechaza: Casi siempre son los permisos.
chmod 700en el directorio,chmod 600en el archivo, y ambos tienen que pertenecer al usuario, no a root.
La comprobación final
Que un comando no devuelva ningún error no significa que haya surtido efecto. Estas seis comprobaciones muestran el estado real:
sshd -T | grep -E "^(permitrootlogin|passwordauthentication|port) "muestra los valores efectivos, no los deseados.ufw status verboseindicaStatus: activecon una regla para tu puerto SSH.timedatectl statusindicaSystem clock synchronized: yes.systemctl --failedno lista nada.unattended-upgrade --dry-run --debugse ejecuta sin mensajes de error.- Una conexión SSH nueva desde una ventana recién abierta funciona, con clave y sin que te pida contraseña.
Solo cuando el punto seis funcione puedes cerrar la ventana de terminal antigua.
Lo que viene después
Una palabra sobre la elección de la distribución, porque decide los próximos años. Debian 12 salió del soporte regular en julio de 2026 y el equipo LTS lo seguirá manteniendo hasta mediados de 2028, con un conjunto de paquetes limitado. Quien monta hoy un sistema nuevo elige Debian 13 o Ubuntu 24.04 LTS. Ubuntu 22.04 LTS sigue en soporte estándar hasta 2027, pero para un servidor pensado para funcionar durante años ya no es la primera opción. Debian 10 y Ubuntu 20.04 están definitivamente terminados desde junio de 2024 y mayo de 2025 respectivamente, y no tienen sitio en ningún servidor nuevo.
También son relevantes en la práctica las diferencias de versión en los repositorios. Debian no incluye mysql-server en absoluto, allí MariaDB es el estándar. Quien necesite una versión concreta de PHP, Node o Java hará bien en comprobar antes qué trae la distribución, en lugar de añadir después repositorios de terceros. Y si añades repositorios de terceros: apt-key está obsoleto, las claves van en /etc/apt/keyrings/ y se referencian en la línea del repositorio mediante signed-by.
Con esto tienes un servidor actualizado, que se mantiene al día por sí solo, que solo te deja entrar a ti y que avisa cuando algo no va bien. Todo lo demás, servidor web, base de datos, certificados, se construye encima de eso y no al lado.
Preguntas frecuentes
¿En qué orden debo configurar un servidor root nuevo?
¿Por qué no surte efecto mi cambio en sshd_config?
¿Por qué no cambia mi puerto SSH en Ubuntu 24.04?
Fail2ban no arranca y avisa de que no ha encontrado ningún archivo de log para el jail sshd. ¿Qué hago?
¿Por qué mi hostname vuelve a desaparecer después del reinicio?
¿Basta con apt upgrade o necesito apt full-upgrade?
2026 KernelHost GmbH. Todos los derechos reservados. Esta guía está protegida por derechos de autor. Su publicación en otros sitios web, aunque sea de forma parcial o modificada, no está permitida sin nuestro consentimiento por escrito. Las citas con indicación de la fuente y un enlace son muy bienvenidas.

