Skip to main content
Developer mode (--dev) starts Geth as a private single-node network optimized for local development. It provides:
  • A pre-funded developer account with no password
  • Mining on demand — blocks are only produced when there are pending transactions
  • An ephemeral chain that starts fresh on every restart (unless --datadir is set)
  • A low chain ID (1337) that MetaMask and most tooling recognize

Starting dev mode

geth --dev
Geth will print the address of the pre-funded account and the IPC endpoint:
INFO [..] Using developer account  address=0x...
INFO [..] IPC endpoint opened       url=/tmp/geth.ipc

Enabling the HTTP-RPC server

geth --dev --http --http.api eth,web3,net,debug
The JSON-RPC server listens on http://127.0.0.1:8545 by default.

Enabling the WebSocket server

geth --dev --ws --ws.api eth,web3,net

Persistent dev node

By default the dev chain is in-memory and disappears when Geth exits. To keep state across restarts, set a data directory:
geth --dev --datadir /tmp/geth-dev
The same pre-funded account is restored on every restart because the genesis is deterministic.

Connecting external tooling

MetaMask

SettingValue
Network nameLocalhost 8545
RPC URLhttp://127.0.0.1:8545
Chain ID1337
Currency symbolETH
Start Geth with --http and --http.corsdomain "*" (development only) before connecting MetaMask.
geth --dev \
  --http \
  --http.api eth,web3,net \
  --http.corsdomain "*"
Setting --http.corsdomain "*" allows any website to make RPC calls to your node. Only use it for local development.

Hardhat / Foundry / other tools

Point any tool that accepts an RPC endpoint to http://127.0.0.1:8545. The dev account private key can be retrieved via the IPC console:
geth attach /tmp/geth.ipc
// Inside the Geth console:
eth.accounts
admin.nodeInfo.enode

Simulated backend for Go tests

The ethclient/simulated package provides an in-process simulated blockchain that is suitable for unit and integration tests in Go. It always uses chain ID 1337 and mines blocks only when you call Commit().

Setting up the backend

package mypackage_test

import (
	"context"
	"math/big"
	"testing"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient/simulated"
)

func TestSimulated(t *testing.T) {
	// Generate a key for the funded account.
	key, _ := crypto.GenerateKey()
	addr := crypto.PubkeyToAddress(key.PublicKey)

	// Pre-fund the account in the genesis block.
	alloc := types.GenesisAlloc{
		addr: {Balance: big.NewInt(1e18)},
	}

	// Create the backend.
	backend := simulated.NewBackend(alloc)
	defer backend.Close()

	client := backend.Client()

	// Check the balance.
	balance, err := client.BalanceAt(context.Background(), addr, nil)
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("balance: %s", balance)

	// Mine a block.
	committedHash := backend.Commit()
	t.Logf("committed block hash: %s", committedHash)
}

Backend API

MethodDescription
NewBackend(alloc, ...options)Create a new simulated blockchain with the given genesis allocation
backend.Client()Return an ethclient-compatible client for the simulated chain
backend.Commit()Seal a block and advance the chain. Returns the new block hash.
backend.Rollback()Discard all pending transactions and revert to the last committed state
backend.Fork(parentHash)Start a side chain from the given block (for reorg simulation)
backend.AdjustTime(d)Advance the block timestamp by d on the next empty block
backend.Close()Shut down the backend. Must be called when the test is done.

Deploying and calling a contract in tests

package mypackage_test

import (
	"context"
	"math/big"
	"testing"

	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient/simulated"

	"myproject/token" // generated by abigen
)

func TestDeploy(t *testing.T) {
	key, _ := crypto.GenerateKey()
	addr := crypto.PubkeyToAddress(key.PublicKey)

	backend := simulated.NewBackend(types.GenesisAlloc{
		addr: {Balance: big.NewInt(1e18)},
	})
	defer backend.Close()

	auth, err := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
	if err != nil {
		t.Fatal(err)
	}
	auth.GasLimit = 3_000_000

	// Deploy using abigen-generated bindings.
	_, _, instance, err := token.DeployToken(
		auth,
		backend.Client(),
		"TestToken",
		big.NewInt(1000),
	)
	if err != nil {
		t.Fatal(err)
	}

	// Mine the deployment transaction.
	backend.Commit()

	// Call a view function.
	name, err := instance.Name(&bind.CallOpts{Context: context.Background()})
	if err != nil {
		t.Fatal(err)
	}
	if name != "TestToken" {
		t.Fatalf("expected TestToken, got %s", name)
	}
}
The simulated backend is faster and more deterministic than running a live dev node. Prefer it for unit tests and reserve geth --dev for end-to-end or manual testing scenarios.

Build docs developers (and LLMs) love