Skip to content
Logo

Docker & Containers

This guide covers running Foundry in Docker for reproducible builds, CI pipelines, and isolated development environments.

Official Foundry Docker image

Use the official Foundry image from GitHub Container Registry:

$ docker pull ghcr.io/foundry-rs/foundry:latest

Available tags:

TagDescription
latestLatest stable release
nightlyNightly build
v1.0.0Specific version

Running Foundry commands

# Run forge build
$ docker run --rm -v $(pwd):/app -w /app ghcr.io/foundry-rs/foundry forge build
 
# Run tests
$ docker run --rm -v $(pwd):/app -w /app ghcr.io/foundry-rs/foundry forge test
 
# Run cast
$ docker run --rm ghcr.io/foundry-rs/foundry cast block-number --rpc-url https://ethereum.reth.rs/rpc

Shell alias for convenience

Add to your shell profile:

~/.bashrc
$ alias dforge='docker run --rm -v $(pwd):/app -w /app ghcr.io/foundry-rs/foundry forge'
$ alias dcast='docker run --rm ghcr.io/foundry-rs/foundry cast'
$ alias danvil='docker run --rm -p 8545:8545 ghcr.io/foundry-rs/foundry anvil --host 0.0.0.0'

Then use:

$ dforge build
$ dforge test
$ dcast balance 0x1234...

Custom Dockerfile

Create a project-specific image with your dependencies:

Dockerfile
FROM ghcr.io/foundry-rs/foundry:latest
 
WORKDIR /app
 
# Copy dependency manifests first for caching
COPY foundry.toml .
COPY remappings.txt .
 
# Install git for dependency management
RUN apk add --no-cache git
 
# Copy source files
COPY src ./src
COPY test ./test
COPY script ./script
COPY lib ./lib
 
# Build the project
RUN forge build
 
# Default command
CMD ["forge", "test"]

Build and run:

$ docker build -t my-project .
$ docker run --rm my-project
$ docker run --rm my-project forge test -vvv

Docker Compose setup

For projects needing Anvil alongside tests:

docker-compose.yml
services:
  anvil:
    image: ghcr.io/foundry-rs/foundry:latest
    command: anvil --host 0.0.0.0
    ports:
      - "8545:8545"
    healthcheck:
      test: ["CMD", "cast", "block-number", "--rpc-url", "http://localhost:8545"]
      interval: 2s
      timeout: 5s
      retries: 10
 
  test:
    image: ghcr.io/foundry-rs/foundry:latest
    volumes:
      - .:/app
    working_dir: /app
    depends_on:
      anvil:
        condition: service_healthy
    command: forge test --fork-url http://anvil:8545
 
  deploy:
    image: ghcr.io/foundry-rs/foundry:latest
    volumes:
      - .:/app
    working_dir: /app
    depends_on:
      anvil:
        condition: service_healthy
    command: forge script script/Deploy.s.sol --broadcast --rpc-url http://anvil:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

Run tests:

$ docker compose up test

Deploy to local Anvil:

$ docker compose up deploy

CI with GitHub Actions

.github/workflows/test.yml
name: Test
 
on: [push, pull_request]
 
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
 
      - name: Install Foundry
        uses: foundry-rs/foundry-toolchain@v1
 
      - name: Build
        run: forge build
 
      - name: Test
        run: forge test -vvv
 
      - name: Check formatting
        run: forge fmt --check
 
      - name: Gas snapshot
        run: forge snapshot --check --tolerance 5

CI with Docker-based runners

For self-hosted runners or environments without foundry-toolchain:

.github/workflows/test.yml
name: Test
 
on: [push, pull_request]
 
jobs:
  test:
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/foundry-rs/foundry:latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
 
      - name: Build
        run: forge build
 
      - name: Test
        run: forge test -vvv

Reproducible builds

Pin the Foundry version for reproducible builds:

Dockerfile
FROM ghcr.io/foundry-rs/foundry:v1.0.0
 
# ... rest of Dockerfile

Or in CI:

.github/workflows/test.yml
- name: Install Foundry
  uses: foundry-rs/foundry-toolchain@v1
  with:
    version: v1.0.0

Running Anvil in Docker

Expose Anvil to your host machine:

$ docker run --rm -p 8545:8545 ghcr.io/foundry-rs/foundry anvil --host 0.0.0.0

Anvil must bind to 0.0.0.0 (not 127.0.0.1) to be accessible from outside the container.

With persistent state:

$ docker run --rm -p 8545:8545 -v anvil-state:/state ghcr.io/foundry-rs/foundry \
    anvil --host 0.0.0.0 --state /state/anvil.json

Multi-stage builds

Optimize image size with multi-stage builds:

Dockerfile
# Build stage
FROM ghcr.io/foundry-rs/foundry:latest AS builder
 
WORKDIR /app
COPY . .
RUN forge build --optimize
 
# Runtime stage
FROM ghcr.io/foundry-rs/foundry:latest AS runtime
 
WORKDIR /app
COPY --from=builder /app/out ./out
COPY --from=builder /app/broadcast ./broadcast
 
# Only include what's needed for deployment
CMD ["forge", "script", "script/Deploy.s.sol", "--broadcast"]

Environment variables

Pass secrets and configuration:

$ docker run --rm \
    -v $(pwd):/app \
    -w /app \
    -e PRIVATE_KEY \
    -e ETHERSCAN_API_KEY \
    ghcr.io/foundry-rs/foundry \
    forge script script/Deploy.s.sol --broadcast --verify

Or with an env file:

$ docker run --rm \
    -v $(pwd):/app \
    -w /app \
    --env-file .env \
    ghcr.io/foundry-rs/foundry \
    forge script script/Deploy.s.sol --broadcast

Best practices

PracticeDescription
Pin versionsUse specific tags, not latest, for reproducibility
Cache dependenciesCopy manifests before source for better layer caching
Use multi-stageReduce final image size by excluding build artifacts
Mount volumesUse -v for local development, COPY for CI
Expose correctlyUse --host 0.0.0.0 for Anvil in containers