Skip to main content
You can run Base on your machine in two ways, both from the base/base repo. The Docker devnet starts a complete, fresh Base stack. The snapshot devnet continues real Base Mainnet state locally, so you can test against deployed contracts and act as any account.
To build against new Base features without running infrastructure, use the hosted Vibenet instead.

Choose a Devnet

Use the Docker devnet to test node behavior or anything that depends on L1. Use the snapshot devnet to test your app against the contracts and liquidity it will meet on Base Mainnet.

Prerequisites

Both devnets need:
  • A clone of base/base. Run every command from its root.
  • just 1.51.0 or newer
  • Foundry (cast) to send transactions
  • jq
Each section below lists what its devnet needs in addition.

Docker Devnet

The Docker devnet runs a complete Base stack: an L1 (Reth + Lighthouse), a Base sequencer, two RPC nodes, and a batcher. It also runs a shadow batcher and shadow validator used to test the Rust batcher. It also needs Docker with Compose v2 and Buildx. The first build compiles the Rust services, so allocate generous memory to Docker.
The Docker devnet is disposable. just devnet up, just devnet up-single, and just devnet down delete all chain data in .devnet/. It publishes ports on all interfaces, so run it on an isolated development machine.

Start the Devnet

Terminal
This stops any running devnet, clears its data, builds the images, and starts a single-sequencer stack. The first run compiles the Rust services and takes several minutes. Check that blocks are advancing:
Terminal

Send a Test Transaction

The devnet prefunds the standard Foundry/Anvil test accounts, listed in etc/docker/devnet-env. Send ETH through the sequencer:
Terminal
A receipt with status 1 confirms the sequencer included the transaction. To run the built-in L1 and L2 checks instead, use just devnet checks.
The test account keys are public. Never use them on Mainnet or Sepolia.

Endpoints

Ports are set in etc/docker/devnet-env. In just devnet up mode, 7545 is one of three conductor-managed sequencers. After a leader change, transactions sent to 7545 can stay pending; send through the RPC node at 8545, which forwards to the current leader.

Hardfork Activation

The devnet schedules Azul at L2 block 20, Beryl at 21, Cobalt at 22, and Denim at 25, so a fresh devnet crosses each activation within its first few blocks. Before Denim, the devnet produces a block every 2 seconds. At Denim, the sequencer switches to 200ms blocks.
The devnet’s canonical batcher (op-batcher) cannot decode EIP-8130 transactions (type 0x79). After the first block that contains one, it stops advancing and the canonical safe head stalls. The shadow base-batcher supports these transactions, so the shadow validator keeps deriving past those blocks. See the devnet README for details.
To move an activation, set L2_BASE_DENIM_BLOCK (or the matching L2_BASE_<HARDFORK>_BLOCK variable) before up or up-single. Set it to an empty value to leave that hardfork unscheduled.

Common Commands

Run just devnet with no arguments for the full list.

Snapshot Devnet

The snapshot devnet downloads a Base node snapshot and continues it as a private local chain. One base dev process runs an execution node and a sequencer that produces 200ms blocks on top of the snapshot head. Deployed contracts, balances, and storage are exactly what they were on Base Mainnet at that block. There is no L1: the chain does not derive from L1, bridge, or advance safe and finalized heads. It also needs:
  • The Rust toolchain from the repo’s rust-toolchain.toml (install rustup), Go, a C/C++ compiler, libclang, OpenSSL development headers, pkg-config, CMake, and protoc. The just recipes build base from your checkout; Docker is not required.
  • At least 1.5 TB of free disk for a Base Mainnet snapshot, which uses about 1.4 TB once unpacked, preferably on a fast NVMe drive. The workflow has been tested on Linux.
Use a dedicated datadir that only the devnet uses. Development blocks and minted balances permanently diverge from Base Mainnet, so never point it at a running node’s datadir or your only copy of a snapshot. To start over, restore a clean copy or download again.

Download a Snapshot

Terminal
This downloads the current state plus 1,339,200 blocks of history (about 830 GiB for Base Mainnet) and writes a node configuration into the directory. It does not start a node. To see the exact archives and sizes before downloading anything, add --print-plan-json. If the download is interrupted, rerun the same command to resume; verified archives are reused. Add --manifest-url <url> to pin a specific snapshot. Never rerun a download into a directory that has already produced development blocks. To use Base Sepolia, keep BASE_CHAIN unset for the download and pass --chain:
Terminal

Run the Devnet

Terminal
For Sepolia, prefix the command with BASE_CHAIN=sepolia. The command builds base, prints the RPC endpoints and the snapshot head, and runs in the foreground.
On the first run, the node rebuilds the transaction and history indexes the snapshot does not include. RPC answers within seconds, but no blocks are produced until the rebuild finishes, which took about 22 minutes for a Base Mainnet snapshot in testing. Later runs start sequencing within seconds.
Both endpoints serve the standard eth_*, debug_*, and trace_* methods, so Foundry, viem, and Hardhat work by pointing them at the HTTP URL. Change the ports with --http.port and --ws.port. The endpoints listen only on loopback and are unauthenticated; keep them private, because anyone who can reach them can act as any account. Check that blocks are advancing:
Terminal
Stop the devnet with Ctrl+C. Rerun the same command to resume from where it stopped. Downtime does not produce catch-up blocks.

Impersonate Accounts

The snapshot devnet adds one RPC method, dev_impersonateTransaction, that runs a transaction as any account without its private key. Use it to fund accounts, move tokens from existing holders, and call owner-only or admin-only functions. The method returns a transaction hash. The request runs as a deposit transaction: it skips signature, nonce, and fee checks, and pays no fees. Signature checks inside contracts, such as ecrecover, EIP-1271, or permit, still apply. To test fees and transaction admission, send signed transactions instead.
The snapshot devnet keeps the snapshot’s chain ID, 8453 for Base Mainnet. Act as real accounts with dev_impersonateTransaction, and sign only with throwaway keys like the one below.

Fund a New Account

Generate a throwaway key and mint it 1 ETH:
Terminal
After the next block, check the receipt and balance:
Terminal
The receipt of an impersonated request has type 0x7e, an effectiveGasPrice of zero, and from set to the impersonated account. Each request increments that account’s nonce. The account can now send normal signed transactions:
Terminal

Act as a Token Holder

Pick any account that holds WETH on Base Mainnet, such as one you find on a block explorer, and check its balance:
Terminal
Transfer 0.001 WETH from the holder to your account, then check your balance after the next block:
Terminal

Call an Owner-Only Function

Set from to the account the contract authorizes. For a contract with an owner() function:
Terminal
Replace setValue(uint256) with the function you want to call. The same request from any other account is included with status 0.

Deploy a Contract as an Account

Omit to and pass the init code as input. This example deploys a contract that returns 42:
Terminal
After the next block, call it:
Terminal
The receipt’s contractAddress is wrong for contracts deployed with dev_impersonateTransaction: it reports the address for nonce zero. The contract is deployed at the address computed from the sender’s nonce before the request, as shown above. Read the nonce first and use cast compute-address.

Confirm Inclusion

A returned hash means the request is queued, not mined. It is included in the next block with enough gas, usually within about 200ms. Poll cast receipt until it returns.
  • Do not resubmit while waiting. Every request is a new transaction, so a repeated request runs again.
  • Reverts are still included. A call that reverts or runs out of gas is included with status 0. Any minted ETH is kept.
  • Pending requests are held in memory. Requests not yet included are lost if the process stops.

Block Timing

The snapshot devnet produces a block every 200ms. Block header timestamps have whole-second resolution, so about five consecutive blocks share a timestamp. Contracts that count blocks see time pass 10 times faster than on a 2-second chain; contracts that compare block.timestamp see normal time. Heavy disk or CPU load can delay blocks.

Learn More

The base/base repo is the source of truth for both devnets:
  • base dev reference: snapshot devnet download retention, restarts, timing, and dev_impersonateTransaction limits
  • Devnet README: Docker devnet topology, batcher setup, the Anvil + Nitro proving variant, and upgrade-signal testing
  • etc/docker/Justfile: every Docker devnet command
  • Testing guide: system tests against the Docker devnet (just devnet tests)
  • Standalone proving guide: run the prover stack, which the Docker devnet does not include. ZK proving against a local devnet requires disabling Beryl and Cobalt.