Skip to main content

Multi-Config Package Development

This guide will walk you through the steps to develop a multi-config Dappnode package, allowing you to create multiple package configurations from a single source. We'll use the Lodestar Generic package as a reference, which builds multiple variants for different networks like Mainnet, Holesky, and Gnosis.

Step 1: Initialize the Package

Start by running the following command to initialize your multi-variant Dappnode package:

npx @dappnode/dappnodesdk@latest init --use-variants

This will create the following directory structure:

.
├── avatar-default.png
├── dappnode_package.json
├── docker-compose.yml
├── Dockerfile
└── package_variants
├── mainnet
│ ├── dappnode_package.json
│ └── docker-compose.yml
└── testnet
├── dappnode_package.json
└── docker-compose.yml

Step 2: Customize your package

Create a directory for each variant

Each variant of your package will have its own configuration files under package_variants. For example, if you have mainnet and testnet variants, you will find the following:

package_variants/
├── mainnet/
│ ├── dappnode_package.json
│ └── docker-compose.yml
└── testnet/
├── dappnode_package.json
└── docker-compose.yml

The contents within each variant directory include the fields that differ from one variant to another. When building a specific variant, such as testnet, the data from these variant-specific files is merged with the root-level dappnode_package.json and docker-compose.yml. This ensures that only the necessary variant-specific changes are applied, while the common configuration remains consistent across all variants.

You can add more variants as needed by creating additional directories and files following this structure.

Customize the Avatar

Replace the default avatar (avatar-default.png) with a square .png image that represents your package (recommended dimensions: width between 200px and 300px). This image will appear in the Dappnode UI, so it's important to choose one that aligns with your package's branding. The avatar is common for each of the variants.

Key Changes in docker-compose.yml and dappnode_package.json

In the root-level docker-compose.yml and dappnode_package.json, specify the general configuration that applies across all variants.

Each variant in package_variants will have its own docker-compose.yml and dappnode_package.json, tailored to the network or environment it is intended for.

For example, package_variants/mainnet/docker-compose.yml could define services using NETWORK=mainnet, while package_variants/testnet/docker-compose.yml would use NETWORK=testnet.

Example of a variant-specific dappnode_package.json:

{
"name": "test-mainnet.public.dappnode.eth",
"version": "0.1.0",
"type": "service"
}

And the corresponding docker-compose.yml:

version: "3.5"
services:
test:
build:
args:
NETWORK: mainnet

Simplified Dockerfile

Here’s an example of the Dockerfile used for the Lodestar Generic package. This Dockerfile is responsible for configuring the Lodestar Ethereum client and handling multiple variants.

ARG UPSTREAM_VERSION

FROM chainsafe/lodestar:${UPSTREAM_VERSION}

ARG NETWORK
ARG STAKER_SCRIPTS_VERSION

# Additional envs

ENV STAKER_SCRIPTS_URL=https://github.com/dappnode/staker-package-scripts/releases/download/${STAKER_SCRIPTS_VERSION}

COPY entrypoint.sh /usr/local/bin/entrypoint.sh

# These scripts provide useful tools for the entrypoint
ADD ${STAKER_SCRIPTS_URL}/consensus_tools.sh /etc/profile.d/
RUN chmod +rx /etc/profile.d/consensus_tools.sh

# Additional commands or package installation

# This environment variable sets the variant (e.g., mainnet, testnet)
ENV NETWORK=${NETWORK}

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

Simplified entrypoint

The entrypoint.sh script for the Lodestar client manages different network setups (e.g., Holesky, Gnosis, Mainnet) and configures runtime flags accordingly. Here’s a simplified version:

#!/bin/sh

SUPPORTED_NETWORKS="gnosis holesky mainnet"
MEVBOOST_FLAG_KEY="--builder"
CLIENT="lodestar"

# Load the tools scripts
. /etc/profile

# Get the necessary environment variables for the beacon node
ENGINE_URL="http://execution.${NETWORK}.staker.dappnode:8551"
VALID_FEE_RECIPIENT=$(get_valid_fee_recipient "${FEE_RECIPIENT}")
MEVBOOST_FLAG=$(get_mevboost_flag "${NETWORK}" "${MEVBOOST_FLAG_KEY}")

JWT_SECRET=$(get_jwt_secret_by_network "${NETWORK}")
echo "${JWT_SECRET}" >"${JWT_FILE_PATH}"

# Start the beacon node with the appropriate flags
echo "[INFO - entrypoint] Running beacon node"

FLAGS="beacon \
--network=${NETWORK} \
--suggestedFeeRecipient=${VALID_FEE_RECIPIENT} \
--jwt-secret=${JWT_FILE_PATH} \
--execution.urls=${ENGINE_URL} \
# ... Additional flags here
--logFileDailyRotate=5 $MEVBOOST_FLAG $EXTRA_OPTS"

# Run the Lodestar client with the specified flags
exec ${CLIENT_BIN} $FLAGS

Prometheus and Grafana

In multi-variant packages, you can use a common Grafana dashboard located at the root level, while each variant can have its own Prometheus targets. These are stored in variant-specific directories like package_variants/gnosis/prometheus-targets.json.

By following this guide, you’ll be able to create multi-variant packages that support different networks or configurations within a single source code base.

Step 3: Build the packages

Once your variants are configured, build any of them using the following command:

npx @dappnode/dappnodesdk@latest build --variants=<comma-separated-list-of-variants>

If you want to build all variants at the same time, you can use:

npx @dappnode/dappnodesdk@latest build --all-variants

These commands will package your services, making them ready for installation on Dappnode machines.

Step 4: Publish the Packages

Once you have built your variants, you can choose to publish them. While publishing is optional if you only want to use the package locally, it is required if you want your package to be available in the public Dappstore for other users to discover and install.

The publish command allows you to specify which variants you want to publish, using the same --variants flag as the build command. You can publish one or more variants as follows:

npx @dappnode/dappnodesdk@latest publish --type=<patch/minor/major> --variants=<comma-separated-list-of-variants> --eth-provider=<your ETH RPC> --content-provider=<your IPFS API> --developer-address=<the address to sign>

If you want to generate all variants publish links at once, you can use the following command:

npx @dappnode/dappnodesdk@latest publish --type=patch --all-variants --eth-provider=https://your-eth-node --content-provider=https://your-ipfs-api --developer-address=0xYourAddress

After running the publish command, you will receive a link to sign the transaction to complete the publishing process.

Automating Publication with GitHub Actions

If your package source code is hosted on GitHub, you can automate the publishing process by setting up GitHub Actions. Use the workflows here to integrate publishing workflows, ensuring your package builds and publishes automatically.

By following these steps, you'll be able to develop, build, and publish multi-variant packages on Dappnode, making it easier to support multiple configurations from a single codebase.