tion, meets enterprise compliance requirements, and provides immediate visibility into supply chain components without sacrificing deployment velocity.
Core Solution
Securing an AI agent container requires a layered strategy that addresses image composition, build reproducibility, secret management, and runtime enforcement. The following implementation demonstrates a production-ready pattern using TypeScript, Docker multi-stage builds, and hardened base images.
Step 1: Base Image Selection & Digest Pinning
Generic distribution images include development tools, debug utilities, and unnecessary system libraries. Hardened images strip these components and apply continuous security patches. Always pin by digest to guarantee immutability.
# syntax=docker/dockerfile:1.7
ARG HARDENED_NODE_IMAGE=ghcr.io/docker/hardened-images/node:22-slim
ARG HARDENED_DIGEST=sha256:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
FROM ${HARDENED_NODE_IMAGE}@${HARDENED_DIGEST} AS builder
Rationale: Digest pinning prevents tag mutation attacks. If a registry is compromised, the digest remains cryptographically verifiable. Hardened images reduce the initial CVE surface by 30-50% compared to standard distributions.
Step 2: Multi-Stage Compilation & Dependency Pruning
Separate build tooling from runtime execution. Install production dependencies only, and clear package manager caches to minimize image footprint.
WORKDIR /usr/src/agent-core
COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY tsconfig.json ./
COPY src/ ./src/
COPY scripts/ ./scripts/
RUN npm run build
Rationale: npm ci enforces lockfile consistency, preventing dependency drift. Excluding dev dependencies removes TypeScript compilers, linters, and test frameworks from the final image. Clearing the cache eliminates unnecessary layers.
Step 3: Build-Time Secret Injection
Private registries or internal Git repositories require authentication during the build phase. Never use ARG or ENV for credentials. Docker’s secret mount feature passes sensitive data as ephemeral files that are never persisted in image layers.
RUN --mount=type=secret,id=registry_token \
npm config set //npm.internal.corp/:_authToken="$(cat /run/secrets/registry_token)" && \
npm ci --omit=dev && \
npm config delete //npm.internal.corp/:_authToken
Build invocation:
docker build \
--secret id=registry_token,env=CORP_NPM_TOKEN \
-t ai-agent-runtime:stable .
Rationale: Secret mounts operate in isolated build contexts. The token is available only during the RUN instruction and is automatically purged. This prevents credential leakage in docker history or registry layer inspection.
Step 4: Runtime Privilege Reduction
The final stage copies only compiled artifacts, enforces non-root execution, and applies filesystem restrictions.
FROM ${HARDENED_NODE_IMAGE}@${HARDENED_DIGEST} AS runner
WORKDIR /opt/agent
ENV NODE_ENV=production
ENV NODE_OPTIONS="--max-old-space-size=1024"
COPY --from=builder /usr/src/agent-core/dist/ ./dist/
COPY --from=builder /usr/src/agent-core/node_modules/ ./node_modules/
RUN groupadd --system agent-svc && \
useradd --system --gid agent-svc --no-create-home agent-runner && \
chown -R agent-runner:agent-svc /opt/agent
USER agent-runner
EXPOSE 8080
CMD ["node", "dist/bootstrap.js"]
Rationale: Running as a non-root user prevents container escape exploits. NODE_OPTIONS caps memory usage to prevent runaway LLM loops from exhausting host resources. The chown command ensures the application user owns only necessary directories.
Container runtime configuration enforces the principle of least privilege. Use Docker Compose or Kubernetes manifests to restrict capabilities, enforce read-only filesystems, and isolate tool execution paths.
services:
orchestrator:
image: ai-agent-runtime:stable
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=256m
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
mem_limit: 1.5g
cpus: 1.0
environment:
- NODE_ENV=production
- MCP_REGISTRY_URL=${MCP_REGISTRY_URL}
- DB_CONNECTION_STRING=${DB_CONNECTION_STRING}
volumes:
- ./workspace:/opt/agent/workspace:ro
- agent-logs:/var/log/agent
networks:
- internal-tools
- external-apis
volumes:
agent-logs:
networks:
internal-tools:
driver: bridge
external-apis:
driver: bridge
Rationale: read_only: true prevents unauthorized filesystem modifications. tmpfs provides a secure temporary directory for browser automation or file processing. cap_drop: ALL removes Linux capabilities like NET_RAW or SYS_ADMIN. no-new-privileges blocks setuid/setgid escalation. Network segmentation isolates internal MCP servers from external model APIs.
Pitfall Guide
1. The latest Tag Trap
Explanation: Using mutable tags like latest or unpinned major versions introduces build drift. A registry update can silently inject vulnerable packages or alter system libraries.
Fix: Always pin base images by digest. Maintain a registry of approved digests and rotate them during scheduled maintenance windows.
2. Build-Arg Secret Leakage
Explanation: Passing credentials via ARG or ENV embeds them in image metadata. Tools like docker history or registry explorers can extract these values.
Fix: Use --mount=type=secret for build-time authentication. For runtime secrets, inject via orchestrator-managed secret stores (Kubernetes Secrets, AWS Secrets Manager, HashiCorp Vault).
3. Overzealous Read-Only Filesystems
Explanation: Enforcing read_only: true without accounting for temporary file requirements breaks browser automation, native module compilation, or log rotation.
Fix: Mount tmpfs volumes for /tmp and /var/log. Explicitly whitelist writable directories using named volumes. Test runtime behavior under restricted permissions before production deployment.
Explanation: Granting all MCP servers, file processors, and browser instances the same credential set violates least privilege. A compromised browser tool gains access to database credentials or model API keys.
Fix: Architect tool isolation. Run high-risk tools in separate containers or namespaces. Use scoped tokens with minimal permissions. Implement network policies to restrict inter-tool communication.
5. Ignoring SBOM Generation
Explanation: Without a Software Bill of Materials, vulnerability scanning lacks context. Teams cannot map CVEs to specific components or prioritize patching.
Fix: Generate SBOMs during CI builds using syft or Docker Scout. Store SBOMs alongside image artifacts. Integrate vulnerability thresholds into pull request gates.
6. Skipping Capability Drops
Explanation: Default container configurations retain unnecessary Linux capabilities. Attackers can leverage these for network sniffing, process manipulation, or privilege escalation.
Fix: Apply cap_drop: ALL and selectively add only required capabilities (e.g., NET_BIND_SERVICE for low ports). Document each capability addition with a security justification.
7. Assuming npm ci Equals Security
Explanation: npm ci ensures dependency consistency but does not validate package integrity or detect malicious transitive dependencies.
Fix: Combine npm ci with automated vulnerability scanning. Use npm audit in CI pipelines. Consider package signing verification or private registry proxying for critical dependencies.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Internal MCP tooling with limited scope | Single hardened container with cap_drop: ALL and scoped tokens | Reduces operational overhead while maintaining strict privilege boundaries | Low infrastructure cost; moderate engineering effort for token scoping |
| Public-facing agent with browser automation | Multi-container architecture with isolated browser runner and read-only orchestrator | Prevents browser exploit from compromising core agent state or credentials | Higher compute cost; requires network policy configuration |
| CI/CD pipeline with private npm scopes | Docker secret mounts + digest-pinned hardened base images | Eliminates credential leakage in build logs and ensures reproducible artifact generation | Minimal cost; integrates seamlessly with existing CI runners |
| Compliance-heavy deployment (SOC2, HIPAA) | SBOM generation + continuous scanning + runtime guardrails + audit logging | Provides verifiable supply chain integrity and meets regulatory audit requirements | Moderate cost for scanning tools and logging infrastructure |
Configuration Template
Dockerfile
# syntax=docker/dockerfile:1.7
ARG HARDENED_BASE=ghcr.io/docker/hardened-images/node:22-slim
ARG BASE_DIGEST=sha256:replace-with-actual-digest
FROM ${HARDENED_BASE}@${BASE_DIGEST} AS builder
WORKDIR /build
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY tsconfig.json ./
COPY src/ ./src/
RUN npm run build
FROM ${HARDENED_BASE}@${BASE_DIGEST} AS runner
WORKDIR /opt/agent
ENV NODE_ENV=production
ENV NODE_OPTIONS="--max-old-space-size=1024"
COPY --from=builder /build/dist/ ./dist/
COPY --from=builder /build/node_modules/ ./node_modules/
RUN groupadd --system agent-svc && \
useradd --system --gid agent-svc --no-create-home agent-runner && \
chown -R agent-runner:agent-svc /opt/agent
USER agent-runner
EXPOSE 8080
CMD ["node", "dist/bootstrap.js"]
docker-compose.yml
services:
agent-core:
build: .
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=256m
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
mem_limit: 1.5g
cpus: 1.0
environment:
- NODE_ENV=production
- MCP_ENDPOINT=${MCP_ENDPOINT}
- DB_URI=${DB_URI}
volumes:
- ./workspace:/opt/agent/workspace:ro
networks:
- agent-net
networks:
agent-net:
driver: bridge
GitHub Actions CI
name: Agent Security Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t agent-runtime:ci .
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: agent-runtime:ci
format: spdx-json
output-file: sbom.json
- name: Scan vulnerabilities
uses: docker/scout-action@v1
with:
image: agent-runtime:ci
command: cves
only-severities: critical,high
exit-code: 1
Quick Start Guide
- Replace base image references in your Dockerfile with the hardened equivalent from your registry. Obtain the current digest from the Docker Hardened Images catalog or your organization's artifact repository.
- Refactor dependency installation to use
npm ci --omit=dev in a dedicated build stage. Copy only compiled artifacts and production node_modules to the final stage.
- Migrate secrets from Dockerfile
ENV/ARG declarations to runtime environment variables or orchestrator-managed secret stores. Use --mount=type=secret for any build-time authentication requirements.
- Apply runtime restrictions by adding
read_only: true, cap_drop: ALL, no-new-privileges:true, and resource limits to your compose or Kubernetes manifest. Validate that temporary file operations route to tmpfs mounts.
- Integrate scanning into your CI pipeline. Generate an SBOM during the build phase and run Docker Scout or equivalent CVE analysis. Block deployments that exceed critical/high vulnerability thresholds.