Skip to content
canopy-networkPublic

About

Docker compose configuration for running a canopy node

Resources

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

Canopy node

Docker-based setup for running a canopy validator (or full node).

Prerequisites

  • A server with docker installed
  • Validators only: a domain name with a DNS *.<DOMAIN> wildcard A record pointing at the server's IP.

1. Node setup

Copy docker-compose.yml and the config/ directory to the server on the same base directory, then:

Note

You may also clone the whole repository with git clone to skip copying each file individually.

# generate the validator key by writing config/keystore.json + config/validator_key.json
docker compose run --rm --no-deps -it keygen

# validators only: modify the "externalAddress" field in config/config.json to point to your domain
#   config/config.json → "externalAddress": "tcp://<your-domain>"

# start the node
docker compose up -d

If you also copy the Makefile, the equivalent shortcuts are make init-keys and make up (see Operations).

Migrating an existing validator

Skip keygen. Instead, copy your existing config.json, keystore.json, and validator_key.json into config/ before running docker compose up -d.

You may also need to update config.json so rpcURL is "/rpc" and adminRPCUrl is "/adminrpc".

2. Monitoring & public access (optional)

docker-compose.monitoring.yml runs Caddy (reverse proxy + auto-HTTPS) and the prometheus/grafana/loki/alloy stack. Caddy is also what exposes the wallet, explorer, and RPC endpoints for public access. The node container only publishes its P2P port (9001) on its own, so skip this step only if you don't need those endpoints reachable from outside the box.

Copy docker-compose.monitoring.yml to the same directory as the previous compose file, along with the monitoring folder, then:

cp .env.example .env
# edit .env: set DOMAIN=<your-domain> (defaults to localhost),
# change AUTH_PASSWORDHASH for production (docker run --rm -it caddy:2.11-alpine caddy hash-password 
# or make hash-password), if the docker command is used, the $ must be escaped as $$ and set GF_ADMIN_PASSWORD

docker compose -f docker-compose.monitoring.yml up -d

Local development: leave DOMAIN=localhost, you can still access through <SERVICE>.localhost. Production: point the *.<DOMAIN> wildcard at the box and certificates are issued automatically via Let's Encrypt.

URLs

Service URL Auth
Wallet https://wallet.<DOMAIN>/ basic-auth
Explorer https://explorer.<DOMAIN>/ open
RPC https://rpc.<DOMAIN>/ open
Admin RPC https://adminrpc.<DOMAIN>/ basic-auth
Grafana https://monitoring.<DOMAIN>/ grafana login
Loki https://loki.<DOMAIN>/ basic-auth

3. Snapshot (optional)

Sync from a mainnet snapshot instead of genesis. Copy scripts/snapshot.sh to the server, then:

SNAPSHOT_URL=<url> ./scripts/snapshot.sh

Run plainly (make snapshot), it restores the root chain (SNAPSHOT_URL -> data/canopy) and every added nested chain (docker-compose.chain-*.yml) from their snapshots: it stops the services, downloads all snapshots concurrently, wipes & extracts each chain's db dir, then starts them all together. SNAPSHOT_URL (root chain) can come from .env; nested-chain snapshots come from https://public-snapshots.app.canopynetwork.org/chain/<id>. Only each chain's db dir is wiped, so the shared keystore.json/validator_key.json are preserved.

You can also run make snapshot-up to additionally bring up monitoring after the snapshots are applied (see Operations).

Operations

The Makefile wraps both compose files together (docker compose -f docker-compose.yml -f docker-compose.monitoring.yml), so its targets act on node + monitoring as one stack:

make help       # all targets
make pull       # pull the latest canopynetwork/canopy image
make gen-key    # first boot only: generate the validator key
make up         # start everything
make logs       # follow node logs
make status     # show container status
make restart    # apply config/config.json edits
make snapshot   # restore root chain + all added nested chains from snapshots (concurrent) and start them
make snapshot-up   # same as snapshot, then also bring up monitoring
make add-nested-chain CHAIN_ID=29            # assemble a nested chain's files (see below)
make add-nested-chain-snapshot CHAIN_ID=29   # assemble + restore its snapshot (start with 'make up')
make down       # stop everything

make up, make node-up, make down, and make status automatically include every docker-compose.chain-*.yml that make add-nested-chain has generated, so the root chain and all added nested chains start/stop/report together.

Nested chains

A node can run extra ("nested") chains alongside the root chain. Each nested chain runs as its own container that connects to the root chain already running in this stack. Two targets, both backed by scripts/add-nested-chain.sh:

make add-nested-chain           CHAIN_ID=29   # assemble files only (does NOT start)
make add-nested-chain-snapshot  CHAIN_ID=29   # assemble (if needed) + restore snapshot (does NOT start)

Assembling the files (add-nested-chain) only prepares the chain; it never starts a container:

  1. Fetches the chain's genesis.json and config.json from the Canopy chain-data API (https://api.app.canopynetwork.org/api/v1).
  2. Fetches the chain's plugin files if it has any (a 404 means it has none and is skipped silently) and writes them where the chain expects them. If the plugin needs env vars, they are documented in .env.example and you are prompted to add them to your .env.
  3. Writes the fetched config.json verbatim, including its rootChain entry as served by the launchpad (it is not rewritten to point at this node).
  4. Reuses this node's config/keystore.json and config/validator_key.json verbatim (the node identity is identical across every chain). Run make gen-key first if you don't have them yet.
  5. Writes everything under data/chain-<id>/ and generates docker-compose.chain-<id>.yml, using the Docker image that matches the chain's plugin language (canopynetwork/canopy-plugin-<lang>:latest, defaulting to go).

Starting from genesis — once the files are assembled, start the whole stack (root chain + every added nested chain) with make up (or make node-up for no monitoring). These targets pick up every docker-compose.chain-*.yml automatically.

Restoring a snapshot (add-nested-chain-snapshot) assembles the files if needed, then reuses scripts/snapshot.sh to restore the chain's snapshot from https://public-snapshots.app.canopynetwork.org/chain/<id> into data/chain-<id>/canopy. It does not start the chain — like add-nested-chain, it only prepares the data; start it (and every other added chain) afterwards with make up. The snapshot only ever touches that chain's db dir, so the shared keystore.json/validator_key.json are never overwritten and the root chain and other nested chains are left untouched. (To restore and start everything at once instead, use make snapshot.)

Both are idempotent: if data/chain-<id> already exists the files are reused, not re-fetched. To recreate a chain from scratch, remove data/chain-<id> and docker-compose.chain-<id>.yml first.

Upgrading canopy: make pull && make up (or docker compose pull node && docker compose up -d). The node also self-updates in place via canopy's auto-update coordinator when autoUpdate is enabled in config (enabled by default).

Repo layout

docker-compose.yml            # node (+ keygen, run once via `docker compose run keygen`)
docker-compose.monitoring.yml # caddy + monitoring stack (configs inlined via `configs:`) 
config/                       # node config + genesis (bind-mounted into the node)
data/                         # runtime state (peers, chain db), gitignored
scripts/                      # snapshot.sh, add-nested-chain.sh

About

Docker compose configuration for running a canopy node

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages