← All posts
FEB 2026

Install & Run Claude Code CLI in Docker (with docker-compose) — Safe Network Isolation

Run Anthropic's Claude Code CLI in a Docker container and docker-compose on Linux — step-by-step install, Dockerfile, and optional Squid proxy for domain-level network isolation.

Docker container running Claude Code CLI with Squid proxy filtering network traffic
On this page

Anthropic’s Claude Code CLI can run bash commands, install packages, and make network requests. That’s powerful — but a single bad prompt could reach places you don’t want it to. This guide shows how to install and run Claude Code CLI in Docker on Linux with full autonomy inside the container, while only allowing outbound traffic to an allowlist of domains.

You’ll end up with a standard Claude Code Docker image, a docker-compose.yml for orchestration, and an optional Squid proxy for domain-level network isolation. All four files fit in ~80 lines.

Quick start: install Claude Code CLI in Docker

If you just want a working container, here are the four files you need:

  • Dockerfile — extends node:20-slim, installs @anthropic-ai/claude-code via npm
  • entrypoint.sh — forwards your OAuth credentials into the container
  • squid.conf — Squid proxy allowlist for domain-level network isolation (optional but recommended)
  • docker-compose.yml — wires them together

Jump to The Files for the contents, or read the next section first for the architecture.

Why isolate Claude Code CLI’s network access?

Claude Code is an agentic CLI. Given tool-use permission, it can curl arbitrary URLs, git push to remotes, ssh into hosts, pip install from any index, and rm -rf anything it has access to. Docker already solves the filesystem blast-radius problem (mount only the workspace), but network is the other half — an agent that can reach arbitrary domains can exfiltrate data, fetch attacker-controlled scripts, or send SSRF-style requests to your internal network.

A Squid proxy on a dedicated allowlist fixes this with three properties:

  1. No direct internet from the agent container. The Docker network is marked internal: true, so the only way out is through the proxy.
  2. Allowlist, not blocklist. You name the domains the agent is allowed to reach (Anthropic API, GitHub, npm registry, etc.). Everything else fails closed.
  3. Transparent to the agent. Claude Code sees standard HTTP_PROXY / HTTPS_PROXY environment variables and respects them without any wrapper code.

Architecture

The setup has two pieces:

  1. A Squid proxy container that maintains the domain allowlist.
  2. A Claude Code container on an internal Docker network. All its traffic goes through the proxy.
┌─────────────────────────────────────────────┐
│  Host                                       │
│                                             │
│  ┌────────────┐    ┌─────────────────────┐  │
│  │ Squid      │◄───│ isolated network    │  │
│  │ (allowlist)│    │   ┌───────────────┐ │  │
│  └─────┬──────┘    │   │ Claude Agent  │ │  │
│        │           │   │ (ephemeral)   │ │  │
│   internet         │   └───────────────┘ │  │
│   network          └─────────────────────┘  │
│        │                                    │
│        ▼                                    │
│   Internet (filtered)                       │
└─────────────────────────────────────────────┘

The agent can do whatever it wants inside /workspace. If it tries to reach example.com or any domain not on the allowlist, the proxy blocks it.

Prerequisites

  • Linux (Debian/Ubuntu, Fedora, Arch — anything with a recent kernel). The setup also works on Docker Desktop for macOS and Windows.
  • Docker Engine 24.0 or newer
  • Docker Compose v2 (comes bundled with current Docker Desktop and the docker-compose-plugin package)
  • An Anthropic API key or an existing claude login on the host (the entrypoint forwards OAuth credentials from ~/.claude into the container)

If you don’t have Docker yet:

# Debian / Ubuntu
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker "$USER"   # log out / back in to pick up group membership

The files

You need four files in a single directory. Here’s each one.

Dockerfile — the Claude Code Docker image

FROM node:20-slim

RUN npm install -g @anthropic-ai/claude-code

RUN apt-get update && apt-get install -y git curl ca-certificates && rm -rf /var/lib/apt/lists/*

COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

USER node
WORKDIR /workspace

ENTRYPOINT ["entrypoint.sh"]

The node user in node:20-slim has UID 1000, which matches most Linux host users. Volume mounts work without permission tweaks.

entrypoint.sh — OAuth credential forwarding

Claude Code reads OAuth credentials from ~/.claude and ~/.claude.json. We mount the host’s credentials read-only, then copy them into the container so Claude has a writable home directory for session data.

#!/bin/bash
set -e

if [ -d "/host-claude" ]; then
    mkdir -p "$HOME/.claude"
    if [ -f "/host-claude/.credentials.json" ]; then
        cp "/host-claude/.credentials.json" "$HOME/.claude/.credentials.json"
    fi
    for f in statsig.json settings.json; do
        if [ -f "/host-claude/$f" ]; then
            cp "/host-claude/$f" "$HOME/.claude/$f"
        fi
    done
fi

if [ -f "/host-claude.json" ]; then
    cp "/host-claude.json" "$HOME/.claude.json"
fi

exec claude "$@"

squid.conf — the domain allowlist

Edit this to match what your agent needs access to.

acl allowed_sites dstdomain .anthropic.com
acl allowed_sites dstdomain .github.com
acl allowed_sites dstdomain .googleapis.com
acl allowed_sites dstdomain registry.npmjs.org
acl allowed_sites dstdomain .sentry.io
acl allowed_sites dstdomain .statsigapi.net

acl SSL_ports port 443
acl CONNECT method CONNECT
http_access allow CONNECT allowed_sites
http_access allow allowed_sites
http_access deny all

http_port 3128

Gotcha: Squid doesn’t allow overlapping entries. If you add .anthropic.com (leading dot covers all subdomains), don’t also add api.anthropic.com. Squid will refuse to start.

docker-compose.yml — wiring it together

services:
  proxy:
    image: ubuntu/squid
    volumes:
      - ./squid.conf:/etc/squid/squid.conf:ro
    networks:
      - isolated
      - internet
    restart: unless-stopped

  agent:
    build: .
    depends_on:
      - proxy
    environment:
      - HTTP_PROXY=http://proxy:3128
      - HTTPS_PROXY=http://proxy:3128
      - http_proxy=http://proxy:3128
      - https_proxy=http://proxy:3128
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
    volumes:
      - ${WORKSPACE:-./workspace}:/workspace
      - ${HOME}/.claude:/host-claude:ro
      - ${HOME}/.claude.json:/host-claude.json:ro
    networks:
      - isolated
    deploy:
      resources:
        limits:
          cpus: "${AGENT_CPUS:-2}"
          memory: "${AGENT_MEMORY:-4g}"
    profiles:
      - run

networks:
  isolated:
    internal: true
  internet:
    driver: bridge

The agent service is under the run profile, so docker compose up only starts the proxy. Agents are started on demand with docker compose run.

Running Claude Code in the container

Start the proxy once:

docker compose up -d proxy

Run Claude Code against a project:

WORKSPACE=/path/to/project docker compose run --rm agent \
  -p "Your prompt here" --dangerously-skip-permissions

Everything after agent is passed directly to the Claude CLI. You get access to every flag Claude supports without any wrapper getting in the way.

When you’re done:

docker compose down

Skipping permissions inside the sandbox

The --dangerously-skip-permissions flag tells Claude Code to execute all tool calls without asking for confirmation. Normally you wouldn’t want this — but inside an isolated container with filtered network access, it’s the right call. The container is the sandbox.

Running parallel Claude Code agents

Each docker compose run creates a separate container. Run as many as you need:

WORKSPACE=./project-a docker compose run --rm agent -p "Fix tests" --dangerously-skip-permissions &
WORKSPACE=./project-b docker compose run --rm agent -p "Add logging" --dangerously-skip-permissions &
wait

They share the proxy but are otherwise completely isolated from each other. This is how I run automated code review workflows and GitHub-issue-to-production pipelines across multiple repos at the same time.

API key instead of OAuth

If you prefer API key authentication over OAuth:

ANTHROPIC_API_KEY=sk-ant-xxx docker compose run --rm agent \
  -p "Your prompt" --dangerously-skip-permissions

Resource limits

Default is 2 CPUs and 4 GB memory. Override with environment variables:

AGENT_CPUS=4 AGENT_MEMORY=8g WORKSPACE=./project docker compose run --rm agent \
  -p "Heavy task" --dangerously-skip-permissions

Optional: wrapper script

If you don’t want to type docker compose -f /path/to/docker-compose.yml every time:

#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
export WORKSPACE="${WORKSPACE:-$(pwd)}"

case "${1:-help}" in
    up)     docker compose -f "$SCRIPT_DIR/docker-compose.yml" up -d proxy ;;
    down)   docker compose -f "$SCRIPT_DIR/docker-compose.yml" down ;;
    status) docker compose -f "$SCRIPT_DIR/docker-compose.yml" ps ;;
    run)    shift; docker compose -f "$SCRIPT_DIR/docker-compose.yml" run --rm agent "$@" ;;
    *)      echo "Usage: $0 {up|down|status|run [claude args...]}" ;;
esac

Or a shell alias in ~/.zshrc:

alias claude-docker='docker compose -f ~/path/to/docker-compose.yml'

Verifying the network isolation works

To confirm the Squid proxy is actually blocking traffic:

# This should fail (not on the allowlist)
docker compose run --rm agent curl -s https://example.com
# Connection refused

# This should succeed (on the allowlist)
docker compose run --rm agent curl -s -o /dev/null -w "%{http_code}" https://api.anthropic.com
# 404 (reachable, just no content at the root)

You can also tail the Squid access log while the agent runs — every outbound request shows up there, which is useful for auditing what Claude Code actually reached out to during a session.

When to use this vs. running Claude Code natively

Situation Docker + Squid isolation Native claude
Interactive pair-programming on your own machine Overkill Better — no overhead
Batch / CI agent runs Recommended Risky
Running prompts from untrusted sources Recommended Dangerous
Parallel agents on isolated workspaces Recommended Works but no resource limits
Compliance / audit requirements Recommended (Squid logs are great evidence) Harder to justify

For day-to-day coding with Claude Code I run it natively. For anything automated, untrusted, or multi-tenant, I run it in this Docker setup.

FAQ

Is there an official Anthropic Docker image for Claude Code CLI? No. Anthropic distributes Claude Code as an npm package (@anthropic-ai/claude-code). The Dockerfile above wrapping it in node:20-slim is currently the closest to an official image.

How do I install Claude Code CLI inside a Docker container? Build the image with docker compose build agent. The Dockerfile uses npm install -g @anthropic-ai/claude-code on top of node:20-slim.

Does Claude Code CLI work with docker-compose? Yes — see the docker-compose.yml above. The agent service is guarded by the run profile so docker compose up only starts the proxy; each invocation gets a fresh ephemeral container.

How do I isolate Claude Code’s network access? Put the agent container on an internal: true Docker network with no bridge to the host, and route all HTTP(S) traffic through a Squid proxy with a dstdomain allowlist. Non-allowlisted domains fail closed.

Can I run multiple Claude Code agents in parallel? Yes — each docker compose run --rm agent creates a separate container. They share the Squid proxy but are otherwise isolated.

Wrapping up

This setup gives you the best of both worlds. Claude Code gets full autonomy to read files, write code, run tests, and install packages. But it can only talk to the domains you approve. Docker handles the filesystem isolation, Squid handles the network filtering, and docker compose run handles spinning up ephemeral agents.

The whole thing is about 80 lines of configuration across four files. No custom tooling, no complex orchestration — just stock Docker, Docker Compose, and a battle-tested proxy.