Distrobox — probar aplicaciones en aislamiento sin arriesgar actualizaciones
Tienes una instalación que funciona bien y, en el horizonte, aparece una versión nueva. Quieres probarla antes de sobrescribir el paquete en el host. Docker clásico es incómodo con aplicaciones GUI (sobre todo Electron): X11, GPU, FUSE, flujos de login. Distrobox resuelve exactamente eso — un contenedor con integración gráfica completa, mientras la instalación del host queda intacta.
Me pasó dos veces hace poco. A veces necesito una versión anterior del slicer de mi impresora 3D: las actualizaciones de Snapmaker pueden liar la configuración. También veo y oigo problemas con Cursor: la lista de tareas no funciona en 3.12 y en 3.9 sí; los modelos se ralentizan mucho en ciertos tipos de tareas, mientras la versión anterior sigue yendo bien. Vendría bien algo que se pueda lanzar en un entorno aislado…
Este artículo explica primero qué es Distrobox y cuándo tiene sentido, y después — con un ejemplo real — guía el test de Cursor 3.14.x junto a una instalación estable 3.9.x: home separado, AppImage, trampas del directorio de trabajo y un atajo práctico al OAuth.
Qué es Distrobox
Distrobox es una capa sobre Podman o Docker. Su objetivo no es el sandbox duro (como Flatpak), sino la integración. El contenedor obtiene:
- acceso a X11 / Wayland (las ventanas GUI se ven nativas),
- audio, USB, discos,
- (por defecto) tu directorio home,
- la posibilidad de exportar aplicaciones al menú del host.
En la práctica ejecutas software de otra distribución — o una copia aparte del mismo programa — sin dual-boot y sin pelearte con Docker completo bajo Electron. El mismo patrón sirve para un IDE, un slicer, un editor de audio o cualquier app que no quieras actualizar a ciegas.
Para qué se usa
Distrobox sirve sobre todo para ejecutar software “como en otra distro”, manteniendo el escritorio normal del host. Usos típicos:
- Apps no disponibles o problemáticas en tu distribución — paquetes solo para Fedora/Arch, dependencias viejas, conflictos de bibliotecas.
- Entornos de desarrollo aislados — un stack aparte (Node, Python, toolchain) sin ensuciar el sistema.
- GUI desde un contenedor — gran ventaja frente a Docker puro: ventanas, audio y archivos del home funcionan sin montajes manuales complicados.
- Versiones antiguas o nuevas en paralelo — probar o hacer rollback sin actualizar el host (Cursor, slicers 3D, etc.).
- Exportar la app al menú del host (
distrobox-export) — parece una aplicación del sistema.
Lo que no es: un sandbox duro de seguridad. Se integra a propósito con el host (home, X11, dispositivos). Si la idea es “no confío en este código”, mejor Flatpak o un contenedor normal con mounts limitados.
Distrobox frente a Docker clásico
| Distrobox | Docker clásico | |
|---|---|---|
| Objetivo | entornos de desarrollo / GUI integrados con el host | aislamiento de servicios, CI, servidores |
| Ventanas GUI | funciona de entrada (X11/Wayland) | montaje manual de sockets, mucha fricción |
| Home | compartido con el host por defecto | suele ser un volumen aparte |
| Sandbox | débil — no lo trates como aislamiento de seguridad | más fuerte (con una configuración sensata) |
Requisitos
- Linux con Podman o Docker (Distrobox prefiere Podman si ambos están instalados),
- el paquete
distroboxde tu distro, o el script de instalación upstream.
En Kubuntu / Ubuntu suele bastar:
sudo apt install distrobox podman
Ejemplo: un Cursor nuevo sin actualizar el host
Objetivo: probar el Cursor más reciente (aquí: AppImage 3.14.7) sin tocar el .deb que funciona (p. ej. 3.9.16 en /usr/bin/cursor).
Estrategia:
- Contenedor Distrobox con un directorio home separado.
- Dentro — el AppImage más nuevo.
- Los datos del editor (
~/.config/Cursor) van al home del contenedor, no al del host.
Sin un home aparte, Distrobox monta tu ~/ real y el Cursor nuevo puede sobrescribir ajustes / la base SQLite de la instalación que funciona. Eso lo evitamos.
Host Distrobox (cursor-test)
───── ───────────────────────
/usr/bin/cursor (3.9.x) Cursor.AppImage (3.14.x)
~/.config/Cursor ~/cursor-test-home/.config/Cursor
↑ intacto ↑ datos aislados
El mismo esquema se aplica a otras apps: --home separado, binario / AppImage / paquete dentro del contenedor, host intacto.
Paso 1 — home separado y creación del contenedor
mkdir -p ~/cursor-test-home
distrobox create \
--name cursor-test \
--image ubuntu:24.04 \
--home ~/cursor-test-home \
--additional-packages "libfuse2t64 libgtk-3-0 libnss3 libxss1 libasound2t64"
Lo clave es --home ~/cursor-test-home. Distrobox también montará el /home/$USER real (integración con el host), pero $HOME dentro del contenedor apunta al directorio de prueba — y ahí deben ir las configs de las apps que respetan $HOME / XDG.
Entrar al contenedor:
distrobox enter -nw cursor-test
La flag -nw (no workdir) te deja directamente en el $HOME del contenedor. Sin ella, Distrobox conserva el directorio actual del host — si entras desde /home/tu, tras enter sigues en /home/tu y parece que --home no funcionó.
Comprobación:
echo $HOME # /home/tu/cursor-test-home
pwd # con -nw: lo mismo
ls ~ # skel (.bashrc, .profile, …) — no tu home completo del host
Si
pwdmuestra/home/tuyecho $HOMEmuestra.../cursor-test-home, está bien — hazcd ~o entra con-nw.
Paso 2 — descargar Cursor (AppImage)
El endpoint https://cursor.com/api/download?platform=linux-x64&releaseTrack=stable no devuelve un binario, solo JSON con downloadUrl, debUrl, version, etc.
Incorrecto (obtienes un JSON de ~600 B):
curl -L "https://cursor.com/api/download?platform=linux-x64&releaseTrack=stable" -o Cursor.AppImage
Correcto:
# dentro del contenedor, en $HOME
URL=$(python3 -c "import json,urllib.request; print(json.load(urllib.request.urlopen('https://cursor.com/api/download?platform=linux-x64&releaseTrack=stable'))['downloadUrl'])")
curl -fL -o Cursor.AppImage "$URL"
chmod +x Cursor.AppImage
file Cursor.AppImage # esperado: ELF 64-bit … (~280 MB)
Ejecución:
./Cursor.AppImage
# en Ubuntu 24.04+ a veces hace falta:
./Cursor.AppImage --no-sandbox
# o si falta FUSE:
./Cursor.AppImage --appimage-extract-and-run
Launcher opcional:
cat > ~/run-cursor.sh <<'EOF'
#!/bin/bash
set -euo pipefail
export HOME=/home/$USER/cursor-test-home # ajusta la ruta
cd "$HOME"
exec ./Cursor.AppImage --no-sandbox "$@"
EOF
chmod +x ~/run-cursor.sh
Paso 3 — trampa: login OAuth en el contenedor
Cursor inicia sesión con OAuth en un navegador externo (xdg-open). En Distrobox, el clic en “Log in” a menudo no hace nada.
Por qué
- Distrobox sustituye
xdg-openpordistrobox-host-exec. distrobox-host-execdepende dehost-spawn, yhost-spawnnecesita Flatpak en el host (interfaz D-Busorg.freedesktop.Flatpak.Development).- Sin Flatpak obtienes
Error: Cannot download host-spawn/ código de salida 127 — el navegador no se abre.
Aunque el navegador se abra, aparece un segundo problema: tras el login, el callback cursor://… en el host suele ir al Cursor del host (handler en /usr/share/applications), no a la instancia del contenedor.
Para un smoke-test de funciones de IA no merece la pena pelearse con eso. Es más simple copiar la sesión de la instalación que ya funciona en el host.
Paso 4 — atajo al OAuth: copiar la sesión del host
Cursor guarda los tokens en SQLite:
~/.config/Cursor/User/globalStorage/state.vscdb
Claves, entre otras:
cursorAuth/accessTokencursorAuth/refreshTokencursorAuth/cachedEmailcursorAuth/cachedTeamcursorAuth/stripeMembershipType
Cierra el Cursor de prueba y luego, en el host:
import sqlite3, shutil, time
host = "/home/USER/.config/Cursor/User/globalStorage/state.vscdb"
test = "/home/USER/cursor-test-home/.config/Cursor/User/globalStorage/state.vscdb"
keys = [
"cursorAuth/accessToken",
"cursorAuth/refreshToken",
"cursorAuth/cachedEmail",
"cursorAuth/cachedScopedProfile",
"cursorAuth/cachedSignUpType",
"cursorAuth/cachedTeam",
"cursorAuth/stripeMembershipType",
]
h = sqlite3.connect(f"file:{host}?mode=ro", uri=True)
vals = {k: h.execute("SELECT value FROM ItemTable WHERE key=?", (k,)).fetchone()[0] for k in keys}
h.close()
shutil.copy2(test, test + f".bak-{int(time.time())}")
t = sqlite3.connect(test)
for k, v in vals.items():
t.execute(
"INSERT INTO ItemTable(key,value) VALUES(?,?) "
"ON CONFLICT(key) DO UPDATE SET value=excluded.value",
(k, v),
)
t.execute("DELETE FROM ItemTable WHERE key=?", ("glass.lastSignedInAuthId",))
t.commit()
t.close()
print("OK")
Tras reiniciar el AppImage debería quedar logueado en la misma cuenta que el host. Es compartir sesión a propósito: cómodo para pruebas, no para un aislamiento duro de identidad.
Si aun así quieres el camino del navegador: instala
flatpaken el host (ruta oficial dehost-spawn) o instala un navegador dentro del contenedor. Vigila el conflicto del handlercursor://.
Paso 5 — qué conviene probar antes de actualizar el host
- arranque GUI, layout, abrir un proyecto,
- Agent / Chat / terminal,
- Git, extensiones, MCP,
- el workflow típico del día a día,
- las herramientas que usas, duración de los comandos.
Si todo va bien — actualizas el .deb del host como siempre. Si no — borras el contenedor y te quedas con la versión anterior.
Limpieza
distrobox stop cursor-test
distrobox rm cursor-test
rm -rf ~/cursor-test-home
El Cursor del host no cambia.
Checklist — chuleta rápida
distrobox create … --home ~/cursor-test-homedistrobox enter -nw cursor-test- Descargar el AppImage real desde
downloadUrl(no el JSON crudo de la API) - Copiar tokens del
state.vscdbdel host (el OAuth en el contenedor suele no arrancar) - Probar el workflow habitual
- Tras el test:
distrobox rm+ eventual actualización del.deb
Resumen
Distrobox es una forma práctica de tener un “segundo Linux” para GUI sin dual-boot y sin el circo de Docker con Electron. Sirve igual para un slicer 3D que para un IDE: --home aparte, binario en el contenedor, host intacto.
Con Cursor, además, el OAuth en Distrobox puede quedarse en silencio total: para una prueba rápida, lo más cómodo es copiar la sesión de la instalación que ya funciona. Así actualizar el host deja de ser un salto al vacío: primero compruebas, después sobrescribes lo que ya te va bien.