Run Docker-in-Docker
This guide shows you how to run Docker inside a Sandbox, enabling you to build and run container images from within a secure sandbox.
Use Docker-in-Docker when you need to:
- Develop containerized applications - Run
docker buildto create images from Dockerfiles - Run Docker as part of CI/CD - Respond to code changes and build and push images using Cloudflare Containers
- Run arbitrary container images - Start containers from an end-user provided image
Cloudflare Containers run without root privileges, so you must use the rootless Docker image. Create a custom Dockerfile that combines the sandbox binary with Docker:
FROM docker:dind-rootlessUSER root
# Use the musl build so it runs on Alpine-based docker:dind-rootlessCOPY --from=docker.io/cloudflare/sandbox:0.7.4-musl /container-server/sandbox /sandboxCOPY --from=docker.io/cloudflare/sandbox:0.7.4-musl /usr/lib/libstdc++.so.6 /usr/lib/libstdc++.so.6COPY --from=docker.io/cloudflare/sandbox:0.7.4-musl /usr/lib/libgcc_s.so.1 /usr/lib/libgcc_s.so.1COPY --from=docker.io/cloudflare/sandbox:0.7.4-musl /bin/bash /bin/bashCOPY --from=docker.io/cloudflare/sandbox:0.7.4-musl /usr/lib/libreadline.so.8 /usr/lib/libreadline.so.8COPY --from=docker.io/cloudflare/sandbox:0.7.4-musl /usr/lib/libreadline.so.8.2 /usr/lib/libreadline.so.8.2
# Create startup script that starts dockerd with# iptables disabled, waits for readiness, then keeps runningRUN printf '#!/bin/sh\n\ set -eu\n\ dockerd-entrypoint.sh dockerd --iptables=false --ip6tables=false &\n\ until docker version >/dev/null 2>&1; do sleep 0.2; done\n\ echo "Docker is ready"\n\ wait\n' > /home/rootless/boot-docker-for-dind.sh && chmod +x /home/rootless/boot-docker-for-dind.sh
ENTRYPOINT ["/sandbox"]CMD ["/home/rootless/boot-docker-for-dind.sh"]Once deployed, you can run Docker commands through the sandbox:
import { getSandbox } from "@cloudflare/sandbox";
const sandbox = getSandbox(env.Sandbox, "docker-sandbox");
// Build an imageawait sandbox.writeFile( "/workspace/Dockerfile", `FROM alpine:latestRUN apk add --no-cache curlCMD ["echo", "Hello from Docker!"]`,);
const build = await sandbox.exec( "docker build --network=host -t my-image /workspace",);if (!build.success) { console.error("Build failed:", build.stderr);}
// Run a containerconst run = await sandbox.exec("docker run --network=host --rm my-image");console.log(run.stdout); // "Hello from Docker!"import { getSandbox } from "@cloudflare/sandbox";
const sandbox = getSandbox(env.Sandbox, "docker-sandbox");
// Build an imageawait sandbox.writeFile( "/workspace/Dockerfile", `FROM alpine:latestRUN apk add --no-cache curlCMD ["echo", "Hello from Docker!"]`,);
const build = await sandbox.exec( "docker build --network=host -t my-image /workspace",);if (!build.success) { console.error("Build failed:", build.stderr);}
// Run a containerconst run = await sandbox.exec("docker run --network=host --rm my-image");console.log(run.stdout); // "Hello from Docker!"Docker-in-Docker in Cloudflare Containers has the following limitations:
- No iptables - Network isolation features that rely on iptables are not available
- Rootless mode only - You cannot use privileged containers or features requiring root
- Ephemeral storage - Built images and containers are lost when the sandbox sleeps. You must persist them manually.
- Dockerfile reference - Customize your sandbox image
- Execute commands - Run commands in the sandbox
- Background processes - Manage long-running processes