Local 4-node development network using Docker Compose. Run a full mesh network locally instead of deploying to 4 VPSes.
- Docker & Docker Compose
- Bun (for identity generation)
- Node dependencies installed (
bun installin parent directory)
cd devnet
# 1. Run setup (generates identities + peerlist)
./scripts/setup.sh
# 2. Start the devnet
docker compose up --build┌─────────────────────────────────────────────────────────────┐
│ Docker Network │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ node-1 │──│ node-2 │──│ node-3 │──│ node-4 │ │
│ │ :53551 │ │ :53552 │ │ :53553 │ │ :53554 │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │ │
│ └─────────────┴──────┬──────┴─────────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ PostgreSQL │ │
│ │ (4 DBs) │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
- PostgreSQL: Single container with 4 databases (node1_db, node2_db, node3_db, node4_db)
- Nodes: 4 containers running the Demos node software
- Networking: Full mesh via Docker DNS (
node-1,node-2, etc.) - Identity: Each node has its own cryptographic identity (BIP39 mnemonic)
Copy .env.example to .env to customize:
cp .env.example .env| Variable | Default | Description |
|---|---|---|
NODE1_PORT |
53551 | HTTP RPC port for node 1 |
NODE2_PORT |
53552 | HTTP RPC port for node 2 |
NODE3_PORT |
53553 | HTTP RPC port for node 3 |
NODE4_PORT |
53554 | HTTP RPC port for node 4 |
NODE1_OMNI_PORT |
53561 | OmniProtocol P2P port for node 1 |
POSTGRES_USER |
demosuser | Postgres username |
POSTGRES_PASSWORD |
demospass | Postgres password |
PERSISTENT |
0 | Set to 1 for persistent volumes |
docker compose up --builddocker compose up --build -d
docker compose logs -f # follow logsdocker compose downdocker compose down -vdocker compose up --buildOnce running, nodes are accessible at:
| Node | HTTP RPC | OmniProtocol |
|---|---|---|
| node-1 | http://localhost:53551 | localhost:53561 |
| node-2 | http://localhost:53552 | localhost:53562 |
| node-3 | http://localhost:53553 | localhost:53563 |
| node-4 | http://localhost:53554 | localhost:53564 |
By default, the devnet runs in ephemeral mode - all data is lost when containers stop.
For persistent development:
# In .env
PERSISTENT=1This creates a postgres-data volume that survives restarts.
To generate new node identities:
./scripts/generate-identities.sh
./scripts/generate-peerlist.sh
docker compose down -v # clear old state
docker compose up --build./scripts/logs.sh # All services
./scripts/logs.sh nodes # All 4 nodes
./scripts/logs.sh node-1 # Specific node
./scripts/logs.sh postgres # Database only./scripts/attach.sh node-1 # Interactive shell in node-1
./scripts/attach.sh postgres # psql client for database./scripts/watch-all.shOpens a tmux session with 4 panes, one per node:
┌─────────────┬─────────────┐
│ node-1 │ node-2 │
├─────────────┼─────────────┤
│ node-3 │ node-4 │
└─────────────┴─────────────┘
Ctrl+BthenDto detachtmux attach -t demos-devnetto reattach
- Ensure
demos_peerlist.jsonwas generated after identities - Check that Docker networking is working:
docker network inspect demos-devnet_demos-network
- Wait for PostgreSQL health check to pass
- Check logs:
docker compose logs postgres
- Change ports in
.envfile - Or stop conflicting services
devnet/
├── docker compose.yml # Main orchestration
├── Dockerfile # Node container image
├── entrypoint.sh # Container startup script
├── .env.example # Configuration template
├── .env # Your local config (gitignored)
├── demos_peerlist.json # Generated peerlist (gitignored)
├── postgres-init/
│ └── init-databases.sql # Creates 4 databases
├── scripts/
│ ├── setup.sh # One-time setup
│ ├── generate-identities.sh
│ ├── generate-identity-helper.ts
│ ├── generate-peerlist.sh
│ ├── logs.sh # View container logs
│ ├── attach.sh # Attach to container
│ └── watch-all.sh # Tmux 4-pane view
└── identities/ # Generated identities (gitignored)
├── node1.identity
├── node1.pubkey
└── ...