Self-hosted

Run your own Ethernal instance

Production

At the moment, it is only possible to deploy Ethernal on https endpoint. If you deploy on a http endpoint, it will not work.

We'll deploy Ethernal remotely using Docker contexts. This assumes that the remote host has the following installed:

  • Docker

  • Node 16 & NPM

Docker context setup

On your local machine run:

export TARGET_HOST=1.1.1.1 # Replace with the IP of your remote server

docker context create ethernal --docker "host=ssh://${USER}@${TARGET_HOST}"
ssh-copy-id $TARGET_HOST
ssh-keyscan -H $TARGET_HOST >> ~/.ssh/known_hosts
eval `ssh-agent -s`
ssh-add

On the remote host, run:

sudo chown $USER:docker /var/run/docker.sock

And on your local machine:

docker context use ethernal
docker container ls # This should display an empty list

Now all your docker commands will execute remotely

Web app setup

Create a .prod.env file with the following content

APP_URL=app.ethernal.com
BASE_DOMAIN=app.ethernal.com
VUE_APP_MAIN_DOMAIN=ethernal.com

POSTGRES_USER=postgres
DB_USERNAME=postgres
POSTGRES_PASSWORD=postgres
DB_PASSWORD=postgres

REDIS_HOST=redis
REDIS_USERNAME=
REDIS_PASSWORD=
ENABLE_REDIS_TLS=
ENABLE_REDIS_TLS_SENTINEL=

ENCRYPTION_KEY=
ENCRYPTION_JWT_SECRET=

FIREBASE_SIGNER_KEY=
FIREBASE_SALT_SEPARATOR=Bw==
FIREBASE_ROUNDS=8
FIREBASE_MEM_COST=14

SERVE_FRONTEND=true

VUE_APP_PUSHER_KEY=
PUSHER_APP_ID=
PUSHER_KEY=
PUSHER_SECRET=

SENDGRID_API_KEY=
SENDGRID_SENDER=

SECRET=

BULLBOARD_USERNAME=ethernal
BULLBOARD_PASSWORD=ethernal
  • Domains/URL variables can't be IPs and need to start with app.,app.yourdomain.com will be your admin access, explorers can be any other subdomains. app & explorers need to be at the same domain level (ie you can't have app.yourdomain.com & explorer.your.domain.com).

  • Because of some legacy code that will be eventually fixed, there are two different variables for POSTGRES credentials

  • REDIS_HOST=redis assumes you are connecting to Redis using the compose file with Docker internal networking. By default, there is no username/password

  • ENCRYPTION_KEY should be a 32 characters hex string, and ENCRYPTION_JWT_SECRET should be a 63 characters hex string

  • Firebase encryption system is used because of legacy as well, but there is no actual Firebase dependencies needed. The signer key needs to be a a base64 encoded string & the salt separator can be kept as show here

  • Pusher keys are all optional, if you set them, the explorer will refresh in real time.

  • If you'd like to use the reset password feature, you'll need to setup Sendgrid env variables, which is an api key & a sender email address. That's the only mail that the app would send, so you probably don't need it. However, please note that at this time, it's the only way to update a password.

  • SECRET can be any random string. It used to protect certain endpoint for access

  • Bullboard env variables are used to protect access to /bull, this page is the admin dashboard for the queuing package BullMQ

Then create a docker-compose.prod.yml file.

Finally run:

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

Once the containers are up, you'll need to create & migrate the database:

docker exec -it web npx sequelize db:create
docker exec -it web npx sequelize db:migrate

Your app should now be accessible at https://app.ethernal.com

CLI setup

Once you've created a workspace, you'll need to sync blocks.

In order to do that, you can use the CLI. It is also recommended to use a process manager like pm2 in order to make sure it's up at all time.

npm install pm2 -g
npm install ethernal -g
ETHERNAL_API_TOKEN=xxx ETHERNAL_API_ROOT=https://app.ethernal.com pm2 start ethernal --name "explorer" -- listen -s -w "Workspace Name"

Or without pm2:

ETHERNAL_API_TOKEN=xxx ETHERNAL_API_ROOT=https://app.ethernal.com ethernal listen -s -w "Workspace Name"

You can find the API token in "Settings" > "Account".

Synchronizing past blocks

If you'd like to synchronize historical blocks, you can use the following command.

ETHERNAL_API_TOKEN=xxx ETHERNAL_API_ROOT=https://app.ethernal.com ethernal sync -t 0 -f 10000 -s -w "Workspace Name"

More info here on this command

Setup a public explorer

You can use the following script to automatically create a workspace + a public explorer linked to it.

You just need to replace constants at the beginning of the file, and run it with node.

Once it's created, start the CLI on this workspace, and blocks will start syncing.

const axios = require('axios');

const API_TOKEN = 'xxx';
const API_ROOT = 'http://app.ethernal.com';
const EXPLORER_SUBDOMAIN = 'ethernal.com '; // All explorers will be created under this subdomain
const EXPLORER_SLUG = 'test-explorer'; // This will be used for the subdomain
const WORKSPACE_NAME = 'Explorer';
const RPC_SERVER = 'http://myRpcNode.com:8545';
const NETWORK_ID = 1337;
const SECRET = 'xxx'; // Secret that you defined in your .env.prod file
const THEME = {"default":{}};
// Customize branding using below values
/*
const THEME = {
    "light": {
        "primary": "#248aff",
        "success": "#00c9a7",
        "warning": "#db9a04",
        "error": "#de4437",
        "background": "#f5f8fc"
    },
    "banner": "banner",
    "logo": "", // link to logo
    "font": "Questrial", // any google font
    "favicon": "", // link to favicon
    "links": [
        {
            "name": "Website",
            "url": "https://example.com"
        },
        {
            "icon": "mdi-twitter", // any icon on https://materialdesignicons.com/
            "name": "Twitter",
            "url": "https://example.com"
        },
        {
            "icon": "mdi-forum",
            "name": "Discord",
            "url": "https://example.com"
        }
    ]
}
*/
const HEADERS = {
    headers: {
        'Authorization': `Bearer ${API_TOKEN}`,
        'Content-Type': 'application/json'
    }
}

async function main() {
    try {
        const workspacePayload = {
            name: WORKSPACE_NAME,
            workspaceData: {
                chain: 'ethereum',
                networkId: NETWORK_ID,
                rpcServer: RPC_SERVER,
                public: true,
                tracing: 'disabled'
            }
        }

        const workspace = (await axios.post(`${API_ROOT}/api/workspaces`, { data: workspacePayload }, HEADERS)).data;

        const explorerPayload = {
            workspaceId: workspace.id,
            name: WORKSPACE_NAME,
            rpcServer: RPC_SERVER,
            theme: THEME,
            token: 'ether',
            domain: `${EXPLORER_SLUG}.${EXPLORER_SUBDOMAIN}`,
            slug: EXPLORER_SLUG,
            chainId: NETWORK_ID
        }

        const explorer = (await axios.post(`${API_ROOT}/api/explorers?secret=${SECRET}`, { data: explorerPayload }, HEADERS)).data;
        console.log(`https://${explorer.domain}`);
    } catch(error) {
        console.log(error)
        console.log(`Error: ${error.response.data}`);
    }
}

main();

Web app updates

Every new release will push a Docker image here, tagged with latest as well as the version number.

You should subscribe to releases here, and decide based on the changelog if you want to upgrade or not.

If you want to, update the docker-compose.prod.yml file by replacing the latest tag with the version you'd like to upgrade to, for web & the workers, and run the following commands:

docker context use ethernal
docker-compose pull && docker-compose -f docker-compose.prod.yml up --force-recreate -d
docker exec -it web npx sequelize db:migrate

Last updated