Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 184 additions & 0 deletions docs/guides/deploy/local.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
---
description: Deploy an Evolve EVM chain locally for development and testing using ev-toolbox and local-da.
---

# 🏠 Local Development Deployment

This guide walks you through deploying a complete Evolve EVM chain on your local machine using [ev-toolbox](https://github.com/evstack/ev-toolbox). It uses the **local-da** mock DA layer so there are no external dependencies or token costs.

## 🏗️ How it works

The local stack is split into two Docker Compose stacks that share a Docker network (`evstack_shared`):

```mermaid
graph TB
subgraph "da-local stack"
LOCAL_DA[local-da<br/>:7980]
end

subgraph "single-sequencer stack"
SEQ_RETH[ev-reth<br/>:8545 JSON-RPC]
SEQ_EVM[ev-node-evm<br/>aggregator mode]
SEQ_RETH <--> SEQ_EVM
end

SEQ_EVM -->|Post blobs| LOCAL_DA

classDef sequencer fill:#e1f5fe
classDef da fill:#fff3e0
class SEQ_RETH,SEQ_EVM sequencer
class LOCAL_DA da
```

The `da-local` stack is started first because it creates the shared Docker network that the sequencer stack joins.

## 💻 Prerequisites {#prerequisites}

- [Docker](https://docs.docker.com/get-docker/) 20.10 or later
- [Docker Compose](https://docs.docker.com/compose/install/) v2 or later
- [Git](https://git-scm.com/)

## 🛠️ Step 1 — Clone ev-toolbox {#clone}

```bash
git clone --depth 1 https://github.com/evstack/ev-toolbox.git
cd ev-toolbox/ev-stacks/stacks
```

## 🌐 Step 2 — Start local-da {#start-local-da}

The `da-local` stack must be started first. It creates the `evstack_shared` Docker network that the sequencer stack joins.

```bash
cd da-local
docker compose up -d
```

Verify it is running:

```bash
docker logs local-da
```

Expected output:

```
INF NewLocalDA: initialized LocalDA component=da
INF Listening on component=da host=0.0.0.0 maxBlobSize=1970176 port=7980
INF server started component=da listening_on=0.0.0.0:7980
```
Comment on lines +65 to +69

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add language identifier to code block.

The fenced code block showing expected output should specify a language identifier for proper rendering and accessibility.

📝 Proposed fix
-```
+```text
 INF NewLocalDA: initialized LocalDA component=da
 INF Listening on component=da host=0.0.0.0 maxBlobSize=1970176 port=7980
 INF server started component=da listening_on=0.0.0.0:7980
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 65-65: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/guides/deploy/local.md` around lines 65 - 69, The fenced code block in
docs/guides/deploy/local.md showing the example daemon logs lacks a language
identifier; update the opening fence for that block to include a language tag
(e.g., use ```text) so the log output renders correctly and is accessible—locate
the fenced block with the three "INF ..." log lines and change its opening
backticks to ```text.

Source: Linters/SAST tools


## 🔑 Step 3 — Create the passphrase file {#passphrase}

The sequencer signs blocks with a key protected by a passphrase. Create the passphrase file in the single-sequencer directory:

```bash
cd ../single-sequencer
echo -n "devpassword" > passphrase
```

:::tip
For local development, any string works as a passphrase. Keep it simple — you will not need it again unless you restart with a wiped volume.
:::

## 🚀 Step 4 — Start the sequencer {#start-sequencer}

Make the entrypoint script executable (required after `git clone` on some systems), then start the stack using the local-DA variant of the compose file:

```bash
chmod +x entrypoint.sequencer.sh
docker compose -f docker-compose.da.local.yml up -d
```

Monitor startup:

```bash
docker compose -f docker-compose.da.local.yml logs -f
```

A healthy startup looks like:

```
single-sequencer | 🚀 INIT: Starting EVM Sequencer initialization
single-sequencer | ✅ SUCCESS: Sequencer initialization completed
single-sequencer | ✅ SUCCESS: Exported genesis.json to /volumes/sequencer_export/genesis.json
single-sequencer | ✅ SUCCESS: Successfully retrieved genesis hash: 0x6aec2...
single-sequencer | 🚀 INIT: Starting EVM sequencer with command: evm start ...
single-sequencer | INF Starting aggregator node component=main
single-sequencer | INF produced block component=executor height=1
single-sequencer | INF produced block component=executor height=2
```
Comment on lines +101 to +110

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add language identifier to code block.

The fenced code block showing healthy startup output should specify a language identifier.

📝 Proposed fix
-```
+```text
 single-sequencer  | 🚀 INIT: Starting EVM Sequencer initialization
 single-sequencer  | ✅ SUCCESS: Sequencer initialization completed
 single-sequencer  | ✅ SUCCESS: Exported genesis.json to /volumes/sequencer_export/genesis.json
 single-sequencer  | ✅ SUCCESS: Successfully retrieved genesis hash: 0x6aec2...
 single-sequencer  | 🚀 INIT: Starting EVM sequencer with command: evm start ...
 single-sequencer  | INF Starting aggregator node component=main
 single-sequencer  | INF produced block component=executor height=1
 single-sequencer  | INF produced block component=executor height=2
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 101-101: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/guides/deploy/local.md` around lines 101 - 110, Add a language
identifier "text" to the fenced code block that contains the sequencer startup
logs (the block starting with "single-sequencer  | 🚀 INIT: Starting EVM
Sequencer initialization" and the following lines) so the snippet renders
correctly; update the opening fence from ``` to ```text and leave the block
content unchanged.

Source: Linters/SAST tools


:::info DA submission errors
You may see errors like `DA layer submission failed: method 'blob.Submit' not found`. This is a known version mismatch between the current local-da Docker images and ev-node-evm. It is **non-fatal** — blocks are produced and the JSON-RPC is fully functional for local development. You can ignore these errors.
:::

## ✅ Step 5 — Verify {#verify}

The sequencer JSON-RPC port (`8545`) is not exposed to the host by default. To expose it for local testing, create an override file:

```bash
cat > docker-compose.override.yml << 'EOF'
services:
ev-reth-sequencer:
ports:
- "8545:8545"
EOF

docker compose -f docker-compose.da.local.yml -f docker-compose.override.yml up -d
```

Then query the chain:

```bash
curl -s -X POST http://localhost:8545 \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```

The `result` field (a hex block number) should increment with each call as new blocks are produced.

## 🔍 Useful commands {#commands}

```bash
# Check container status
docker ps

# Follow all sequencer logs
docker compose -f docker-compose.da.local.yml logs -f

# Follow just the ev-node logs (block production)
docker logs -f single-sequencer

# Follow just the reth logs
docker logs -f ev-reth-sequencer

# Stop everything
docker compose -f docker-compose.da.local.yml down
cd ../da-local && docker compose down
```

## ⚙️ Configuration {#configuration}

All configuration is via environment variables in `.env` and the `docker-compose.da.local.yml` file.

| Variable | Default | Description |
| ----------------------------------- | ------- | -------------------------------------------------------- |
| `SEQUENCER_EV_RETH_PROMETHEUS_PORT` | `9000` | Host port for ev-reth Prometheus metrics |
| `SEQUENCER_EV_NODE_PROMETHEUS_PORT` | `26660` | Host port for ev-node Prometheus metrics |
| `DA_SIGNING_ADDRESSES` | (empty) | DA signing addresses — leave empty for local-da |
| `EVM_BLOCK_TIME` | `500ms` | How often the sequencer produces blocks (set in compose) |

To change the block time or other settings, edit `docker-compose.da.local.yml` directly or add them to your `docker-compose.override.yml`.

## 🎉 Next Steps {#next-steps}

Once your local chain is running:

- [Testnet Deployment](./testnet.md) — deploy with real Celestia DA and a multi-node setup
- [Local DA Guide](../da/local-da.md) — more about the `local-da` mock DA node
- [Metrics](../metrics.md) — add Prometheus + Grafana monitoring

:::warning
This setup is for development only. The `local-da` mock does not provide real data availability guarantees. Do not use it for any production environment.
:::
1 change: 1 addition & 0 deletions docs/guides/deploy/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ When you're ready to test with real network conditions, you can deploy to testne

Choose the deployment approach that matches your current needs:

- [🏠 Local Development](./local.md) - Run everything on your machine with local-da (no external dependencies)
- [🌐 Testnet Deployment](./testnet.md) - Deploy on testnet with external DA networks

:::warning Disclaimer
Expand Down
Loading