OpenClaw: instalación oficial (Quadlet) vs distrobox — lo que la documentación no te cuenta
TL;DR: OpenClaw recomienda instalar con su script oficial (run-openclaw-podman.sh) que usa Quadlet + --userns=keep-id. Yo lo instalé en un container distrobox ya existente. Los dos funcionan, pero tienen trade-offs muy distintos en seguridad, permisos, y mantenimiento. Aquí va la comparativa real, no la de marketing.
Las dos rutas
Ruta A: Instalación oficial (Quadlet)
OpenClaw incluye scripts para Podman en su paquete npm. El proceso:
# 1. Build de la imagen
./scripts/podman/setup.sh
# 2. Con Quadlet (systemd nativo)
./scripts/podman/setup.sh --quadlet
# 3. Lanzar
./scripts/run-openclaw-podman.sh launch
Esto crea:
- Imagen
openclaw:local(basada en la imagen oficial de OpenClaw) - Container con
--userns=keep-id --user <tu_uid>:<tu_gid> - Quadlet en
~/.config/containers/systemd/openclaw.container - Token en
~/.openclaw/.env - Puertos publicados en
127.0.0.1:18789y127.0.0.1:18790
Ruta B: Distrobox
Yo ya tenía un container distrobox con Debian Bookworm para otros proyectos. Instalé OpenClaw dentro:
# Dentro del container
npm install -g openclaw
openclaw configure
Y creé un systemd service manual:
# ~/.config/systemd/user/openclaw-gateway.service
ExecStart=/usr/bin/podman exec -u pablo openclaw bash -c '...'
La comparativa real
| Aspecto | Oficial (Quadlet) | Distrobox |
|---|---|---|
| Imagen | openclaw:local (propias, optimizada) | debian:bookworm (genérica) |
| UserNS | keep-id (mapea UIDs automáticamente) | private (sin mapeo) |
| Usuario | Tu uid:gid explícito | Root por defecto, necesita -u pablo |
| Privileged | No | Sí |
| Bind mounts | Solo ~/.openclaw | 28 mounts (todo /home, /tmp, /usr…) |
| Puertos | Solo 18789 + 18790 (en 127.0.0.1) | Hereda del host |
| Systemd | Quadlet nativo (openclaw.service) | Service manual |
| Token | Auto-generado en ~/.openclaw/.env | Manual |
| Updates | Rebuild de imagen | npm update -g openclaw |
| Tamaño | Imagen dedicada (~500MB) | Imagen compartida con otros tools |
| Seguridad | Hardened por defecto | Permisivo (privileged, full mounts) |
| Permisos plugins | Automático (keep-id) | Manual (-u pablo) |
El problema que no te cuenta la documentación
La documentación oficial dice:
“Permission denied (EACCES) on config or workspace: The container runs with –userns=keep-id and –user : by default.”
Es decir: si usas el script oficial, esto no te pasa. Los UIDs se mapean solos. Plugins, config, workspace — todo pertenece al usuario correcto automáticamente.
La realidad con distrobox: 3 horas de debug
El container corre como root. Los archivos del host son de tu usuario. OpenClaw detecta el mismatch y bloquea todos los plugins externos:
WARN plugin codex: blocked plugin candidate: suspicious ownership
(/home/pablo/.openclaw/npm/node_modules/@openclaw/codex, uid=1000, expected uid=0 or root)
Lo que intenté (y no funcionó)
chown -R root:root — Funcionó hasta que instalé algo desde el host o ejecuté openclaw doctor --fix. Los archivos volvían a UID 1000. Y al reiniciar: bloqueados.
Instalar desde dentro del container — Los archivos se creaban como root ✅, pero el CLI del host decía “plugin not found” ❌. El host valida contra su propio filesystem.
El círculo vicioso:
Host instala → archivos UID 1000 → container bloquea "suspicious ownership"
Container instala → archivos root → host CLI dice "plugin not found"
La solución: -u pablo
Distrobox crea un usuario dentro del container con el mismo UID que tu usuario del host. En mi caso, pablo (UID 1000) existe en ambos lados.
# Verificar
podman exec openclaw grep pablo /etc/passwd
# pablo::1000:1000:Pablo:/home/pablo:/usr/bin/fish
# Solución: un flag
podman exec -u pablo openclaw bash -c 'openclaw gateway run ...'
Un flag. Tres letras. El gateway corre como pablo dentro del container. Los archivos que crea son pablo:pablo. Desde el host también son pablo:pablo. Cero conflicto.
Pero esto no está documentado en ningún sitio. Lo encontré por trial-and-error después de 3 horas.
El issue de GitHub
Encontré un reporte similar en openclaw/openclaw#33685: en Fedora Silverblue, el script oficial también fallaba con permisos porque el UID mapping no funcionaba con su configuración de storage. El workaround: quitar UserNS=keep-id y cambiar ownership manualmente. Mismo problema, distro diferente.
¿Cuándo elegir cada ruta?
Elige la ruta oficial si:
- ✅ Estás instalando OpenClaw por primera vez
- ✅ No tienes un container existente
- ✅ Quieres la configuración más segura (no-privileged, keep-id)
- ✅ Quieres updates limpios (rebuild imagen)
- ✅ Estás en un entorno multi-usuario o production-ish
Elige distrobox si:
- ✅ Ya tienes un container distrobox con herramientas compartidas
- ✅ Quieres que OpenClaw comparta el mismo entorno que otros tools
- ✅ No quieres mantener una imagen Docker separada
- ✅ Actualizas con
npm updateen vez de rebuild - ⚠️ Pero tienes que añadir
-u pabloal service
Mi situación
Yo elegí distrobox porque mi container ya tiene Node.js, Python, y otras herramientas. No quería mantener una imagen separada solo para OpenClaw. El trade-off: tuve que debuguear los permisos manualmente y el container es privileged (menos aislamiento).
Cómo migrar de distrobox a oficial (si algún día quieres)
# 1. Backup del estado actual
cp -r ~/.openclaw ~/.openclaw.backup
# 2. Parar el container actual
podman stop openclaw
# 3. Setup oficial
cd $(npm root -g)/openclaw
./scripts/podman/setup.sh --quadlet
# 4. Restaurar config
cp ~/.openclaw.backup/openclaw.json ~/.openclaw/openclaw.json
cp -r ~/.openclaw.backup/workspace ~/.openclaw/workspace
cp -r ~/.openclaw.backup/agents ~/.openclaw/agents
cp -r ~/.openclaw.backup/memory ~/.openclaw/memory
# 5. Iniciar
systemctl --user daemon-reload
systemctl --user start openclaw.service
Lecciones aprendidas
1. Lee la documentación oficial ANTES de improvisar. Si hubiera empezado con el script oficial, habría ahorrado 3 horas de permisos.
2. Si usas distrobox, verifica el usuario. podman exec <container> id <tu-usuario>. Si existe, usa -u <tu-usuario>. Si no existe, créalo.
3. openclaw doctor --fix puede empeorar las cosas. En mi caso, desactivó skills que yo había instalado manualmente. Úsalo con cuidado y revisa qué cambia antes de aceptar.
4. El security audit es tu amigo. openclaw security audit te dice exactamente qué está mal. En mi caso pasó de 3 critical a 0 después de: rotar token, añadir rate limiting, limpiar allowedOrigins, y fixear plugin ownership.
5. Los plugins externos necesitan atención. memory-core (bundled) funciona siempre. memory-lancedb (externo) necesita que el ownership sea correcto. Si ves “suspicious ownership”, el problema no es el plugin — es cómo corres el container.
El estado actual de mi setup
Container: distrobox (debian:bookworm)
Gateway: -u pablo, puerto 18789, bind lan
Modelo: glm-5-turbo (z.ai) + failover gpt-5.4-mini
Compaction: glm-4.7
Memory: memory-lancedb + text-embedding-3-small (z.ai)
Acceso móvil: Tailscale serve → HTTPS
Telegram: @Sitensobot
Skills: github, obsidian, session-logs (3 reales, 0 rotos)
Contexto: 3.8 KB inyectado (target < 8KB)
Security: 0 critical, 0 warn
Sessions: 185
Disco: 612 MB (~/.openclaw/)
Funciona. Pero si volviera a empezar, usaría el script oficial.