
Automatización web reproducible con Playwright, Docker, X11 y CDP
Una automatización web puede parecer una cadena sencilla: un script llama a Playwright, Playwright abre Chromium y el navegador visita una aplicación. Esa imagen es útil para empezar, pero oculta casi todo lo que hace estable al sistema.
En IXATU comprobé que el problema interesante no era aprender otra API de browser automation. Era convertir el navegador en una pieza de infraestructura reproducible y convertir su uso en una capacidad operacional gobernada.
La primera tesis es concreta: El navegador también puede tratarse como infraestructura versionada. La segunda amplía el foco: automatizar no basta; la operación necesita límites, evidencia y una decisión explícita sobre sus efectos.
El problema no era Playwright
Debajo de una llamada de Playwright aparecen el browser, sus dependencias nativas, las fuentes, los certificados, el IPC, la memoria compartida, el display gráfico y la compatibilidad entre cliente y navegador.
En WSL2, resolver esa pila dentro de cada repositorio crea deriva. Un proyecto instala un browser, otro añade paquetes del sistema y un tercero conserva una combinación distinta que sólo funciona en una máquina.
El coste no se limita al espacio ocupado. Cada diferencia reaparece como diagnóstico repetido, comportamiento desigual entre proyectos y tentación de modificar el host por ensayo y error hasta que un smoke deja de fallar.
Ese impulso es especialmente peligroso con agentes. Ante un error, un agente puede encadenar apt install, playwright install o playwright install-deps sin haber identificado todavía dónde corre el navegador.
La alternativa que adopté fue sacar esa complejidad del proyecto consumidor y colocarla en un runtime compartido, versionado y comprobable.
El navegador como infraestructura versionada
La arquitectura general separa el proyecto de la maquinaria del browser:
Proyecto
↓
Makefile / CLI / agente / comando
↓
playwright-docker
↓
Docker
↓
imagen Playwright versionada
↓
Chromium
↓
aplicación web
La imagen oficial de Playwright aporta un browser y las dependencias de sistema preparadas para trabajar juntos. El contrato mejora cuando la versión está fijada y cambia de forma deliberada.
Usar latest desplaza una decisión importante al momento de ejecución. El mismo comando puede resolver hoy una imagen distinta de la de ayer y convertir un fallo de aplicación en un cambio silencioso de infraestructura.
Versionar el runtime no vuelve determinista toda la web. Sí reduce una fuente grande de incertidumbre y permite preguntar con precisión qué combinación se ejecutó cuando aparece una diferencia.
Sacar el runtime del proyecto
En mi entorno, la capacidad transversal vive junto al tooling compartido. Los proyectos consumidores utilizan su interfaz en lugar de copiar Dockerfiles, scripts de arranque y ajustes de permisos.
Esta filosofía está representada públicamente por Dotfiles: configuración reproducible, CLI, utilidades devx y contratos que pueden reutilizar varios repositorios sin convertir cada uno en propietario de la misma infraestructura.
La frontera importa. Una capacidad transversal pertenece al entorno compartido; el proyecto que la usa conserva los comandos y criterios específicos de su dominio.
Así se evita que una corrección del launcher tenga varias implementaciones divergentes. También se distingue la evolución del tooling de la evolución funcional de cada aplicación.
Una primitive compartida: playwright-docker
playwright-docker actúa como puerta estable. Por detrás puede construir un docker run con --rm, --init, configuración de IPC, UID/GID, workspace, descargas y las variables necesarias.
humano / agente
↓
playwright-docker
↓
docker run
↓
runtime Playwright controlado
No hace falta conocer cada flag interno para consumir la capacidad, pero sí debe existir un contrato observable. El launcher no es un alias opaco que permite cualquier comportamiento.
Tres comprobaciones de lectura ayudan a fijar el contexto antes de ejecutar un browser:
command -v playwright-docker
playwright-docker --help
docker version
La ayuda forma parte de la interfaz operacional. Permite conocer ejemplos, imagen por defecto y variables admitidas sin instalar navegadores ni modificar el host.
Docker es la maquinaria. El launcher es el punto de entrada estable que humanos, agentes, Makefiles o scripts pueden compartir.
Playwright es tooling, no runtime de la aplicación
Playwright no es el runtime de IXATU Operations. La aplicación no lo necesita para prestar servicio en producción ni debe incorporarlo como dependencia de su arquitectura funcional.
En este contexto, Playwright es tooling de validación, diagnóstico y operación. Puede comprobar rutas, contenido renderizado, redirects y estados visibles, pero permanece fuera del proceso normal de la aplicación.
Esta separación permite actualizar o sustituir la herramienta sin alterar el producto. También evita desplegar un browser donde sólo hace falta servir una API y una interfaz web.
Tres modos para tres necesidades
El modo por defecto es headless. Playwright controla Chromium dentro de Docker y no presenta una ventana.
Headless encaja con navegación técnica, smokes, rutas, contenido renderizado y CI cuando el entorno lo permite. Que la página haya cargado no es evidencia suficiente.
Una comprobación útil observa señales estables: título, ruta, estado, texto esperado, ausencia de un error conocido o un indicador no vacío. La señal debe responder a la pregunta del smoke.
El segundo modo es headed con X11. Chromium sigue dentro del contenedor y Playwright continúa controlándolo. X11 sólo lleva la ventana hasta un display visible.
X11 transporta la pantalla, no el control.
Este modo ayuda cuando necesito ver la navegación, depurar una interfaz o atravesar un login supervisado. También permite que una persona introduzca un OTP sin entregar el secreto a la automatización.
El tercer modo utiliza CDP, Chrome DevTools Protocol. Playwright se conecta a un Chrome o Edge Chromium que ya existe fuera del contenedor.
X11 transporta la pantalla; CDP transporta el control hacia un navegador externo.
CDP es excepcional y opt-in. Puede resolver un caso donde el browser debe vivir en el host, pero ofrece menos fidelidad que el protocolo normal de Playwright y amplía la superficie que hay que gobernar.
No es una forma alternativa de mostrar Chromium del contenedor. Cambia quién posee el navegador, su perfil, su ciclo de vida y el endpoint que concede control.
Un árbol de decisión pequeño
La elección puede resumirse sin comenzar por flags:
¿Necesito navegador?
│
▼
¿Necesito verlo?
│ │
NO SÍ
│ │
headless ¿dónde debe correr?
│
┌──────┴─────────┐
│ │
Chromium Docker Chrome/Edge host
│ │
X11 CDP
La regla práctica es: headless por defecto; X11 si necesito ver Chromium dentro de Docker; CDP si necesito controlar un browser externo.
La pregunta no es cuál modo tiene más capacidades. Es cuál conserva la frontera más pequeña que satisface la necesidad real.
Windows, WSL2, Docker y localhost
Una parte importante del diagnóstico consiste en reconocer capas. Windows, WSL2 y Docker pueden tener namespaces de red distintos aunque todos utilicen la palabra localhost.
127.0.0.1 significa “yo mismo”; primero hay que determinar quién es “yo” en esa capa. Para un proceso del contenedor, “yo” no suele ser Windows ni el servicio que escucha en otra red.
Antes de abrir puertos o cambiar el firewall conviene responder tres preguntas: dónde corre Playwright, dónde corre el browser y desde qué capa se intenta alcanzar el endpoint.
Un fallo de conexión puede pertenecer al launcher, Docker, el browser, X11, CDP, la red, la autenticación o la UI. Cambiar infraestructura antes de clasificarlo sólo añade variables.
La regla que más tiempo me ahorra es ésta: Antes de arreglar Playwright, determina dónde está ejecutándose realmente el navegador.
Después la bifurcación resulta más clara. Browser dentro de Docker que necesito ver: X11. Chrome o Edge externo que necesito controlar: CDP.
Automatización no significa mutación
Playwright puede realizar una validación observacional: abrir páginas, leer contenido, comprobar una ruta, observar un redirect o ejecutar un smoke sin alterar datos.
También puede enviar formularios, pulsar controles administrativos, cambiar configuración o actuar sobre producción. La API utilizada es la misma; el efecto no lo es.
La herramienta no determina el nivel de riesgo; lo determina el efecto de la acción.
Por eso “usar Playwright” no constituye autorización. Una acción que muta datos, identidad, Azure, SQL, despliegues o configuración necesita un alcance y una aprobación explícitos.
El modo read-only debe describir lo que hace el flujo, no una propiedad imaginaria del browser. Incluso una lectura puede exponer datos o degradar un origen si consulta sin límites.
Automatización supervisada
La automatización completa no siempre es el objetivo correcto. En sistemas corporativos, un modelo híbrido puede preservar mejor la frontera de seguridad.
Playwright navega
↓
aparece un gate sensible
↓
la persona autentica o introduce OTP
↓
Playwright continúa
La automatización no necesita conocer la contraseña, el factor MFA ni el OTP. La persona atraviesa el gate y conserva autoridad sobre esa transición.
Para Microsoft Entra, el patrón que utilizo por defecto es HUMAN-SUPERVISED. No automatizo credenciales corporativas sólo para eliminar una pausa legítima.
Una sesión autenticada prueba que el gate se ha superado. No demuestra por sí sola que cada usuario, grupo o rol tenga la autorización fina esperada.
Storage state, CDP y artifacts sensibles
Un storage state puede contener cookies, local storage y estado de sesión. Cuando representa una sesión autenticada, debe tratarse como material sensible.
No debería versionarse, imprimirse, pegarse en una PR, adjuntarse a un issue ni publicarse como artifact. El mismo cuidado se aplica a perfiles de navegador y trazas autenticadas.
Con CDP uso un perfil aislado, nunca el perfil personal habitual. El endpoint debe estar restringido, no exponerse a redes amplias y cerrarse junto con el browser al terminar.
X11 también debe habilitarse sólo cuando aporta valor. No necesita montar todo el directorio personal ni copiar perfiles o credenciales al contenedor.
Screenshots, HTML, PDF y traces pueden contener PII, cookies, tokens o datos de negocio. No son logs inocuos por el hecho de haber sido generados por una prueba.
La seguridad funciona por capas: perfil, red, permisos, autenticación humana, superficie del launcher, alcance de la acción y política de conservación de artifacts.
Evidencia mínima, no acumulación máxima
Una buena automatización no sólo ejecuta. Produce evidencia suficiente para explicar qué se comprobó, qué señal apareció y en qué capa es probable que esté un fallo.
Una salida pequeña puede incluir:
launcher: PASS/FAIL
Docker: PASS/FAIL
modo: headless/X11/CDP
ruta
estado visible
redirect observado
señal renderizada: PASS/FAIL
timestamp
capa probable del fallo
Esta evidencia es más útil que una carpeta indiscriminada de capturas. Facilita revisar la conclusión sin conservar datos que no eran necesarios para demostrarla.
El criterio es proporcionalidad. Si una ruta, un título y un estado prueban el smoke, almacenar el HTML completo o el perfil de sesión añade riesgo sin mejorar la decisión.
Qué debería hacer un agente ante un fallo
Un agente no debería responder a un fallo de Playwright instalando dependencias por reflejo. Primero debe descubrir el contrato operativo del entorno.
Las preguntas iniciales son pequeñas: ¿existe tooling compartido?, ¿qué imagen está fijada?, ¿dónde se ejecuta el browser?, ¿qué modos admite el launcher?, ¿qué efecto está autorizado?
humano / agente
↓
interfaz estable
↓
runtime versionado
↓
comandos y runbook del proyecto
Este orden reduce cambios accidentales en el host y mejora el diagnóstico. El agente puede observar precondiciones, proponer una ejecución y recoger evidencia sin convertir esa capacidad en permiso general.
Las condiciones de parada forman parte del diseño: no duplicar wrappers, no montar $HOME completo, no exponer CDP, no publicar sesiones y no tocar infraestructura para hacer pasar un smoke.
IXATU Operations como caso real
En IXATU Operations utilizo este patrón para validar superficies web sin convertir Playwright en parte del producto. Un smoke puede observar rutas de UI, un estado renderizado o un redirect hacia Microsoft Entra.
También puede comprobar que el Dashboard presenta una señal esperada, que la Web enruta hacia /api o que /api/health resulta observable a través de la propia superficie web.
Esas comprobaciones se formulan sin exponer endpoints privados, credenciales, datos de clientes ni detalles internos. El caso aporta evidencia del patrón; no convierte el artículo en documentación operacional de la empresa.
Cuando la autenticación exige intervención, la persona atraviesa el gate y Playwright continúa con una sesión gobernada. Cuando sólo hace falta validar contenido público o estado técnico, headless mantiene una frontera menor.
La experiencia de IXATU confirmó algo reutilizable: el browser es infraestructura del tooling y la automatización es una operación con efectos, permisos y evidencia, no una sucesión neutral de clics.
Billing / Invoices como automatización intensiva
En Billing / Invoices Playwright deja de ser sólo una herramienta de smoke y pasa a formar parte de un flujo operativo repetitivo.
El navegador recorre un portal de facturación, localiza suministros por CUPS y busca las facturas emitidas dentro del periodo mensual que corresponde procesar.
El objetivo no es automatizar una pantalla aislada, sino transformar una secuencia manual y repetitiva en un proceso reproducible.
El flujo identifica el suministro, navega hasta sus documentos, selecciona las facturas del mes y descarga los archivos que alimentarán las siguientes etapas del pipeline.
La frontera sigue siendo importante. Playwright resuelve la interacción con el portal y la obtención de documentos.
No decide si una factura es válida, cómo debe contabilizarse ni qué mutaciones están autorizadas. Esa lógica pertenece al dominio de Billing y a sus propios controles.
Cuando la sesión existente permite trabajar sin intervención, el flujo puede ejecutarse headless.
Si el portal exige login, OTP o inspección visual, el patrón admite una transición supervisada con X11 o, en casos concretos, una conexión CDP a un navegador externo.
Este uso intensivo también refuerza las reglas de seguridad. Los CUPS, las sesiones autenticadas y las facturas descargadas deben permanecer fuera de logs públicos, commits y evidencias innecesarias.
La reproducibilidad del navegador sirve para automatizar más sin ampliar de forma accidental la superficie de datos expuesta.
De una herramienta a una capacidad gobernada
El modelo mental final separa responsabilidades:
Playwright = control y automatización
Chromium / Chrome / Edge = navegador
Docker = runtime reproducible
X11 = transportar la pantalla
CDP = transportar el control hacia un browser externo
Dotfiles = tooling compartido y reutilizable
Runbook = operación gobernada
Ninguna de estas piezas resuelve por sí sola reproducibilidad, seguridad y diagnóstico. La arquitectura aparece cuando sus fronteras son explícitas y la opción por defecto reduce autoridad y superficie.
Antes de tocar dependencias, red o firewall, vuelvo a dos preguntas: ¿dónde se está ejecutando realmente el navegador? y ¿necesito verlo o necesito controlar otro navegador?
Responderlas suele decidir el modo correcto. Gobernar el efecto decide si la automatización puede continuar.