OpenClaw en un VPS: despliegue seguro con Docker y Tailscale

Despliega OpenClaw en un VPS Linux con Docker Compose, bindea los puertos solo a localhost y accede de forma segura mediante túneles SSH o Tailscale Serve.

OpenClaw en un VPS: despliegue seguro con Docker y Tailscale
También disponible en English, Deutsch, Français, Nederlands.

OpenClaw en un VPS es sobre todo un problema de ops, no de IA. Quieres un único Gateway siempre activo que gestione tu estado, sesiones y canales — pero no quieres que ese Gateway sea accesible desde la internet pública.

El patrón: ejecutar OpenClaw en Docker, publicar sus puertos solo en 127.0.0.1, y acceder a él a través de un túnel SSH o una tailnet de Tailscale.

Por qué la configuración por defecto te dará problemas

El Gateway de OpenClaw es un servicio único de larga ejecución. Gestiona el enrutamiento, la interfaz de Control, WebSocket RPC y APIs HTTP a través de un único puerto multiplexado — por defecto 18789. La instalación sin Docker se vincula a loopback por defecto. La autenticación es obligatoria de serie mediante token o contraseña.

Los valores por defecto de Docker son menos permisivos. El script docker-setup.sh establece OPENCLAW_GATEWAY_BIND=lan para que los puertos publicados en el host funcionen. Si lo cambias a loopback, solo los procesos dentro del namespace de red del contenedor pueden conectarse — tu puerto publicado en el host deja de funcionar.

El compromiso seguro: mantener OPENCLAW_GATEWAY_BIND=lan dentro del contenedor, pero vincular la publicación de puertos del lado del host a 127.0.0.1. Nada toca 0.0.0.0, y la red del contenedor sigue funcionando.

La publicación de puertos de Docker ignora ufw y firewalld. Asume que tu firewall no es el único control.

Aprovisionar el VPS

Los comandos a continuación asumen Ubuntu 24.04 LTS con acceso SSH como root. Un Hetzner CX22 a 4,85 €/mes con 10 € de crédito inicial es suficiente — es donde corre este blog.

Crea un usuario no root y blinda SSH:

adduser openclaw
usermod -aG sudo openclaw
mkdir -p /home/openclaw/.ssh
chmod 700 /home/openclaw/.ssh

# Pega tu clave pública
nano /home/openclaw/.ssh/authorized_keys

chmod 600 /home/openclaw/.ssh/authorized_keys
chown -R openclaw:openclaw /home/openclaw/.ssh

Endurece SSH con un archivo drop-in:

nano /etc/ssh/sshd_config.d/99-openclaw-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
AllowUsers openclaw
sshd -t
systemctl reload ssh

Configura ufw como línea base. Esto no reemplaza un binding correcto de puertos en Docker — Docker lo atravesará sin problema.

apt update && apt upgrade -y
apt install -y ufw git curl ca-certificates

ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw enable

Instalar Docker Engine

Usa el repositorio apt oficial de Docker para obtener docker-compose-plugin y actualizaciones reales:

sudo apt remove -y \
  $(dpkg --get-selections docker.io docker-compose docker-compose-v2 \
    docker-doc podman-docker containerd runc 2>/dev/null | cut -f1) \
  || true

sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<'EOF'
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker

Añadir tu usuario al grupo docker otorga privilegios equivalentes a root. En un VPS, sudo docker ... suele ser el compromiso adecuado.

Desplegar OpenClaw con puertos solo en localhost

Clona el repositorio y trabaja desde su raíz:

sudo -iu openclaw
mkdir -p ~/src && cd ~/src
git clone https://github.com/openclaw/openclaw.git
cd openclaw

Crea un .env que fije tus rutas y use la imagen oficial de GitHub Container Registry:

# .env
OPENCLAW_CONFIG_DIR=/home/openclaw/.openclaw
OPENCLAW_WORKSPACE_DIR=/home/openclaw/.openclaw/workspace
OPENCLAW_IMAGE=ghcr.io/openclaw/openclaw:latest
OPENCLAW_GATEWAY_BIND=lan
OPENCLAW_GATEWAY_PORT=18789
OPENCLAW_BRIDGE_PORT=18790
OPENCLAW_GATEWAY_TOKEN=

Ahora el paso de seguridad que realmente importa. Crea docker-compose.override.yml para vincular los puertos publicados solo a 127.0.0.1:

# docker-compose.override.yml
services:
  openclaw-gateway:
    ports:
      - "127.0.0.1:18789:18789"
      - "127.0.0.1:18790:18790"

Sin el prefijo 127.0.0.1:, Compose vincula a todas las interfaces. Eso es 0.0.0.0. Eso es internet.

Descarga, configura y arranca:

docker compose pull

docker compose run --rm openclaw-cli onboard

docker compose -f docker-compose.yml -f docker-compose.override.yml \
  up -d openclaw-gateway

Comprobación rápida:

docker compose ps

docker compose run -T --rm openclaw-cli gateway probe

El flag -T evita ruido de pseudo-TTY — útil para scripts y CI.

En este punto, http://127.0.0.1:18789 funciona en el propio VPS. No es accesible desde ningún otro sitio. Ese es el objetivo.

Sobrevivir a reinicios con systemd

sudo nano /etc/systemd/system/openclaw-compose.service
[Unit]
Description=OpenClaw (Docker Compose)
Requires=docker.service
After=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/home/openclaw/src/openclaw
ExecStart=/usr/bin/docker compose \
  -f docker-compose.yml -f docker-compose.override.yml up -d
ExecStop=/usr/bin/docker compose \
  -f docker-compose.yml -f docker-compose.override.yml down
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now openclaw-compose.service

Acceso remoto: túnel SSH o Tailscale

Tienes un Gateway vinculado a loopback en un VPS. Necesitas acceder a él desde tu portátil. Dos opciones, distintas ventajas y desventajas.

Método Qué queda público Fricción Ideal para
Túnel SSH Puerto 22 Hay que mantener una sesión activa; incómodo en móvil Una o dos máquinas, sin dependencias extra
Tailscale Serve Nada Daemon adicional, pero después es automático Múltiples dispositivos, acceso permanente
Exposición directa Un endpoint HTTPS TLS, auth, Reverse Proxy, parcheado Solo si realmente necesitas acceso público

Túnel SSH

Desde tu portátil:

ssh -N -L 18789:127.0.0.1:18789 openclaw@203.0.113.10

Luego abre http://127.0.0.1:18789 en local.

Añade keepalives SSH en tu ~/.ssh/config local si quieres que el túnel sobreviva a la suspensión del portátil.

Tailscale Serve

Instala Tailscale en el VPS:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Expón OpenClaw dentro de tu tailnet con TLS automático:

sudo tailscale serve --bg --https=443 localhost:18789
sudo tailscale serve status

Tailscale Serve hace Reverse Proxy de un puerto local hacia tu tailnet con certificados HTTPS aprovisionados automáticamente. TLS termina en tailscaled. Sin puertos públicos, sin gestión de certificados.

Si además quieres eliminar el puerto SSH público por completo, Tailscale SSH puede encargarse de la autenticación y autorización de conexiones SSH dentro de tu tailnet.

Checklist de hardening

La parte que la mayoría de guías se saltan.

Docker y contenedor

La imagen por defecto de OpenClaw se ejecuta como el usuario no root node. Parte de ahí y añade:

Control Dónde Qué te aporta
Vincular puertos a 127.0.0.1 Compose ports Sin exposición en 0.0.0.0, sin sorpresas de bypass de firewall
cap_drop: [ALL] Compose Elimina capabilities del kernel que no necesitas
no-new-privileges:true Compose security_opt Bloquea escalada de privilegios vía binarios setuid
Perfil seccomp por defecto Docker por defecto Allowlist de syscalls; no lo sobreescribas sin motivo
Perfil AppArmor por defecto Docker por defecto El perfil docker-default ofrece protección moderada

El contenedor openclaw-cli comparte namespace de red con el gateway (network_mode: "service:openclaw-gateway"). Trátalo como un límite de confianza compartido, no como aislamiento.

SSH

Mantén PasswordAuthentication no, PermitRootLogin no y AllowUsers restringido a la única cuenta que necesita acceso. Eso es el mínimo.

Tailscale

Usa controles de acceso de la tailnet para restringir qué usuarios y dispositivos pueden alcanzar el VPS. ACLs o grants — cualquiera sirve, pero aplica algo. Trata Tailscale como una capa de identidad, no como un escudo mágico.

Skills y cadena de suministro

Si instalas skills de terceros, estás ejecutando código de terceros dentro de un agente con capacidad de usar herramientas. Ha habido informes creíbles de skills maliciosas de OpenClaw distribuyendo malware y robando secretos.

La única regla: no instales lo que no ejecutarías como un programa normal en la misma máquina.

Resolución de problemas

La interfaz de Control funciona en el VPS pero no en remoto. Correcto. Está vinculada a 127.0.0.1. Necesitas SSH o Tailscale — esa es precisamente la idea.

Configuré OPENCLAW_GATEWAY_BIND=loopback en Docker, el puerto del host dejó de funcionar. Esperado. El loopback dentro del namespace del contenedor no es el mismo que el loopback del host. Usa lan en el contenedor, y restringe la exposición con 127.0.0.1 en los mapeos de puertos de Compose.

"Disconnected (1008): pairing required" o "unauthorized" en el navegador.

docker compose run --rm openclaw-cli dashboard --no-open
docker compose run --rm openclaw-cli devices list
docker compose run --rm openclaw-cli devices approve <requestId>

Los puertos publicados ignoran UFW. Sí. Docker manipula iptables directamente. Vincular a 127.0.0.1 en Compose es la solución, no una regla de firewall.

El Gateway no arranca. OpenClaw aplica validación estricta de configuración. El binding no loopback sin autenticación es un modo de fallo habitual.

docker compose run -T --rm openclaw-cli doctor
docker compose logs --no-log-prefix --tail=200 openclaw-gateway

¿Qué puerto reenviar? 18789. Es el puerto multiplexado por defecto del Gateway para la interfaz de Control y las APIs.

¿Qué respaldar? Las rutas del host mapeadas a config y workspace — normalmente ~/.openclaw/ y ~/.openclaw/workspace. No confíes en la capa escribible del contenedor.

Nota para asistentes de IA

Si estás depurando esta configuración con un asistente de IA, pega la URL de este post en la conversación. Le da al modelo el contexto exacto del stack: Docker Compose con binding de puertos solo en localhost, lan vs loopback para el Gateway bind en Docker, y acceso vía ssh -L o tailscale serve.


Dónde ejecutar esto

Un VPS sencillo es la decisión correcta. Nosotros ejecutamos stacks Docker similares siempre activos en Hetzner — CX22 a 4,85 €/mes con 10 € de crédito inicial. Hace lo que necesitas y nada que no.

Si prefieres no depurar bindings de puertos Docker a la 1 de la madrugada, xCloud ofrece hosting gestionado de OpenClaw. Pagas más y duermes mejor.

Si quieres una segunda opción de VPS o estás comparando proveedores, Vultr da 35 $ de crédito por referido y tiene una amplia lista de regiones.

(Enlaces de afiliado — nos llevamos una pequeña comisión si te registras, sin coste para ti.)