SSH en Linux: guía práctica de terminal, túneles, procesos y debugging

SSH no es una herramienta: es la puerta a todo lo demás

De subir un favicon a dominar túneles, señales Unix y debugging a nivel de kernel. Una guía técnica sin rodeos para dejar de tener miedo a la terminal.

La terminal no da miedo. Da poder.

Hace unos días entré por SSH a un servidor con una misión ridícula: subir un favicon. Salí sabiendo configurar túneles inversos, interpretar exit codes y matar procesos con la señal correcta. Este artículo es todo lo que aprendí por el camino, condensado.

El momento en que todo cambia

Hay un antes y un después. Antes usaba FileZilla para subir archivos, cPanel para crear bases de datos y rezaba para que nada se rompiera. Después, entiendo por qué las cosas funcionan y puedo arreglarlas cuando fallan.

El salto no fue aprender comandos sueltos. Fue entender el modelo mental de Unix: todo es un archivo, todo comando devuelve un código, todo proceso recibe señales. Una vez que ves eso, el resto es vocabulario.

Si puedes hacerlo por SSH, puedes automatizarlo. Si puedes automatizarlo, no lo hagas a mano nunca más.

1. Por qué SSH y no FTP (la decisión que lo cambia todo)

Cuando editas código en producción, la diferencia entre FTP y SSH no es comodidad. Es riesgo.

Tarea FTP SSH
Subir archivo Arrastrar y esperar scp en una línea
Editar código Descargar → editar → subir sed -i en milisegundos
Limpiar caché Imposible php artisan view:clear
Ver logs en vivo Descargar y abrir tail -f
Riesgo de romper producción Alto Cambios controlados desde terminal

Por FTP, cada archivo que subes puede pasar por un estado intermedio donde la web todavía intenta servirlo. Por SSH puedes trabajar directamente sobre el servidor y utilizar herramientas mucho más precisas para comprobar, modificar y validar los cambios.

2. Los cimientos: los comandos que usarás cada día

Hay 10 comandos que cubren una parte enorme del trabajo diario. No memorices flags: entiende qué hace cada uno y por qué existe.

ls -la · el estándar

-l da formato largo: permisos, dueño, tamaño y fecha. -a incluye archivos ocultos como .env, .git o .htaccess. Sin -a no ves una parte importante del proyecto.

grep -rn "buscar" . · el rey de la búsqueda

-r busca recursivamente y -n muestra el número de línea. Combinado con find y xargs puedes buscar rápidamente en grandes cantidades de archivos. Es una de las primeras herramientas que conviene utilizar cuando no sabes dónde está algo.

tail -f · el log en vivo

Abres un log, recargas la web y ves el error aparecer en tiempo real. Es una de las formas más rápidas de investigar problemas en producción sin modificar el código.

sed -i · el editor silencioso

sed -i 's|VIEJO|NUEVO|g' archivo.txt reemplaza texto directamente en un archivo sin abrir un editor interactivo.

Es especialmente útil para cambios pequeños y controlados, aunque en producción siempre conviene hacer copia previa y comprobar el resultado.

rsync · lo que scp debería haber sido siempre

Solo transfiere las diferencias. Para una carpeta con miles de archivos donde solo han cambiado tres, puede ahorrar muchísimo tiempo. Y con --dry-run puedes comprobar qué haría antes de ejecutarlo.

3. El modelo mental que nadie te explica

Esta es la parte que separa a quien sabe usar comandos de quien entiende qué pasa. Si solo lees una sección de este artículo, que sea esta.

Todo es un archivo

En Unix, muchas interfaces del sistema se exponen mediante archivos y descriptores. Cada proceso comienza normalmente con tres descriptores estándar:

  • 0 → stdin (entrada)
  • 1 → stdout (salida)
  • 2 → stderr (errores)

Por eso cmd 2> errores.txt funciona: estás redirigiendo el descriptor 2. Y &> redirige tanto la salida estándar como los errores. Ese detalle explica buena parte del sistema de redirecciones.

El shell expande en este orden

Cuando escribes una línea, Bash realiza diferentes fases de expansión antes de ejecutar el comando. Conocerlas evita muchos problemas con variables y comillas:

1. Brace expansion       {a,b,c}  →  a b c
2. Tilde expansion       ~        →  /home/usuario
3. Parameter expansion   $VAR     →  valor
4. Command substitution  $(cmd)   →  salida
5. Arithmetic            $((1+2)) →  3
6. Word splitting        (por IFS)
7. Pathname expansion    *.txt    →  archivos
8. Quote removal         "quita"  →  quita

El paso de word splitting es el culpable de muchos bugs en scripts: $VAR sin comillas puede dividirse por espacios, mientras que "$VAR" mantiene el contenido como una única expansión.

Regla práctica: cuando una variable pueda contener espacios, utiliza normalmente "$VAR".

Todo comando devuelve un exit code

Los procesos comunican su estado mediante un código de salida. En términos generales, 0 significa éxito y un valor distinto de cero indica algún tipo de fallo o condición especial.

Esto es la base de operadores como && —continúa si lo anterior tuvo éxito— y || —ejecuta la alternativa si lo anterior falló—.

Algunos códigos que aparecen con frecuencia son 1 para errores generales, 126 cuando algo no puede ejecutarse, 127 cuando no se encuentra un comando y 130 cuando el proceso termina tras Ctrl+C.

Los procesos no se matan: reciben señales

Esta fue una de las ideas más importantes. kill no significa necesariamente "matar": envía una señal.

Por defecto, kill PID envía SIGTERM (15), que permite al proceso reaccionar y terminar de forma ordenada si está preparado para ello.

kill -9 envía SIGKILL. No puede ser capturada ni ignorada por el proceso y termina inmediatamente su ejecución. Por eso puede impedir tareas de limpieza pendientes.

Regla: utiliza primero kill / SIGTERM. Reserva kill -9 para los casos en los que el proceso realmente no termina de forma normal.

4. Los trucos que separan a un usuario de un sysadmin

Túneles SSH: una VPN en una línea

SSH no solo abre terminales. También puede redirigir puertos. Esto permite crear un túnel cifrado entre tu máquina y servicios accesibles desde el servidor:

# Puerto local 8080 → puerto 80 del servidor
ssh -L 8080:localhost:80 usuario@servidor

# Acceder a MySQL solo accesible desde el servidor
ssh -L 3306:localhost:3306 usuario@servidor

# Proxy SOCKS para el tráfico compatible
ssh -D 1080 usuario@servidor

Caso real: tu base de datos MySQL solo acepta conexiones locales. Con ssh -L 3306:localhost:3306, un cliente local puede conectarse a localhost:3306 mientras SSH transporta esa conexión hacia el servidor.

De esta forma puedes evitar exponer directamente el puerto de la base de datos a Internet.

tmux: sobrevive a la desconexión

Si una tarea larga depende directamente de una sesión SSH, perder la conexión puede interrumpir el proceso. tmux permite mantener una sesión de terminal persistente aunque cierres temporalmente la conexión SSH.

tmux new -s deploy

# lanzas tu deploy de 20 minutos

# detach con:
Ctrl+b d

# cierras SSH

# vuelves a entrar y recuperas la sesión:
tmux attach -t deploy

Es una herramienta especialmente útil para procesos largos y tareas de administración.

El truco base64 para scripts complejos

Si estás en PowerShell y necesitas ejecutar un script SSH con muchas comillas anidadas, paréntesis o llaves, la combinación de shells puede resultar incómoda.

Una solución práctica es codificar el contenido del script en Base64 y reconstruirlo en el servidor.

$script = @'
cd ~/proyecto
sed -i 's|VIEJO|NUEVO|g' archivo.txt
'@

$b64 = [Convert]::ToBase64String(
    [Text.Encoding]::UTF8.GetBytes($script)
)

ssh usuario@host "echo '$b64' | base64 -d | bash"

El resultado es una cadena mucho más sencilla de transportar entre PowerShell y Bash cuando las comillas empiezan a complicar la ejecución.

5. Cuando las cosas van mal: debugging real

404 o 403 en producción

curl -I https://tuweb.com/ruta permite comprobar las cabeceras HTTP y el código de respuesta sin abrir el navegador.

  • 404 → el recurso o ruta no se ha encontrado.
  • 403 → el servidor ha entendido la petición pero está rechazando el acceso, normalmente por permisos, configuración o reglas de acceso.

"Edité un blade pero no se ve el cambio"

Una de las primeras comprobaciones en Laravel es la caché de vistas:

php artisan view:clear

Después puedes realizar una recarga forzada del navegador con Ctrl+Shift+R.

"No space left on device"

El disco está lleno y diferentes servicios pueden empezar a fallar. Un diagnóstico rápido:

df -h
du -sh * | sort -h
php artisan cache:clear

Antes de borrar logs u otros archivos, identifica qué está ocupando realmente el espacio y qué puede eliminarse de forma segura.

"Address already in use"

Alguien ya está utilizando el puerto. Primero identifica el proceso:

lsof -i :8080

Después decide si realmente debe detenerse. No conviene utilizar kill -9 automáticamente sin comprobar qué proceso es.

Cuando no hay log que explique nada

Cuando los logs de aplicación no son suficientes, herramientas como strace permiten observar determinadas llamadas del proceso al sistema.

strace -f -e trace=openat comando

Puede resultar especialmente útil para descubrir qué archivos intenta abrir un proceso, qué recursos no encuentra o dónde se produce una llamada inesperada.

6. El arsenal avanzado (para cuando ya no tienes miedo)

~/.ssh/config: deja de escribir 30 caracteres por conexión

El archivo de configuración de SSH permite convertir conexiones largas y repetitivas en simples alias:

Host prod
    HostName mi-servidor.com
    User mi_usuario
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

Host *.internal
    User deploy
    ProxyJump bastion

Ahora ssh prod puede utilizar toda esa configuración sin volver a escribirla cada vez.

Con ProxyJump también puedes utilizar un servidor intermedio como bastión para alcanzar otros sistemas sin exponerlos directamente.

journalctl: logs de systemd con filtros potentes

journalctl -u nginx -f
journalctl --since "1 hour ago"
journalctl -p err -b

Puedes seguir los logs de un servicio, limitar la búsqueda a un periodo concreto o filtrar por prioridad.

Las prioridades van desde emerg(0) hasta debug(7).

set -euo pipefail: el shebang moderno

#!/usr/bin/env bash
set -euo pipefail

-e hace que el script termine ante determinados errores. -u considera error el uso de variables no definidas. pipefail hace que un pipeline refleje el fallo de cualquiera de sus comandos, no solamente el del último.

Por ejemplo:

false | true
echo $?

Sin pipefail, el resultado puede reflejar únicamente el estado del último comando. Con pipefail, el fallo anterior puede propagarse al resultado del pipeline.

7. Las reglas que me llevo

  1. Mira el prompt antes de escribir ssh o scp. PS C:\ = tu PC. Si el prompt muestra que estás dentro del servidor, compruébalo antes de ejecutar comandos destructivos.
  2. Backup antes de editar. cp archivo archivo.bak. Dos segundos pueden ahorrar horas.
  3. Después de tocar una vista Blade: php artisan view:clear y después recarga el navegador si es necesario.
  4. Verifica con grep, curl y los logs. No te fíes únicamente de tu memoria.
  5. Comillas raras entre PowerShell y Bash: utiliza una estrategia de transporte clara, como el script codificado en Base64, cuando realmente lo necesites.
  6. SIGTERM antes que SIGKILL. kill antes que kill -9.
  7. En scripts Bash: set -euo pipefail es un buen punto de partida para evitar determinados fallos silenciosos.
  8. Usa "$VAR" cuando quieras preservar una variable como una única palabra.
  9. Antes de un rsync --delete, utiliza siempre --dry-run para comprobar qué va a ocurrir.
  10. Deploys largos: utiliza tmux o un sistema de gestión de procesos apropiado para producción.

La conclusión que nadie te cuenta

SSH no es simplemente una herramienta para abrir una terminal remota. Es una de las interfaces fundamentales para administrar sistemas Unix y automatizar operaciones sobre servidores.

Cuando aprendes SSH de verdad, no aprendes solamente un comando. Aprendes el modelo mental que después aparece detrás de Git, automatización, despliegues, backups, infraestructura como código y muchas otras herramientas.

La GUI simplifica muchas tareas, pero también puede ocultar lo que realmente está ocurriendo. La terminal obliga a enfrentarse directamente con archivos, procesos, permisos, señales, conexiones y servicios.

Aprender esto duele. Al principio te equivocas constantemente, te pierdes entre directorios, rompes cosas y no sabes cómo volver atrás.

Pero cada error te enseña algo que una interfaz gráfica puede haber ocultado durante años.

Todo lo moderno está construido alrededor de sistemas que puedes administrar desde texto. Cuando dominas la terminal, dejas de limitarte a utilizar la infraestructura: empiezas a entender cómo funciona.

Si has llegado hasta aquí leyendo, ya has dado el salto mental que separa a los dos mundos. El resto es vocabulario, práctica y muchas horas delante de una terminal.