Docker permite empaquetar una aplicación y parte de su entorno en una imagen, distribuir ese artefacto y crear contenedores a partir de él. Facilita repetir una forma de construir y ejecutar software; el resultado también depende de la plataforma, la configuración y los recursos disponibles.

La separación útil es entre receta, artefacto y ejecución. Un Dockerfile describe la construcción. Una imagen conserva capas de filesystem y configuración. Un contenedor ejecuta procesos con una capa escribible y parámetros de runtime. Esos tres objetos tienen ciclos de vida distintos.

De la receta al proceso

El builder interpreta un Dockerfile y su contexto: los archivos disponibles para copiar y utilizar durante la construcción. El resultado puede almacenarse localmente o enviarse a un registry, desde el cual otro entorno obtiene la imagen. El registry distribuye artefactos; no ejecuta por sí mismo la aplicación.

Un tag, como una versión legible, puede apuntar a una imagen diferente con el tiempo. Un digest identifica contenido concreto. Fijar un digest ayuda a elegir el mismo artefacto; actualizarlo requiere una decisión explícita y una nueva verificación. Tampoco demuestra por sí solo que el contenido sea confiable.

La imagen no contiene automáticamente todo lo necesario para operar: variables, secretos, puertos publicados, volúmenes y límites de recursos pueden definirse al crear el contenedor. La arquitectura de CPU y el sistema operativo de la imagen deben ser compatibles con el runtime. Una imagen multiplataforma puede seleccionar un artefacto adecuado, pero no convierte cualquier binario en portable.

El diagrama muestra un flujo habitual de distribución. El paso por un registry es opcional cuando se utiliza una imagen local. Los volúmenes y secretos pertenecen al contrato de ejecución, no a la receta del código.

---
config:
  look: handDrawn
---
flowchart TD
    recipe["Dockerfile<br/>+ contexto"] --> builder["Builder<br/>construcción"]
    builder --> image["Imagen<br/>capas + configuración"]
    image --> registry["Registry<br/>tag o digest"]
    registry --> runtime["Runtime<br/>creación"]
    settings["Configuración y secretos<br/>fuera de la imagen"] --> runtime
    runtime --> container["Contenedor<br/>procesos + capa escribible"]
    volumes["Volúmenes<br/>persistencia según contrato"] --> container

Un Dockerfile mínimo y explícito

Este ejemplo es ilustrativo para una aplicación Node sin compilación previa. Presupone package.json, un package-lock.json compatible y server.js en el contexto; sus dependencias de ejecución deben estar en dependencies. El servidor debe escuchar en 0.0.0.0:3000. No representa una aplicación real probada aquí.

FROM node:22-bookworm-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --chown=node:node server.js ./
USER node
EXPOSE 3000
CMD ["node", "server.js"]

FROM elige la base; este tag es legible y mutable, por lo que una entrega que requiera fijar el artefacto debe resolver y registrar su digest. WORKDIR define el directorio de trabajo. COPY incorpora archivos del contexto. RUN ejecuta un comando durante la construcción; npm ci utiliza el lockfile y falla si no coincide con el manifiesto.

USER establece el usuario para instrucciones posteriores y para la ejecución por defecto. EXPOSE documenta el puerto esperado: no lo publica automáticamente hacia el host ni abre un firewall. CMD define el comando por defecto; puede sustituirse al arrancar. Su forma de array evita introducir una shell intermediaria para este comando.

Un .dockerignore reduce lo que entra al contexto de construcción. Para el ejemplo puede excluir:

node_modules
.git
.env
.env.*
npm-debug.log*

Se evita copiar dependencias instaladas para otra plataforma, historial y archivos locales sensibles. La exclusión debe adaptarse al proyecto; no garantiza detectar todos sus secretos. Contraseñas y tokens no deben incrustarse en archivos de la imagen, argumentos de build o variables persistidas en su configuración. Si construir requiere una credencial, se utiliza un mecanismo temporal de secretos del builder y se comprueba que el proceso no la copie a su resultado. Las prácticas de construcción de Docker ayudan a revisar contexto, bases y estructura de la receta.

Caché de construcción y multistage

La caché de build evita repetir trabajo cuyo resultado puede reutilizarse. En el ejemplo, copiar manifiesto y lockfile antes del código permite conservar el resultado de instalar dependencias cuando cambia únicamente server.js. Si cambia el lockfile, ese paso y los resultados que dependen de él necesitan recalcularse.

No todas las instrucciones generan una nueva capa de filesystem: algunas modifican configuración de la imagen. La reutilización depende de la instrucción, sus entradas y los resultados anteriores. Un paso cacheado no consulta automáticamente si un repositorio remoto tiene paquetes nuevos. La política de actualización de bases y dependencias debe definirse, no suponerse a partir de que el build termina correctamente. La documentación de invalidación detalla ese contrato de reutilización.

En una construcción multistage, un stage de construcción puede contener compiladores y dependencias de desarrollo, y un stage de ejecución recibe los artefactos necesarios mediante COPY --from. Una aplicación que genera dist/ no necesita conservar toda su toolchain para ejecutarla. Los archivos, librerías y permisos requeridos en runtime deben seguir presentes.

Los stages forman un grafo de dependencias: un cambio en el stage del que se copia puede afectar a su consumidor. Un stage independiente puede conservar trabajo reutilizable; cambiar una etapa no obliga universalmente a reconstruir todas ni hace que las demás sean independientes por definición.

Esta caché conserva resultados de construcción. La caché de aplicación conserva respuestas o datos durante el servicio, con un contrato diferente de frescura, invalidación y recuperación.

Estado y ciclo de vida del contenedor

Un contenedor añade una capa escribible sobre la imagen. Los cambios de esa capa pertenecen a esa instancia y no actualizan la imagen del registry. Detener y volver a iniciar la misma instancia no equivale a eliminarla y crear otra: al eliminarla puede perderse el estado guardado únicamente en su capa.

Los volúmenes o servicios externos permiten separar datos del ciclo de vida del contenedor. Su existencia no garantiza durabilidad por sí sola: ubicación, backups, permisos y recuperación deben definirse. El modelo de datos sigue determinando qué almacén es autoritativo y qué puede reconstruirse.

La configuración se entrega de acuerdo con el entorno, y los secretos tienen un ciclo de vida propio. Montar un directorio del host permite que el proceso actúe sobre lo expuesto según sus permisos. Montar el socket de Docker puede conceder control amplio sobre el daemon y sus recursos; no es una integración inocua sólo porque el proceso esté en un contenedor.

Kernel, recursos y aislamiento

Los contenedores Linux convencionales comparten el kernel del entorno Linux que los ejecuta. Namespaces separan vistas de procesos, red y otros recursos; cgroups permiten controlar y contabilizar recursos. En macOS y Windows, Docker Desktop suele proporcionar ese entorno mediante una VM Linux, en lugar de convertir cada contenedor en una VM con kernel propio.

Una VM ejecuta un kernel invitado propio sobre una frontera de virtualización. Compartir kernel suele reducir costes de arranque y memoria frente a levantar un sistema invitado completo por aplicación, aunque el resultado depende de la carga, las imágenes y el entorno. Esa eficiencia conserva una frontera de aislamiento distinta.

CPU y memoria disponibles, límites, permisos y capacidades cambian el comportamiento. Una imagen pequeña no limita automáticamente cuánto puede consumir su proceso. Ejecutar con un usuario no root, reducir privilegios y exponer sólo los recursos necesarios ayuda a contener alcance; no establece una frontera infalible.

Compartir kernel introduce una dependencia de seguridad común. Vulnerabilidades, configuraciones privilegiadas, mounts y credenciales expuestas pueden ampliar el impacto de una ejecución. Un contenedor no debe describirse como un sistema operativo completo ni como aislamiento absoluto frente al host.

Docker Sandboxes para agentes

Docker Sandboxes es una capacidad específica para ejecutar agentes en entornos aislados; no es otro nombre para cualquier contenedor Docker. En su modalidad local, cada sandbox utiliza una microVM con kernel Linux y daemon Docker propios, añadiendo una frontera de virtualización respecto del host. También debe distinguirse de la VM general de Docker Desktop que sostiene contenedores convencionales; esta explicación no extrapola las mismas condiciones a la modalidad cloud.

La CLI actual sbx es independiente y no requiere Docker Desktop ni Docker Engine en el host. Las plataformas y requisitos de virtualización deben comprobarse en la documentación de instalación vigente. La separación del daemon evita tratar el acceso a su Docker interno como acceso directo al daemon del host.

El agente puede ejecutar código y herramientas en su entorno, pero el modo de workspace importa. En el modo directo, se comparten archivos reales del host y los cambios son bidireccionales e inmediatos. Un kernel separado no protege lo que se expone deliberadamente como carpeta compartida. Otros modos de trabajo cambian ese contrato y deben revisarse de forma explícita.

Un escape del contenedor puede alcanzar recursos de la microVM, incluido su kernel o daemon. Comprometer el kernel invitado no elimina por sí mismo la frontera del hipervisor, pero tampoco demuestra que cualquier escape quede contenido. El hipervisor, el workspace compartido, la red, las credenciales y las herramientas externas forman parte del alcance real y de lo que se decide exponer al agente.

El sandbox ayuda a reducir el impacto de ejecutar código no confiable. No elimina la necesidad de delimitar qué archivos, identidades y acciones puede alcanzar ese código.

Operar según las necesidades

Docker puede utilizarse en producción. Kubernetes y Amazon ECS son opciones de orquestación según las necesidades; no son requisitos universales para ejecutar contenedores. La decisión depende de despliegues, réplicas, descubrimiento, recuperación, distribución entre máquinas y operación del servicio. Un único host y un sistema distribuido presentan problemas diferentes.

La forma de empaquetar no decide la recuperación del trabajo asíncrono, la consistencia de una base ni el comportamiento de un pago incierto. Esos contratos permanecen aunque los participantes compartan una imagen o un cluster.

Una entrega útil identifica el artefacto que ejecuta, sus dependencias externas, los recursos que expone y el estado que debe sobrevivir. La verificación separa que la imagen pueda construirse, que el proceso arranque y que el servicio complete su operación bajo los límites y fallos relevantes.