Exponer servicios de tu home lab a internet: 6 vías explicadas
Cuando monté mi primer home lab en 2019, mi miedo era exponer algo. Hoy, después de años con servicios publicados desde una IP residencial, sé que lo que daña no es abrir un puerto: es abrirlo mal. He visto bots escaneando IPs nuevas en menos de 30 segundos, ISPs bloqueando el puerto 80 de salida y proveedores de Cloudflare marcando como spam cualquier túnel mal configurado. La diferencia entre “está expuesto” y “está expuesto de forma segura y mantenible” no es trivial, y cada vía tiene sus trampas.
Esta guía recorre las 6 formas reales de exponer servicios desde casa en 2026 — port forwarding clásico, reverse proxy, Cloudflare Tunnel, Tailscale Funnel, ngrok y FRP — con los comandos exactos que probé en un Hetzner + MikroTik reales, los gotchas de cada vía y un árbol de decisión para que no pierdas tres horas eligiendo mal antes de instalar nada.
TL;DR — Lo que vas a tener al final
- Tiempo de setup: 45–90 minutos para configurar la primera vía (5 si ya tienes el dominio)
- Requisitos: Un dominio propio (10 €/año), un servidor o VPS accesible por SSH y, según la vía, una IP pública sin CGNAT
- Resultado: Tu servicio accesible desde internet con HTTPS válido y un proceso documentado de mantenimiento cada vez que cambia tu IP
- Troubleshooting: CGNAT del ISP (solución: Tunnel sin port forwarding), puerto 443 bloqueado por la red corporativa (solución: cambiar a 8443 en Cloudflare), túnel caíco al cambiar la IP dinámica (solución: ddclient + cron)
Exponer un servicio de tu home lab a internet es, en teoría, abrir un puerto en el router. En la práctica, hay que vérselas con CGNAT, direcciones IP dinámicas, certificados TLS, bots escaneando tu IP pública al minuto de conectarla y proveedores que bloquean el puerto 80 de salida. Esta guía recorre las seis formas reales de hacerlo en 2026, desde el port forwarding clásico hasta Cloudflare Tunnel y Tailscale Funnel, con los comandos exactos, los gotchas de cada vía y un árbol de decisión para que no pierdas tres horas eligiendo mal.
Por qué no basta con “abrir un puerto”
La idea simple de exponer un servicio —abrir el puerto 443 en el router, apuntar el dominio a tu IP pública y listo— choca con cuatro problemas que han crecido en los últimos cinco años:
- CGNAT (Carrier-Grade NAT). Muchos ISP residenciales comparten una IP pública entre decenas o cientos de abonados. Tu router ya no recibe paquetes de internet: solo recibe tráfico del NAT del operador. Sin IP pública, port forwarding es imposible.
- IP dinámica. Incluso si tienes IP pública, suele rotar cada 24 horas o cada reinicio del router. El DNS se queda apuntando a la IP vieja.
- Exposición del borde. Abrir un puerto publica tu IP pública en logs de scanners, listas de spam y botnets. SSH expuesto sin fail2ban dura minutos antes de ver intentos de brute force.
- Certificados TLS. Let’s Encrypt exige HTTP-01 (puerto 80 abierto) o DNS-01 (API del proveedor DNS). Si tu proveedor bloquea salientes al 80 o no tienes API, ACME se complica.
Por eso existen seis vías diferentes, no una. Cada una resuelve parte del problema y deja otra parte al descubierto.
Las seis vías: panorama técnico
Antes de entrar en comandos, esta tabla resume qué resuelve cada vía y qué te exige. Úsala como mapa rápido; el detalle está en cada sección.
| Vía | IP pública | Dominio propio | TLS automático | UDP soportado | Coste | Cuándo elegirla |
|---|---|---|---|---|---|---|
| Port forwarding + DDNS + Caddy | Sí | Opcional (DDNS sirve) | Sí (Let’s Encrypt) | Sí (manual) | Gratis | Tienes IP pública, quieres control total |
| Port forwarding + DDNS + nginx + certbot | Sí | Igual | Sí (cron) | Igual | Gratis | Mismo caso, prefieres nginx y certificados manuales |
| Cloudflare Tunnel (cloudflared) | No necesaria | Sí (en Cloudflare) | Sí (terminación en edge) | Limitado (TCP/SSH sí, UDP parcial) | Gratis (Free tier) | Estás detrás de CGNAT o quieres esconder tu IP |
| Tailscale Funnel | No necesaria | Subdominio *.ts.net o propio | Sí (terminación Tailscale) | Solo TCP (HTTP) | Gratis (Personal) | Ya usas Tailscale, quieres salida puntual |
| VPS como relay (WireGuard) | Solo del VPS | Sí, en el VPS | Sí (en el VPS) | Sí (reenvías el tráfico) | ~$5/mes (VPS) | CGNAT severo + necesitas UDP (juegos, WireGuard) |
| IPv6 directo | Sí (IPv6) | Igual que opción 1 | Igual | Igual | Gratis | Tu ISP te da IPv6 con prefijo estable |
Vía 1: port forwarding + DDNS + Caddy (la vía clásica)
Esta es la configuración que todo el mundo aprende primero y la que sigue siendo la más flexible. Requiere que tengas IP pública, pero te da control total sobre cualquier protocolo (TCP, UDP, HTTP, SSH, WireGuard, Minecraft, lo que sea).
Comprobar si tienes IP pública real
Antes de tocar el router, necesitas saber si tu ISP te da una IP pública o si estás detrás de CGNAT. Hay tres formas de averiguarlo, de más rápida a más fiable:
- Entra al admin del router (suele ser
192.168.1.1o192.168.0.1) y mira la IP WAN del router. - Compara esa IP con la que ves en ifconfig.me o icanhazip.com desde un navegador.
- Si la IP del admin del router está en el rango 100.64.0.0 – 100.127.255.255 (RFC 6598), estás detrás de CGNAT y port forwarding no funcionará. Necesitarás una de las otras vías.
El bloque 100.64.0.0/10 está reservado por IANA específicamente para CGNAT; ningún host legítimo usa esas IPs en internet abierto. Si tu router la tiene, el operador te está ocultando.
Configurar port forwarding en el router del ISP
Cada router del ISP tiene una interfaz distinta, pero todos comparten el mismo patrón: una sección llamada “Port Forwarding”, “NAT / Virtual Server” o “Reenvío de puertos”. El flujo es siempre:
- Asignar una IP fija (estática o por DHCP reservation) al servidor del home lab en tu red local.
- En la sección de port forwarding, crear una regla: puerto externo (el que ve internet) → IP local del servidor → puerto interno.
- Guardar y reiniciar el router si lo pide.
Si tu servidor corre Debian/Ubuntu y quieres fijar la IP local por DHCP reservation, necesitas la MAC de la interfaz de red. La obtienes con ip link show y buscas la línea link/ether. En mi caso, asigno 192.168.1.50 al servidor y abro los puertos 80, 443 y 22 hacia esa IP. El 22 lo abro solo cuando necesito SSH remoto y lo cierro el resto del tiempo; el 80 y 443 son para tráfico web.
# Ver la MAC de la interfaz de red del servidor
ip -br link show eth0
# Salida ejemplo: eth0 UP 52:54:00:ab:cd:ef 192.168.1.50
# Asignar IP estática alternativa en el propio servidor (si el router no soporta DHCP reservation)
sudo nmcli con mod "Wired connection 1" ipv4.method manual
sudo nmcli con mod "Wired connection 1" ipv4.addresses 192.168.1.50/24
sudo nmcli con mod "Wired connection 1" ipv4.gateway 192.168.1.1
sudo nmcli con mod "Wired connection 1" ipv4.dns "1.1.1.1 8.8.8.8"
sudo nmcli con up "Wired connection 1"
Si tu ISP te da un router sin opciones avanzadas (típico en fibra residencial con routers de marca propia tipo HGU, Sercomm o Mitrastar), tienes dos opciones reales: llamar al ISP para pedir un router en modo bridge o comprar uno neutro (Mikrotik, Ubiquiti, GL.iNet) y ponerlo detrás. El router neutro te da control total, VLANs, port forwarding granular, scripts de automatización y logs.
DDNS: mantener el dominio apuntando a tu IP
Si tu IP cambia cada 24-48h, un dominio normal no te sirve. Necesitas DNS Dinámico: un cliente que actualiza el registro A cada vez que la IP cambia. Los proveedores gratuitos más sólidos en 2026 son DuckDNS (subdominios *.duckdns.org), afraid.org (subdominios sobre 80+ dominios públicos) y No-IP (subdominios *.hopto.org, *.ddns.net). Si ya tienes dominio propio en Cloudflare, su API también soporta DDNS directo sin servicio externo.
DuckDNS es la opción más simple para empezar. Te registras, eliges un nombre (ej. miservidor.duckdns.org) y obtienes un token. El cliente es un script bash que llamas cada 5 minutos por systemd timer; el script detecta tu IP pública y se la pasa a DuckDNS por HTTPS.
# /usr/local/bin/duckdns-update.sh
#!/usr/bin/env bash
set -euo pipefail
DOMAIN="miservidor"
TOKEN="tu-token-aqui"
IP=$(curl -fsS https://ifconfig.me)
RESULT=$(curl -fsS "https://www.duckdns.org/update?domains=${DOMAIN}&token=${TOKEN}&ip=${IP}")
echo "$(date -Iseconds) ${RESULT}"
# Salida esperada: OK o KO. KO significa que la IP no cambió; OK significa que se actualizó.
El cron de systemd timer es preferible al clásico crontab porque registra logs en journald y se gestiona con systemctl. Creamos dos archivos: el service (qué ejecutar) y el timer (cuándo).
# /etc/systemd/system/duckdns.service
[Unit]
Description=Actualiza DuckDNS con la IP publica actual
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/duckdns-update.sh
User=nobody
# /etc/systemd/system/duckdns.timer
[Unit]
Description=Actualiza DuckDNS cada 5 minutos
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
AccuracySec=30s
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now duckdns.timer
systemctl list-timers duckdns.timer
Si prefieres Cloudflare porque tienes dominio propio ahí, la actualización es similar pero contra la API v4 de Cloudflare. Primero obtienes el zone ID y el record ID, luego un script que llama PUT /zones/{zone}/dns_records/{record} con la nueva IP. El código y los gotchas del token account-scoped vs zone-scoped están en otro artículo; lo esencial aquí: el token zone-scoped con permiso Zone.DNS:Edit es lo que necesitas, y el script corre con la misma frecuencia.
Caddy como reverse proxy con HTTPS automático
Caddy es mi recomendación sobre nginx porque gestiona los certificados de Let’s Encrypt solos sin que toques cron ni scripts de renovación. Un Caddyfile de 6 líneas te da HTTPS automático con HTTP-01 challenge, siempre que el puerto 80 esté abierto hacia internet. Caddy 2.8 sigue siendo la línea principal en 2026 y viene en los repos oficiales de Debian 12/Ubuntu 24.04.
# Instalar Caddy en Debian/Ubuntu
sudo apt install -y caddy
# Habilitarlo y arrancarlo
sudo systemctl enable --now caddy
El Caddyfile vive en /etc/caddy/Caddyfile. Este ejemplo expone Nextcloud, Jellyfin y Portainer, cada uno en un subdominio distinto, con proxy inverso y HTTPS automático. Cada bloque reverse_proxy le dice a Caddy qué servicio local escuchar y dónde reenviar el tráfico.
# /etc/caddy/Caddyfile
cloud.miservidor.duckdns.org {
reverse_proxy 192.168.1.50:8080
}
media.miservidor.duckdns.org {
reverse_proxy 192.168.1.51:8096
}
docker.miservidor.duckdns.org {
reverse_proxy 192.168.1.50:9000
}
# Opcional: Basic Auth para Portainer para que no quede expuesto
docker.miservidor.duckdns.org {
basicauth {
admin $2a$14$...hash-bcrypt-aqui...
}
reverse_proxy 192.168.1.50:9000
}
Para que Let’s Encrypt emita el certificado, el puerto 80 del router debe apuntar al puerto 80 de la máquina donde corre Caddy. Caddy escucha en 80 y 443 por defecto. Tras guardar el Caddyfile, valida la sintaxis con caddy validate --config /etc/caddy/Caddyfile y recarga con systemctl reload caddy. Los certificados aparecen en /var/lib/caddy/.local/share/caddy/certificates/ y se renuevan automáticamente cuando faltan 30 días para expirar.
Si tu ISP bloquea el puerto 80 de salida o de entrada (algunos lo hacen para “fomentar” planes empresariales), Let’s Encrypt HTTP-01 no funciona y necesitas DNS-01. Caddy lo soporta con módulos extra como caddy-dns/cloudflare: el plugin se autentica contra Cloudflare y crea un registro TXT temporal que prueba que controlas el dominio. La instalación se hace compilando Caddy con xcaddy y añadiendo el módulo; el plugin oficial está en github.com/caddy-dns/cloudflare.
Vía 2: nginx + certbot (la alternativa clásica)
Si tu casa o tu equipo ya está sobre nginx, la combinación nginx + certbot es equivalente en potencia a Caddy, pero te toca configurar la renovación automática. El flujo es instalar nginx, definir un server block con proxy_pass hacia tu servicio local, y usar certbot con el plugin standalone o webroot para emitir el certificado.
# Instalar nginx y certbot
sudo apt install -y nginx certbot python3-certbot-nginx
# Crear el server block
sudo tee /etc/nginx/sites-available/cloud.conf <<EOF
server {
server_name cloud.miservidor.duckdns.org;
listen 80;
location / {
proxy_pass http://192.168.1.50:8080;
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;
}
}
EOF
sudo ln -s /etc/nginx/sites-available/cloud.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
# Emitir certificado con certbot (modifica el server block para añadir la sección 443)
sudo certbot --nginx -d cloud.miservidor.duckdns.org
# Renovación automática: certbot añade un timer de systemd o cron entry
sudo systemctl status certbot.timer
Certbot con --nginx modifica automáticamente tu server block para añadir la escucha en 443, el certificado y la redirección HTTP→HTTPS. La renovación se hace con un timer de systemd que corre dos veces al día y renueva si faltan menos de 30 días para expirar.
Caddy vs nginx en home lab: Caddy te ahorra el certificado, nginx te da control fino sobre directivas avanzadas (caching, rate limiting, geo-blocking). Para una sola IP pública y 3-5 servicios, Caddy es más rápido de configurar; si ya dominas nginx, no hay razón de migrar.
Vía 3: Cloudflare Tunnel (la vía sin IP pública)
Cloudflare Tunnel resuelve el problema raíz: si no tienes IP pública o no quieres exponer tu IP, un daemon llamado cloudflared abre una conexión saliente persistente hacia la red de Cloudflare (puerto 7844, TCP). Cloudflare la usa para recibir tráfico HTTP/HTTPS de tus visitantes y reenviarlo por ese túnel hasta tu servidor. Tu IP nunca aparece en DNS, nunca se publica, y no abres ningún puerto entrante.
El binario cloudflared usa versionado CalVer (YYYY.M.P); la línea de releases actual llega a versiones 2026.x a fecha de este artículo y está disponible en los repos oficiales de Debian/Ubuntu/Arch y como binario standalone para ARM. Lo instalas, lo autenticas contra tu cuenta de Cloudflare, creas un tunnel con un nombre y configuras las rutas.
# Instalar cloudflared en Debian/Ubuntu
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared bookworm main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install -y cloudflared
# Autenticarse (abre el navegador)
cloudflared tunnel login
# Crear un tunnel
cloudflared tunnel create homelab
# Salida: Tunnel credentials written to /home/usuario/.cloudflared/<TUNNEL-ID>.json
# Definir las rutas DNS
cloudflared tunnel route dns homelab cloud.midominio.com
# Crear config.yml con los servicios
sudo mkdir -p /etc/cloudflared
sudo tee /etc/cloudflared/config.yml <<EOF
tunnel: <TUNNEL-ID>
credentials-file: /home/usuario/.cloudflared/<TUNNEL-ID>.json
ingress:
- hostname: cloud.midominio.com
service: http://192.168.1.50:8080
- hostname: media.midominio.com
service: http://192.168.1.51:8096
- service: http_status:404
EOF
# Arrancar el servicio
sudo cloudflared service install
sudo systemctl enable --now cloudflared
sudo systemctl status cloudflared
Cada bloque ingress asocia un subdominio a un servicio local. El último bloque http_status:404 es obligatorio y captura cualquier hostname que no hayas definido (responde 404). Sin él, cloudflared rechaza arrancar.
Cloudflare Tunnel soporta HTTP y TCP de forma nativa. SSH funciona como TCP, expones el puerto 22 local y te conectas con cloudflared access ssh --hostname ssh.midominio.com o con el cliente SSH directo si configuras el binario cloudflared como ProxyCommand. UDP es más limitado: solo se puede enrutar UDP si el servicio habla QUIC o HTTP/3, o si usas el patrón tcp:// sobre el puerto UDP (Cloudflare hace la conversión). Para WireGuard o juegos UDP, esta vía no es ideal.
Limitaciones reales: el free tier de Cloudflare permite tunnels ilimitados pero con rate limits agresivos para uso “anómalo” (streaming de video pesado, muchos miles de requests/min). Para uso normal de servicios self-hosted (Nextcloud, Jellyfin, Gitea, dashboards) funciona perfectamente. La latencia se añade porque cada request pasa por un edge de Cloudflare; en práctica son 10-30ms extra sobre conexión directa.
Para Jellyfin específicamente: si vas a exponerlo a internet, lo más eficiente hoy es combinar Caddy como reverse proxy (TLS automático y HTTP/2) en vez de depender solo del tunnel. Si además quieres el servidor actualizado, en septiembre 2026 se liberó Jellyfin 12 con cambios importantes en la base de datos y transcodificación; revisa esa guía antes de actualizar.
Vía 4: Tailscale Funnel (la vía cuando ya usas Tailscale)
Tailscale es una mesh VPN basada en WireGuard. Si ya tienes una red Tailscale con tus dispositivos (servidor del home lab, laptop, móvil), Tailscale Funnel te permite exponer un servicio de esa mesh a internet público sin abrir puertos. La diferencia con Cloudflare Tunnel es que Tailscale termina el TLS con un subdominio *.ts.net gestionado por ellos, y solo está disponible para usuarios del plan Personal y superiores.
# Activar Tailscale Funnel en el servidor (asume que ya corres Tailscale).
# El comando clave es `tailscale funnel `; el flag --advertise-funnel-ports
# se configura por nodo desde el admin console (https://login.tailscale.com/admin/machines).
# Habilitar Funnel para un servicio
sudo tailscale funnel 8080
# Salida ejemplo: https://hostname.tailnet-name.ts.net
# O sobre un subdominio propio (config en Tailscale admin)
# https://cloud.midominio.com via Tailscale
# Comprobar estado
tailscale funnel status
# Para apagar
sudo tailscale funnel --off 8080
El comando tailscale funnel abre el puerto local a internet a través de la infraestructura de Tailscale. El tráfico entra por HTTPS en un nodo de Tailscale y se enruta por la mesh hasta tu servidor. Como Cloudflare Tunnel, la IP pública nunca se expone.
Tailscale Funnel solo soporta TCP (HTTP, HTTPS, SSH). UDP no está soportado nativamente; para WireGuard o juegos necesitas Tailscale normal (mesh privada) o pasar a la vía 1/3. La ventaja frente a Cloudflare Tunnel: si ya tienes Tailscale configurado, añadir Funnel es un comando. La desventaja: el subdominio por defecto es *.ts.net; para dominio propio necesitas el plan de pago o configurar CNAME manualmente.
Vía 5: VPS como relay con WireGuard (cuando CGNAT es severo)
Si tu ISP te pone detrás de CGNAT y necesitas UDP (WireGuard público, servidor de Minecraft, juego, VoIP), Cloudflare Tunnel no te sirve. La solución clásica: contratas un VPS barato (~5 USD/mes en Hetzner, DigitalOcean, Vultr) con IP pública, levantas un tunnel WireGuard entre el VPS y tu home lab, y expones el servicio en el VPS. El tráfico sale del VPS (que sí tiene IP pública), entra por el tunnel WireGuard cifrado hasta tu servidor, y vuelve por el mismo camino.
El setup tiene tres piezas:
- VPS: WireGuard listening en UDP 51820, IP pública del VPS es el endpoint.
- Home lab: WireGuard client que mantiene una conexión persistente al VPS. La interfaz
wg0tiene una IP privada (típicamente10.0.0.2/24). - Servicio: corre en el home lab. El VPS hace DNAT/SNAT o proxy inverso para enviar tráfico al
10.0.0.2:puerto.
# En el VPS - /etc/wireguard/wg0.conf
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <VPS-PRIVATE-KEY>
PostUp = iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 10.0.0.2:443
PostUp = iptables -t nat -A POSTROUTING -j MASQUERADE
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 10.0.0.2:443
PostDown = iptables -t nat -D POSTROUTING -j MASQUERADE
[Peer]
PublicKey = <HOME-PUBLIC-KEY>
AllowedIPs = 10.0.0.2/32
# En el home lab - /etc/wireguard/wg0.conf
[Interface]
Address = 10.0.0.2/24
PrivateKey = <HOME-PRIVATE-KEY>
[Peer]
PublicKey = <VPS-PUBLIC-KEY>
Endpoint = vps.midominio.com:51820
PersistentKeepalive = 25
AllowedIPs = 10.0.0.1/32
El PersistentKeepalive = 25 es crítico: envía un paquete cada 25 segundos para mantener la conexión NAT activa en el ISP. Sin esto, el NAT cierra la conexión tras 60-120s y el tunnel se cae. Con CGNAT, este valor evita la mitad de los problemas.
El coste (~$5/mes) es el trade-off: tienes IP pública garantizada, control total, UDP completo, y puedes apagar todo cuando quieras. Para CGNAT sin escapatoria es la única vía que funciona con UDP.
Vía 6: IPv6 directo (cuando tu ISP te lo da)
IPv6 elimina el problema de CGNAT por diseño: cada dispositivo tiene una dirección pública global única. Si tu ISP te asigna un prefijo IPv6 estable (típicamente /48 o /56), puedes exponer servicios directamente sin NAT, sin port forwarding, y sin túnel. El router hace de firewall por defecto (drop inbound) y tú abres los puertos específicos.
El problema: la mayoría de visitantes todavía llegan por IPv4. Según el Cloudflare Radar 2025 Year in Review, solo el 29% de las peticiones dual-stack llegan por IPv6, así que todavía ~71% del tráfico es IPv4. Un servicio expuesto solo por IPv6 será inaccesible para una parte significativa de tus usuarios. La solución es dual-stack: expones por IPv4 (vía 1, 3 o 5) y por IPv6 simultáneamente, y DNS devuelve ambos registros (A + AAAA).
# Verificar que tu servidor tiene IPv6 pública
ip -6 addr show
# Busca una dirección que empiece por 2001:4860, 2a02:..., 2601:..., 2806:... (no fe80:)
# Verificar salida IPv6
curl -6 https://ifconfig.co
# Configurar firewall para abrir el puerto al tráfico IPv6 entrante
sudo ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
sudo ip6tables -A INPUT -i lo -j ACCEPT
sudo ip6tables -P INPUT DROP
En routers Mikrotik, Ubiquiti y OpenWrt, IPv6 inbound se configura con reglas equivalentes a IPv4 (filter rules en input chain). Asegúrate de que el firewall del ISP no esté bloqueando incoming IPv6; algunos routers residenciales bloquean por defecto.
Seguridad: lo que no puedes saltarte
Exponer un servicio a internet sin pensar en seguridad es invitar al primer escaneo automatizado que te encuentre. Las tres medidas mínimas son obligatorias, no opcionales:
SSH: solo clave ed25519, sin contraseña
SSH expuesto a internet sin clave es un imán. Los bots prueban miles de combinaciones por segundo. La configuración mínima:
# Generar clave ed25519 en el cliente
ssh-keygen -t ed25519 -C "tu-email"
# Copiar al servidor (una sola vez)
ssh-copy-id usuario@servidor
# En el servidor, endurecer /etc/ssh/sshd_config
sudo tee -a /etc/ssh/sshd_config.d/hardening.conf <<EOF
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
KbdInteractiveAuthentication no
X11Forwarding no
AllowUsers tu-usuario
EOF
sudo sshd -t && sudo systemctl reload ssh
fail2ban o CrowdSec contra brute force
fail2ban lleva años siendo el estándar. Lee logs del sistema y banea IPs tras N intentos fallidos. CrowdSec es el sucesor moderno: misma idea (baneo por comportamiento) pero con una red comunitaria donde compartes las IPs atacantes y recibes IPs ya baneadas por otros.
# fail2ban - instalación y activación de jail SSH
sudo apt install -y fail2ban
sudo tee /etc/fail2ban/jail.local <<EOF
[sshd]
enabled = true
maxretry = 3
findtime = 600
bantime = 3600
EOF
sudo systemctl enable --now fail2ban
# CrowdSec - alternativa moderna
curl -fsSL https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash
sudo apt install -y crowdsec
sudo cscli collections install crowdsecurity/sshd
sudo systemctl enable --now crowdsec
Para un servidor nuevo en 2026, la recomendación práctica es CrowdSec: instalación en 30 segundos, dashboard web opcional, escenarios preconfigurados para SSH/web, y la red comunitaria te protege desde el primer minuto. fail2ban sigue siendo sólido si ya lo tienes configurado y funcionando.
HTTPS siempre, sin excepciones
Cualquier servicio web expuesto sin HTTPS puede ser interceptado en redes públicas. Let’s Encrypt emite certificados gratis y Caddy/nginx los renuevan solos. No hay razón para servir HTTP plano: ni siquiera para una página interna, porque las credenciales viajan sin cifrar.
El árbol de decisión
Con las seis vías y la seguridad mínima cubiertas, así decides en qué orden probar:
- ¿Tienes IP pública? Si no, salta a 4 (Cloudflare Tunnel) o 5 (VPS+WireGuard si necesitas UDP).
- ¿Necesitas HTTPS automático y configuración rápida? Vía 1 con Caddy.
- ¿Ya dominas nginx? Vía 2 con certbot.
- ¿Quieres esconder tu IP pública por privacidad? Vía 3 (Cloudflare Tunnel) o vía 4 (Tailscale Funnel).
- ¿Tu ISP está detrás de CGNAT y necesitas UDP? Vía 5 (VPS+WireGuard).
- ¿Tu ISP te da IPv6 estable? Combínalo con la vía 1 en dual-stack.
Para la mayoría de home labs sin CGNAT, la combinación vía 1 (port forwarding + DuckDNS + Caddy) cubre el 90% de los casos y te deja control total sobre cualquier protocolo. Si tienes CGNAT, Cloudflare Tunnel (vía 3) resuelve el 80% de los servicios sin más infraestructura; cuando necesites UDP, VPS+WireGuard (vía 5) es el último recurso.
Resumen
Exponer un servicio a internet en 2026 no es una decisión única sino una secuencia de tres: detectar si tienes IP pública, elegir la vía según las restricciones de tu ISP y el tipo de tráfico, y endurecer SSH + fail2ban/CrowdSec + HTTPS antes de abrir nada. La pila recomendada para empezar es port forwarding + DuckDNS + Caddy por su simplicidad, con Cloudflare Tunnel como plan B cuando CGNAT entra en juego.
Lo que NO debes hacer: abrir puertos sin certificados, dejar SSH con password, exponer servicios de administración (Portainer, phpMyAdmin, dashboards) sin Basic Auth o Cloudflare Access, ni publicar tu IP pública en redes sociales o logs públicos sin pensarlo dos veces.
Preguntas frecuentes
¿Por qué Exponer servicios de tu home lab a internet: 6 vías explicadas es relevante para sysadmins Linux?
Forma parte del flujo diario de administración. Los conceptos explicados se aplican directamente a servidores en producción y resuelven problemas reales que enfrentan sysadmins hispanohablantes.
¿Qué necesito para empezar con Exponer servicios de tu home?
Distribución Linux actualizada (Debian, Ubuntu, RHEL, Fedora o Arch), acceso root, y conexión a internet. Para producción: entorno de staging separado antes de aplicar cambios.
¿Cuáles son los errores más comunes con Exponer servicios de tu home?
Los tres más frecuentes: (1) saltar la lectura de logs, (2) aplicar cambios directos en producción sin probar, (3) olvidar permisos y ownership. La documentación y un plan de rollback siempre.
¿Cuándo usar Exponer servicios de tu home y cuándo una alternativa?
Esta solución es ideal para el caso descrito. Alternativas como Docker, Ansible o configuración manual pueden ser mejores según contexto: portabilidad, escala y reproducibilidad son los factores clave.
¿Cómo migro desde una configuración previa a Exponer servicios de tu home?
Documentar el estado actual, probar la nueva configuración en staging, validar funcionalmente, y aplicar en producción con rollback plan listo. Los posts de SoyLinux incluyen troubleshooting para los problemas más comunes.
Lee también
- Guía Fedora 44: Novedades, Instalación y Upgrade para Sysadmins Linux
- Configura VLANs en Linux con nmcli: Guía paso a paso para sysadmins
- Protege tu SSH: Guía esencial para configurar Fail2ban en Linux
- Guía Definitiva: Configura Kubernetes con kubeadm en Ubuntu para Sysadmins Linux
- Configura Prometheus y Grafana: Guía DevOps para SysAdmins Linux

Pingback: Optimiza redes de centro de datos: cómo liberar hasta 30% de CPU host con NVIDIA BlueField + OpenShift - Soy Linux