Docker packages an application and part of its environment into an image, distributes that artifact, and creates containers from it. It helps repeat a way of building and running software; the result also depends on the platform, configuration, and available resources.

The useful distinction is between recipe, artifact, and execution. A Dockerfile describes the build. An image holds filesystem layers and configuration. A container runs processes with a writable layer and runtime parameters. These three objects have different lifecycles.

From recipe to process

The builder interprets a Dockerfile and its context: the files available to copy and use during the build. The result can be stored locally or pushed to a registry, from which another environment retrieves the image. The registry distributes artifacts; it does not run the application itself.

A tag, such as a readable version, can point to a different image over time. A digest identifies specific content. Pinning a digest helps select the same artifact; updating it requires an explicit decision and new verification. It also does not establish that the content is trustworthy on its own.

The image does not automatically contain everything needed to operate: variables, secrets, published ports, volumes, and resource limits can be defined when creating the container. The image’s CPU architecture and operating system must be compatible with the runtime. A multi-platform image can select an appropriate artifact, but it does not make every binary portable.

The diagram shows a common distribution flow. The registry step is optional when using a local image. Volumes and secrets belong to the execution contract, rather than the code’s recipe.

---
config:
  look: handDrawn
---
flowchart TD
    recipe["Dockerfile<br/>+ context"] --> builder["Builder<br/>build"]
    builder --> image["Image<br/>layers + configuration"]
    image --> registry["Registry<br/>tag or digest"]
    registry --> runtime["Runtime<br/>creation"]
    settings["Configuration and secrets<br/>outside the image"] --> runtime
    runtime --> container["Container<br/>processes + writable layer"]
    volumes["Volumes<br/>persistence by contract"] --> container

A minimal, explicit Dockerfile

This example is illustrative for a Node application without a prior compilation step. It assumes package.json, a compatible package-lock.json, and server.js in the context; runtime dependencies must be listed under dependencies. The server must listen on 0.0.0.0:3000. It does not represent a real application tested here.

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 selects the base; this tag is readable and mutable, so a release that needs to pin the artifact must resolve and record its digest. WORKDIR sets the working directory. COPY includes files from the context. RUN executes a command during the build; npm ci uses the lockfile and fails if it does not match the manifest.

USER sets the user for subsequent instructions and for execution by default. EXPOSE documents the expected port: it does not automatically publish it to the host or open a firewall. CMD sets the default command; it can be replaced at startup. Its array form avoids introducing an intermediate shell for this command.

A .dockerignore reduces what enters the build context. For the example, it can exclude:

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

This avoids copying dependencies installed for another platform, history, and sensitive local files. The exclusions must be adapted to the project; they do not guarantee detecting all its secrets. Passwords and tokens should not be embedded in image files, build arguments, or variables persisted in its configuration. If a build needs a credential, a temporary builder secret mechanism is used, and the process is checked to ensure it does not copy that secret into its output. Docker’s build practices help review context, bases, and recipe structure.

Build cache and multistage builds

The build cache avoids repeating work whose result can be reused. In the example, copying the manifest and lockfile before the code preserves the dependency installation result when only server.js changes. If the lockfile changes, that step and the results depending on it need to be recalculated.

Not every instruction creates a new filesystem layer: some change image configuration. Reuse depends on the instruction, its inputs, and previous results. A cached step does not automatically check whether a remote repository has new packages. The update policy for bases and dependencies must be defined, rather than inferred from a successful build. The invalidation documentation details that reuse contract.

In a multistage build, a build stage can contain compilers and development dependencies, while a runtime stage receives the required artifacts through COPY --from. An application that generates dist/ does not need to retain its entire toolchain to run. The files, libraries, and permissions required at runtime must remain present.

Stages form a dependency graph: a change in the stage being copied from can affect its consumer. An independent stage can retain reusable work; changing one stage does not universally require rebuilding all stages, nor make the others independent by definition.

This cache stores build results. An application cache stores responses or data during service, with a different contract for freshness, invalidation, and recovery.

Container state and lifecycle

A container adds a writable layer over the image. Changes in that layer belong to that instance and do not update the registry image. Stopping and restarting the same instance is different from deleting it and creating another: deleting it can lose state stored only in its layer.

Volumes or external services separate data from the container’s lifecycle. Their existence does not guarantee durability on its own: location, backups, permissions, and recovery must be defined. The data model still determines which store is authoritative and what can be reconstructed.

Configuration is supplied according to the environment, and secrets have their own lifecycle. Mounting a host directory allows the process to act on what is exposed according to its permissions. Mounting the Docker socket can grant broad control over the daemon and its resources; it is not a harmless integration simply because the process runs in a container.

Kernel, resources, and isolation

Conventional Linux containers share the kernel of the Linux environment running them. Namespaces separate views of processes, networking, and other resources; cgroups enable resource control and accounting. On macOS and Windows, Docker Desktop usually supplies that environment through a Linux VM, rather than turning each container into a VM with its own kernel.

A VM runs its own guest kernel across a virtualization boundary. Sharing a kernel usually reduces startup and memory costs compared with starting a complete guest system per application, although the result depends on the workload, images, and environment. That efficiency retains a different isolation boundary.

Available CPU and memory, limits, permissions, and capabilities change behavior. A small image does not automatically limit how much its process can consume. Running as a non-root user, reducing privileges, and exposing only the required resources helps limit scope; it does not establish an infallible boundary.

Sharing a kernel introduces a common security dependency. Vulnerabilities, privileged configurations, mounts, and exposed credentials can expand the impact of execution. A container should not be described as a complete operating system or as absolute isolation from the host.

Docker Sandboxes for agents

Docker Sandboxes is a specific capability for running agents in isolated environments; it is not another name for every Docker container. In its local mode, each sandbox uses a microVM with its own Linux kernel and Docker daemon, adding a virtualization boundary from the host. It must also be distinguished from the general Docker Desktop VM supporting conventional containers; this explanation does not extrapolate the same conditions to cloud mode.

The current sbx CLI is independent and does not require Docker Desktop or Docker Engine on the host. Platforms and virtualization requirements must be checked in the current installation documentation. Daemon separation prevents treating access to its internal Docker as direct access to the host daemon.

The agent can run code and tools in its environment, but the workspace mode matters. In direct mode, real host files are shared, and changes are bidirectional and immediate. A separate kernel does not protect what is deliberately exposed as a shared directory. Other working modes change that contract and need explicit review.

A container escape can reach microVM resources, including its kernel or daemon. Compromising the guest kernel does not remove the hypervisor boundary on its own, but neither does it establish that every escape remains contained. The hypervisor, shared workspace, network, credentials, and external tools are part of the actual scope and of what is chosen to expose to the agent.

The sandbox helps reduce the impact of running untrusted code. It does not remove the need to bound which files, identities, and actions that code can reach.

Operating according to the requirements

Docker can be used in production. Kubernetes and Amazon ECS are orchestration options according to the requirements; they are not universal prerequisites for running containers. The decision depends on deployments, replicas, discovery, recovery, distribution across machines, and service operations. A single host and a distributed system face different problems.

Packaging does not determine asynchronous work recovery, database consistency, or the handling of an uncertain payment. Those contracts remain even when participants share an image or cluster.

A useful release identifies the artifact it runs, its external dependencies, the resources it exposes, and the state that must survive. Verification separates whether the image can be built, whether the process starts, and whether the service completes its operation under the relevant limits and failures.