---
# System prepended metadata

title: Deploying Full zkEVM

---


- [zkEVM](#zkevm)
  - [Requisites](#requisites)
    - [Variables](#variables)
    - [Resources](#resources)
  - [Dependences](#dependences)
  - [Mainnet files](#mainnet-files)
  - [Deploy contracts](#deploy-contracts)
    - [Create wallets](#create-wallets)
    - [Prepare configuration](#prepare-configuration)
    - [Deploy contracts](#deploy-contracts-1)
  - [zkNode deployment](#zknode-deployment)
    - [Aprove Matic token for sequencer](#aprove-matic-token-for-sequencer)
    - [Configure genesis](#configure-genesis)
    - [Change config](#change-config)
    - [Add wallets](#add-wallets)
    - [Edit DBs](#edit-dbs)
    - [Configure prover](#configure-prover)
    - [Configure services](#configure-services)
    - [Start services](#start-services)
      - [Open Ports](#open-ports)
    - [Activate forced transactions](#activate-forced-transactions)
    - [Provide L1 Ether to the bridge](#provide-l1-ether-to-the-bridge)
    - [Claim our L2 zkEther](#claim-our-l2-zkether)
- [\[Extra\] L1 Goerli fullnode](#extra-l1-goerli-fullnode)
  - [Requirements](#requirements)
    - [Resources](#resources-1)
    - [Software](#software)
    - [L1 account](#l1-account)
  - [Preparation](#preparation)
  - [Deployment](#deployment)
  - [Validation](#validation)
  - [\[OPTIONAL\] HTTPS Reverse Proxy](#optional-https-reverse-proxy)

# zkEVM

## Requisites

### Variables

* INFURA_PROJECT_ID.
* ETHERSCAN_API_KEY.
* IP public address.
* L1 Goerli node RPC.
* Goerli address with 15 GöETH.

### Resources

The zkEVM is highly resource consuming, for that reason, we propose two different recommended resources depending if you want to have a full prover or not.

The required resources for a mock prover are:
* 32 vcpu
* 256gb of memory

The required resources for a full prover are:
* 96 vcpu
* 768gb of memory

An example of suitable virtual machines in public cloud would be:

* AWS: https://aws.amazon.com/ec2/instance-types/r6a/
  * r6a.8xlarge for mock prover.
  * r6a.24xlarge for full prover.

Regarding the disk usage, the initial space required is minimal (<1gb) but as the network runs, you should monitor the avaiable
space since the system is always appending data.

----

## Dependences

First, install the following dependences:
```bash
# APT dependences
sudo apt update -y
sudo apt install -y tmux git curl unzip jq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Install docker
sudo usermod -aG docker $USER
newgrp docker && newgrp $USER

# Nodejs (NVM)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash

source ~/.bashrc
nvm install 16

node -v
# v16.20.0

# Install go
wget https://go.dev/dl/go1.20.4.linux-amd64.tar.gz
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.20.4.linux-amd64.tar.gz
rm -rf go1.20.4.linux-amd64.tar.gz

echo '
export ZKEVM_NET=mainnet
export ZKEVM_DIR=~/zkevm/zkevm-node
export ZKEVM_CONFIG_DIR=~/zkevm/zkevm-config

[ -d "/usr/local/go/bin" ] && PATH="/usr/local/go/bin:$PATH"
' >> ~/.profile
source .profile

go version
# go version go1.20.4 linux/amd64

cd zkevm
```

## Mainnet files

It is recommended to exec the wget on a tmux or byobu because the download is over 70GB. So if there's any network problem the download could continue from previous fails and not from scra>

```bash
tmux
$ wget https://de012a78750e59b808d922b39535e862.s3.eu-west-1.amazonaws.com/v1.1.0-rc.1-fork.4.tgz
ctrl + d
...

# File is over 70GB. Once finished, extract it
tar xzvf v1.1.0-rc.1-fork.4.tgz
```

## Deploy contracts

Get the contracts from Github.
```bash
git clone https://github.com/0xPolygonHermez/zkevm-contracts.git

cd zkevm-contracts
npm i
```

### Create wallets

Create wallets.js file:
```js
const ethers = require("ethers");

async function main() {
    const arrayNames = ["## Deployment Address", "\n\n## Trusted sequencer", "\n\n## Trusted aggregator"];
    for (let i = 0; i < arrayNames.length; i++) {
        const wallet = ethers.Wallet.createRandom();
        console.log(arrayNames[i]);
        console.log(`Address: ${wallet.address}`);
        console.log(`PrvKey: ${wallet._signingKey().privateKey}`);
        console.log(`mnemonic: "${wallet._mnemonic().phrase}"`);

        const keystoreJson = await wallet.encrypt("password");
        console.log(`keystore: ${keystoreJson}`);
    }
}
main().catch((e) => {
    console.error(e);
    process.exit(1);
});
```

Generate wallerts
```bash
node wallets.js | tee wallets.txt
```

### Prepare configuration

Edit environment variables:
```bash
cp .env.example .env

nano .env
[
MNEMONIC="..." #(from wallets.txt Deployment Address mnemonic)
INFURA_PROJECT_ID="..." #(your infura project ID)
ETHERSCAN_API_KEY="..." #(your etherscan API key)
]
```

Edit deploy_parameters.json
```bash
cd deployment
cp deploy_parameters.json.example deploy_parameters.json
nano deploy_parameters.json
```

```json
{
 "realVerifier": true,
 "trustedSequencerURL": "http://X.X.X.X:8545", // your public IP
 "networkName": "zkevm",
 "version": "0.0.1",
 "trustedSequencer": "", // from wallets.txt Trusted sequencer
 "chainID": 42069, // put your prefered id
 "trustedAggregator": "", // from wallets.txt aggregator sequencer
 "trustedAggregatorTimeout": 604799,
 "pendingStateTimeout": 604799,
 "forkID": 4,
 "admin": "", // from wallets.txt Deployment Address
 "zkEVMOwner": "", // from wallets.txt Deployment Address
 "timelockAddress": "", // from wallets.txt Deployment Address
 "minDelayTimelock": 1,
 "salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
 "initialZkEVMDeployerOwner": "", // from wallets.txt Deployment Address
 "maticTokenAddress": "", // put an existing contract address or leave it empty auto-deploy a new contract
 "zkEVMDeployerAddress": "", // put an existing contract address or leave it empty auto-deploy a new contract
 "deployerPvtKey": "",
 "maxFeePerGas": "",
 "maxPriorityFeePerGas": "",
 "multiplierGas": ""
}
```

At this point, you need to send the (15 GöETH) to Deployment address from wallets.txt.

Then, you have to adjust the gasPrice accouding to the network status. In case of Goerli, you could check it with the following command adding your ETHERSCAN_API_KEY:

```bash
# Put your Etherscan api key
ETHERSCAN_API_KEY="" echo "$(($(printf "%d\n" $(curl -s "https://api-goerli.etherscan.io/api?module=proxy&action=eth_gasPrice&apikey=$ETHERSCAN_API_KEY" | jq -r .result))/1000000000)) Gwei"
```

Edit ~/zkevm/zkevm-contracts/deployment/helpers/deployment-helpers.js to adjust the gasPrice according to network status. It is recommended to add 50 Gwei to the current gasPrice to be sure tran>
```js
const gasPriceKeylessDeployment = '100';
```

### Deploy contracts

```bash
npm run deploy:deployer:ZkEVM:goerli
npm run verify:deployer:ZkEVM:goerli
npm run deploy:testnet:ZkEVM:goerli
npm run verify:ZkEVM:goerli
```
The previous scripts will auto-deploy the matic token contract and the zkEVMDeployer contract if required.

You will see in the logs the verification of each smart contract deployed, but you can check it on etherscan.
* https://goerli.etherscan.io/address/0x -> Put the Deployer address from wallets.txt


## zkNode deployment

```bash
$ mkdir -p ~/zkevm/data/{statedb,pooldb} ~/zkevm/zkevm-config ~/zkevm/zkevm-node

curl -L https://github.com/0xPolygonHermez/zkevm-node/releases/latest/download/$ZKEVM_NET.zip > $ZKEVM_NET.zip && unzip -o $ZKEVM_NET.zip -d $ZKEVM_DIR && rm $ZKEVM_NET.zip

cp $ZKEVM_DIR/$ZKEVM_NET/example.env $ZKEVM_CONFIG_DIR/.env
nano $ZKEVM_CONFIG_DIR/.env
[
  # URL of a JSON RPC for Ethereum mainnet
  ZKEVM_NODE_ETHERMAN_URL = "http://localhost:8845" # (set a valid goerli RPC node)
  # PATH WHERE THE STATEDB POSTGRES CONTAINER WILL STORE PERSISTENT DATA
  ZKEVM_NODE_STATEDB_DATA_DIR = "~/zkevm/data/statedb"
  # PATH WHERE THE POOLDB POSTGRES CONTAINER WILL STORE PERSISTENT DATA
  ZKEVM_NODE_POOLDB_DATA_DIR = "~/zkevm/data/pooldb"
  # OPTIONAL, UNCOMENT IF YOU WANT TO DO ADVANCED CONFIG
  # ZKEVM_ADVANCED_CONFIG_DIR = "/should/be/same/path/as/ZKEVM_CONFIG_DIR"
]
```

### Aprove Matic token for sequencer

```bash
npx hardhat console --network goerli
```

```js
const provider = ethers.getDefaultProvider("") // set goerli RPC node
const privateKey = '' // From wallet.txt Trusted sequencer
const wallet = new ethers.Wallet(privateKey, provider);

const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock', provider);
maticTokenContract = maticTokenFactory.attach("") // From ~/zkevm/zkevm-contract/deployments/goerly_***/deploy_output.json maticTokenAddress
maticTokenContractWallet = maticTokenContract.connect(wallet)
await maticTokenContractWallet.approve("", ethers.utils.parseEther("100.0")) // From ~/zkevm/zkevm-contract/deployments/goerly_***/deploy_output.json polygonZkEVMAddress
```

### Configure genesis

```bash
cp ~/zkevm/zkevm-contracts/deplyoments/goerli_***/genesis.json ~/zkevm/zkevm-node/mainnet/config/environments/public/public.genesis.config.json

nano ~/zkevm/zkevm-node/mainnet/config/environments/public/public.genesis.config.json
```

Edit file changing the following parameters from  ~/zkevm/zkevm-contracts/deployments/goerli_***/deploy_output.json:
```js
{
  "l1Config" : {
                "chainId": 5,
                "polygonZkEVMAddress": "",
                "maticTokenAddress": "",
                "polygonZkEVMGlobalExitRootAddress": ""  // deploymentBlockNumber from ~/zkevm/zkevm-contracts/deployments/goerli_***/deploy_output.json
  },
 "genesisBlockNumber": 9050589,
 "root": "",
 "genesis":
}
```

Just take into account that  polygonZkEVMGlobalExitRootAddress is called deploymentBlockNumber in deploy_output.json.

### Change config 

Edit ~/zkevm/zkevm-node/mainnet/config/environments/public/public.node.config.toml
```yml
IsTrustedSequencer = true
[Log]
Environment = "development"
Level = "info"
Outputs = ["stderr"]

[StateDB]
User = "state_user"
Password = "state_password"
Name = "state_db"
Host = "zkevm-state-db"
Port = "5432"
EnableLog = false
MaxConns = 200

[Pool]
FreeClaimGasLimit = 1500000
MaxTxBytesSize=30132
MaxTxDataBytesSize=30000
DefaultMinGasPriceAllowed = 1000000000
MinAllowedGasPriceInterval = "5m"
PollMinAllowedGasPriceInterval = "15s"
        [Pool.DB]
        User = "pool_user"
        Password = "pool_password"
        Name = "pool_db"
        Host = "zkevm-pool-db"
        Port = "5432"
        EnableLog = false
        MaxConns = 200

[Etherman]
URL = "" # put a valid Goerli node
MultiGasProvider = false
L1URL = "" # put a valid Goerli node
L2URLs = ["http://zkevm-rpc:8545"]
        [Etherman.Etherscan]
                ApiKey = "" # Etherscan api key
[RPC]
Host = "0.0.0.0"
Port = 8545
ReadTimeoutInSec = 60
WriteTimeoutInSec = 60
MaxRequestsPerIPAndSecond = 5000
SequencerNodeURI = ""
BroadcastURI = "http://3.144.195.147:61090"
DefaultSenderAddress = "0x1111111111111111111111111111111111111111"
EnableL2SuggestedGasPricePolling = true
        [RPC.WebSockets]
                Enabled = true
                Port = 8546

[Synchronizer]
SyncInterval = "2s"
SyncChunkSize = 100
trustedSequencerURL = ""

[MTClient]
URI = "zkevm-executor:50061"

[Executor]
URI = "zkevm-executor:50071"

[Metrics]
Host = "0.0.0.0"
Port = 9091
Enabled = true
ProfilingHost = "0.0.0.0"
ProfilingPort = 6060
ProfilingEnabled = false

[Sequencer]
WaitPeriodPoolIsEmpty = "1s"
WaitPeriodSendSequence = "15s"
LastBatchVirtualizationTimeMaxWaitPeriod = "10s"
BlocksAmountForTxsToBeDeleted = 100
FrequencyToCheckTxsForDelete = "12h"
MaxTxsPerBatch = 150
MaxBatchBytesSize = 129848
MaxCumulativeGasUsed = 30000000
MaxKeccakHashes = 468
MaxPoseidonHashes = 279620
MaxPoseidonPaddings = 149796
MaxMemAligns = 262144
MaxArithmetics = 262144
MaxBinaries = 262144
MaxSteps = 8388608
WeightBatchBytesSize = 1
WeightCumulativeGasUsed = 1
WeightKeccakHashes = 1
WeightPoseidonHashes = 1
WeightPoseidonPaddings = 1
WeightMemAligns = 1
WeightArithmetics = 1
WeightBinaries = 1
WeightSteps = 1
TxLifetimeCheckTimeout = "10m"
MaxTxLifetime = "3h"
MaxTxSizeForL1 = 131072
        [Sequencer.Finalizer]
                GERDeadlineTimeoutInSec = "2s"
                ForcedBatchDeadlineTimeoutInSec = "60s"
                SendingToL1DeadlineTimeoutInSec = "20s"
                SleepDurationInMs = "100ms"
                ResourcePercentageToCloseBatch = 10
                GERFinalityNumberOfBlocks = 0
                ClosingSignalsManagerWaitForCheckingL1Timeout = "10s"
                ClosingSignalsManagerWaitForCheckingGER = "10s"
                ClosingSignalsManagerWaitForCheckingForcedBatches = "10s"
                ForcedBatchesFinalityNumberOfBlocks = 0
                TimestampResolution = "15s"
        [Sequencer.DBManager]
                PoolRetrievalInterval = "500ms"
        [Sequencer.Worker]
                ResourceCostMultiplier = 1000

[SequenceSender]
WaitPeriodSendSequence = "5s"
LastBatchVirtualizationTimeMaxWaitPeriod = "5s"
MaxTxSizeForL1 = 131072
SenderAddress = "0x225c96B7dB4223f0244DcfC833e0bB9f40a948E4"
PrivateKeys = [{Path = "/pk/sequencer.keystore", Password = "password"}]

[Aggregator]
Host = "0.0.0.0"
Port = 50081
ForkId = 4
RetryTime = "5s"
VerifyProofInterval = "30s"
TxProfitabilityCheckerType = "acceptall"
TxProfitabilityMinReward = "1.1"
ProofStatePollingInterval = "5s"
SenderAddress = "" # trustedAggregator from deploy_output.json
CleanupLockedProofsInterval = "2m"
GeneratingProofCleanupThreshold = "10m"

[EthTxManager]
ForcedGas = 0
PrivateKeys = [
        {Path = "/pk/sequencer.keystore", Password = "password"},
        {Path = "/pk/aggregator.keystore", Password = "password"}
]

[Database]
Database = "postgres"
User = "test_user"
Password = "test_password"
Name = "test_db"
Host = "zkevm-bridge-db"
Port = "5435"
MaxConns = 20

[BridgeController]
Store = "postgres"
Height = 32

[BridgeServer]
GRPCPort = "9090"
HTTPPort = "8080"

[NetworkConfig]
GenBlockNumber = 000000 # deploymentBlockNumber from deploy_output.json
PolygonZkEVMAddress = "" # polygonZkEVMAddress from deploy_output.json
PolygonBridgeAddress = "" # PolygonZkEVMBridge from genesis.json
PolygonZkEVMGlobalExitRootAddress = "" # polygonZkEVMGlobalExitRootAddress from deploy_output.json
MaticTokenAddress = "" # maticTokenAddress from deploy_output.json
L2PolygonBridgeAddresses = [""] # PolygonZkEVMBridge from genesis.json
L1ChainID = 5 # Goerli chainID

[L2GasPriceSuggester]
Type = "default"
DefaultGasPriceWei = 100000000

[ClaimTxManager]
FrequencyToMonitorTxs = "1s"
PrivateKey = {Path = "/pk/sequencer.keystore", Password = "password"}
Enabled = true
```

### Add wallets

```bash
nano zkevm-config/sequencer.keystore (from wallets.txt)
nano zkevm-config/aggregator.keystore (from wallets.txt)
```

### Edit DBs

Edit ~/zkevm/zkevm-node/mainnet/db/scripts/init_prover_db.sql to match this:
```sql
CREATE DATABASE prover_db;
\connect prover_db;

CREATE SCHEMA state;

CREATE TABLE state.nodes (hash BYTEA PRIMARY KEY, data BYTEA NOT NULL);
CREATE TABLE state.program (hash BYTEA PRIMARY KEY, data BYTEA NOT NULL);

CREATE USER prover_user with password 'prover_pass';
GRANT CONNECT ON DATABASE prover_db TO prover_user;
ALTER USER prover_user SET SEARCH_PATH=state;
GRANT ALL PRIVILEGES ON SCHEMA state TO prover_user;
GRANT ALL PRIVILEGES ON TABLE state.nodes TO prover_user;
GRANT ALL PRIVILEGES ON TABLE state.program TO prover_user;
```

### Configure prover

Create ~/zkevm/config.json file and put your PUBLIC IP on the paramter *aggregatorClientHost*:

```json
{
    "runExecutorServer": false,
    "runExecutorClient": false,
    "runExecutorClientMultithread": false,

    "runStateDBServer": false,
    "runStateDBTest": false,

    "runAggregatorServer": false,
    "runAggregatorClient": true,
    "proverName": "static_prover",

    "runFileGenBatchProof": false,
    "runFileGenAggregatedProof": false,
    "runFileGenFinalProof": false,
    "runFileProcessBatch": false,
    "runFileProcessBatchMultithread": false,
    "runFileExecutor": false,

    "runKeccakScriptGenerator": false,
    "runKeccakTest": false,
    "runStorageSMTest": false,
    "runBinarySMTest": false,
    "runMemAlignSMTest": false,
    "runSHA256Test": false,
    "runBlakeTest": false,
    "executeInParallel": true,
    "useMainExecGenerated": true,
    "useProcessBatchCache": true,
    "saveRequestToFile": false,
    "saveInputToFile": true,
    "saveDbReadsToFile": true,
    "saveDbReadsToFileOnChange": false,
    "saveOutputToFile": true,
    "saveFilesInSubfolders": false,
    "saveProofToFile": true,
    "saveResponseToFile": false,
    "loadDBToMemCache": true,
    "loadDBToMemCacheInParallel": false,
    "dbMTCacheSize": 16384,
    "dbProgramCacheSize": 16384,
    "dbMultiWrite": true,
    "dbFlushInParallel": true,

    "opcodeTracer": false,
    "logRemoteDbReads": false,
    "logExecutorServerResponses": false,

    "executorServerPort": 50071,
    "executorROMLineTraces": false,
    "executorClientPort": 50071,
    "executorClientHost": "127.0.0.1",

    "stateDBServerPort": 5432,
    "stateDBURL": "local",

    "aggregatorServerPort": 50081,
    "aggregatorClientPort": 50081,
    "aggregatorClientHost": "", // YOUR-PUBLIC-IP-ADDRESS
    "aggregatorClientMockTimeout": 10000000,

    "mapConstPolsFile": false,
    "mapConstantsTreeFile": false,

    "inputFile": "testvectors/aggregatedProof/recursive1.zkin.proof_0.json",
    "inputFile2": "testvectors/aggregatedProof/recursive1.zkin.proof_1.json",

    "outputPath": "/output/",
    "configPath": "/mnt/prover/config/",

    "zkevmCmPols_
    
    
    
    
    
    
    
    
    
    ": "/mnt/prover/runtime/zkevm.commit",
    "c12aCmPols": "runtime/c12a.commit",
    "recursive1CmPols_disabled": "runtime/recursive1.commit",
    "recursive2CmPols_disabled": "runtime/recursive2.commit",
    "recursivefCmPols_disabled": "runtime/recursivef.commit",
    "finalCmPols_disabled": "runtime/final.commit",

    "publicsOutput": "public.json",
    "proofFile": "proof.json",

    "databaseURL": "postgresql://prover_user:prover_pass@zkevm-state-db:5432/prover_db",
    "databaseURL_disabled": "local",
    "dbNodesTableName": "state.nodes",
    "dbProgramTableName": "state.program",
    "dbConnectionsPool": true,
    "cleanerPollingPeriod": 600,
    "requestsPersistence": 3600,
    "maxExecutorThreads": 20,
    "maxProverThreads": 8,
    "maxStateDBThreads": 8
}
```

### Configure services

Edit the ~/zkevm/zkevm-node/mainnet/docker-compose.yml file and replace it with the following content:

```yml
version: "3.5"

networks:
  default:
    name: zkevm

services:

  zkevm-state-db:
    container_name: zkevm-state-db
    restart: unless-stopped
    image: postgres
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ]
    ports:
      - 5432:5432
    volumes:
      - ./db/scripts/init_prover_db.sql:/docker-entrypoint-initdb.d/init.sql
      - ${ZKEVM_NODE_STATEDB_DATA_DIR}:/var/lib/postgresql/data
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/postgresql.conf:/etc/postgresql.conf
    environment:
      - POSTGRES_USER=state_user
      - POSTGRES_PASSWORD=state_password
      - POSTGRES_DB=state_db
    command:
      - "postgres"
      - "-N"
      - "500"
      - "-c"
      - "config_file=/etc/postgresql.conf"

  zkevm-pool-db:
    container_name: zkevm-pool-db
    restart: unless-stopped
    image: postgres
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ]
    deploy:
      resources:
        limits:
          memory: 2G
        reservations:
          memory: 1G
    ports:
      - 5433:5432
    volumes:
      - ${ZKEVM_NODE_POOLDB_DATA_DIR}:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=pool_user
      - POSTGRES_PASSWORD=pool_password
      - POSTGRES_DB=pool_db
   command:
      - "postgres"
      - "-N"
      - "500"

  zkevm-executor:
    container_name: zkevm-executor
    restart: unless-stopped
    image: hermeznetwork/zkevm-prover:v1.1.4-RC2-fork.4
    depends_on:
      zkevm-state-db:
        condition: service_healthy
    ports:
      - 50061:50061 # MT
      - 50071:50071 # Executor
    volumes:
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.prover.config.json:/usr/src/app/config.json
    command: >
      zkProver -c /usr/src/app/config.json

  zkevm-sync:
    container_name: zkevm-sync
    restart: unless-stopped
    depends_on:
      zkevm-state-db:
        condition: service_healthy
      zkevm-executor:
        condition: service_started
    image: hermeznetwork/zkevm-node:v0.1.2-RC1
    environment:
      - ZKEVM_NODE_ETHERMAN_URL=${ZKEVM_NODE_ETHERMAN_URL}
    volumes:
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.genesis.config.json:/app/genesis.json
    command:
      - "/bin/sh"
      - "-c"
      - "/app/zkevm-node run --network custom --custom-network-file /app/genesis.json --cfg /app/config.toml --components synchronizer"

  zkevm-l2gaspricer:
    container_name: zkevm-l2gaspricer
    image: hermeznetwork/zkevm-node:v0.1.2-RC1
    depends_on:
      zkevm-pool-db:
          condition: service_healthy
    environment:
      - ZKEVM_NODE_POOL_DB_HOST=zkevm-pool-db
    volumes:
      - ${ZKEVM_CONFIG_DIR}/sequencer.keystore:/pk/keystore
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.genesis.config.json:/app/genesis.json
    command:
      - "/bin/sh"
      - "-c"
      - "/app/zkevm-node run --network custom --custom-network-file /app/genesis.json --cfg /app/config.toml --components l2gaspricer"

  zkevm-eth-tx-manager:
    container_name: zkevm-eth-tx-manager
    image: hermeznetwork/zkevm-node:v0.1.2-RC1
    depends_on:
      zkevm-state-db:
          condition: service_healthy
    ports:
      - 9094:9091 # needed if metrics enabled
    environment:
      - ZKEVM_NODE_STATEDB_HOST=zkevm-state-db
    volumes:
      - ${ZKEVM_CONFIG_DIR}/sequencer.keystore:/pk/sequencer.keystore
      - ${ZKEVM_CONFIG_DIR}/aggregator.keystore:/pk/aggregator.keystore
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.genesis.config.json:/app/genesis.json
    command:
      - "/bin/sh"
      - "-c"
      - "/app/zkevm-node run --network custom --custom-network-file /app/genesis.json --cfg /app/config.toml --components eth-tx-manager"

  zkevm-rpc:
    container_name: zkevm-rpc
    restart: unless-stopped
    depends_on:
      zkevm-pool-db:
        condition: service_healthy
      zkevm-state-db:
        condition: service_healthy
      zkevm-sync:
        condition: service_started
    image: hermeznetwork/zkevm-node:v0.1.2-RC1
    ports:
      - 8545:8545
      - 9091:9091 # needed if metrics enabled
    environment:
      - ZKEVM_NODE_ETHERMAN_URL=${ZKEVM_NODE_ETHERMAN_URL}
    volumes:
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.genesis.config.json:/app/genesis.json
    command:
      - "/bin/sh"
      - "-c"
      - "/app/zkevm-node run --network custom --custom-network-file /app/genesis.json --cfg /app/config.toml --components rpc --http.api eth,net,debug,zkevm,txpool,web3"

  zkevm-sequencer:
    container_name: zkevm-sequencer
    image: hermeznetwork/zkevm-node:v0.1.2-RC1
    depends_on:
      zkevm-pool-db:
        condition: service_healthy
      zkevm-state-db:
        condition: service_healthy
      zkevm-executor:
        condition: service_started
    ports:
      - 9092:9091 # needed if metrics enabled
      - 6060:6060
    environment:
      - ZKEVM_NODE_STATEDB_HOST=zkevm-state-db
      - ZKEVM_NODE_POOL_DB_HOST=zkevm-pool-db
      - ZKEVM_NODE_SEQUENCER_SENDER_ADDRESS=0x593A083125Dc0f6E50c0DAe8689ece7E22987450
    volumes:
      - ${ZKEVM_CONFIG_DIR}/sequencer.keystore:/pk/sequencer.keystore
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.genesis.config.json:/app/genesis.json
    command:
      - "/bin/sh"
      - "-c"
      - "/app/zkevm-node run --network custom --custom-network-file /app/genesis.json --cfg /app/config.toml --components sequencer,sequence-sender"

  zkevm-aggregator:
    container_name: zkevm-aggregator
    image: hermeznetwork/zkevm-node:v0.1.2-RC1
    depends_on:
      zkevm-pool-db:
        condition: service_healthy
      zkevm-state-db:
        condition: service_healthy
    ports:
      - 50081:50081
      - 9093:9091 # needed if metrics enabled
    environment:
      - ZKEVM_NODE_STATEDB_HOST=zkevm-state-db
      - ZKEVM_NODE_AGGREGATOR_SENDER_ADDRESS=0x37dB10dab4C2aEE6D161ad873D0696C4494613D0
    volumes:
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.genesis.config.json:/app/genesis.json
    command:
      - "/bin/sh"
      - "-c"
      - "/app/zkevm-node run --network custom --custom-network-file /app/genesis.json --cfg /app/config.toml --components aggregator"

  zkevm-explorer-l2:
    container_name: zkevm-explorer-l2
    image: hermeznetwork/zkevm-explorer:latest
    ports:
      - 4004:4004
    environment:
      - PORT=4004
      - NETWORK=POE
      - SUBNETWORK=Polygon zkEVM
      - CHAIN_ID=42069
      - COIN=ETH
      - ETHEREUM_JSONRPC_VARIANT=geth
      - ETHEREUM_JSONRPC_HTTP_URL=http://zkevm-rpc:8545
      - DATABASE_URL=postgres://l2_explorer_user:l2_explorer_password@zkevm-explorer-l2-db:5432/explorer
      - ECTO_USE_SSL=false
      - MIX_ENV=prod
      - LOGO=/images/blockscout_logo.svg
      - LOGO_FOOTER=/images/blockscout_logo.svg
      - SUPPORTED_CHAINS=[]
      - SHOW_OUTDATED_NETWORK_MODAL=false
    command: ["/bin/sh", "-c", "mix do ecto.create, ecto.migrate; mix phx.server"]
  zkevm-explorer-l2-db:
    container_name: zkevm-explorer-l2-db
    image: postgres
    ports:
      - 5436:5432
    environment:
      - POSTGRES_USER=l2_explorer_user
      - POSTGRES_PASSWORD=l2_explorer_password
      - POSTGRES_DB=l2_explorer_db
    command: [ "postgres", "-N", "500" ]

  zkevm-bridge-db:
    container_name: zkevm-bridge-db
    image: postgres
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ]
    expose:
      - 5435
    ports:
      - 5435:5432
    environment:
      - POSTGRES_USER=test_user
      - POSTGRES_PASSWORD=test_password
      - POSTGRES_DB=test_db
    command: ["postgres", "-N", "500"]

  zkevm-bridge-service:
    container_name: zkevm-bridge-service
    image: hermeznetwork/zkevm-bridge-service:v0.1.0
    depends_on:
      zkevm-bridge-db:
        condition: service_healthy
    ports:
      - 9090:9090
      - 8081:8080
    environment:
      - ZKEVM_BRIDGE_DATABASE_USER=test_user
      - ZKEVM_BRIDGE_DATABASE_PASSWORD=test_password
      - ZKEVM_BRIDGE_DATABASE_NAME=test_db
      - ZKEVM_BRIDGE_DATABASE_HOST=zkevm-bridge-db
      - ZKEVM_BRIDGE_DATABASE_PORT=5432
    volumes:
      - ${ZKEVM_CONFIG_DIR}/sequencer.keystore:/pk/sequencer.keystore
      - ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
    command:
      - "/bin/sh"
      - "-c"
      - "/app/zkevm-bridge run --cfg /app/config.toml"
  zkevm-bridge-ui:
    container_name: zkevm-bridge-ui
    image: hermeznetwork/zkevm-bridge-ui:latest
    ports:
      - 8080:80
    environment:
      - ETHEREUM_RPC_URL=$ZKEVM_NODE_ETHERMAN_URL
      - ETHEREUM_EXPLORER_URL=https://goerli.etherscan.io
      - ETHEREUM_BRIDGE_CONTRACT_ADDRESS=0x5567fAbF3B17F320BA3906d2f6CD43021d27A8AB
      - ETHEREUM_FORCE_UPDATE_GLOBAL_EXIT_ROOT=true
      - ETHEREUM_PROOF_OF_EFFICIENCY_CONTRACT_ADDRESS=0x12c3e0CC1d4619deaC19014a72C98c129448Cfd7
      - POLYGON_ZK_EVM_RPC_URL=http://147.83.40.37:8545
      - POLYGON_ZK_EVM_EXPLORER_URL=http://147.83.40.37:4004
      - POLYGON_ZK_EVM_BRIDGE_CONTRACT_ADDRESS=0x5567fAbF3B17F320BA3906d2f6CD43021d27A8AB
      - POLYGON_ZK_EVM_NETWORK_ID=1
      - BRIDGE_API_URL=http://147.83.40.37:8081
      - ENABLE_FIAT_EXCHANGE_RATES=false
      - ENABLE_OUTDATED_NETWORK_MODAL=false
      - ENABLE_DEPOSIT_WARNING=true
      - ENABLE_REPORT_FORM=false

  zkevm-prover-server:
    container_name: zkevm-prover-server
    image: hermeznetwork/zkevm-prover:v1.1.4-RC2-fork.4
    ports:
      - 50051:50051
    volumes:
     - ~/zkevm/v1.1.0-rc.1-fork.4/config:/mnt/prover/config:ro
     - ~/zkevm/config.json:/app/config.json
    command: "zkProver -c /app/config.json"
```

### Start services

Start databases:

```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-pool-db zkevm-state-db
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-pool-db
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-state-db
```

Start executor:

```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-executor
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-executor
```

Start syncronizer

```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-sync
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-sync
```


Start L2 gas pricer

```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-l2gaspricer
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-l2gaspricer
```

Start Transaciion manager

```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-eth-tx-manager
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-eth-tx-manager
```

Start RPC

```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-rpc
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-rpc
```

Start sequencer

```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-sequencer
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-sequencer
```

Start aggregator

```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-aggregator
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-aggregator
```

Start Explorer

```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-explorer-l2 zkevm-explorer-l2-db
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-explorer-l2-db
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-explorer-l2
```

Start bridge

```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-bridge-service zkevm-bridge-ui zkevm-bridge-db
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-bridge-db
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-bridge-service
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-bridge-ui
```
Start prover

```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-prover-server
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-prover-server
```

#### Open Ports

* zkevm-aggregator       50081->50081/tcp
* zkevm-aggregator       9093->9091/tcp
* zkevm-bridge-db        5435->5432/tcp
* zkevm-bridge-service   9090->9090/tcp, 8081->8080/tcp
* zkevm-bridge-ui        8080->80/tcp
* zkevm-eth-tx-manager   9094->9091/tcp
* zkevm-executor         50061->50061/tcp, 50071->50071/tcp
* zkevm-explorer-l2      4004->4004/tcp
* zkevm-explorer-l2-db   5436->5432/tcp
* zkevm-pool-db          5433->5432/tcp
* zkevm-prover-server    50051->50051/tcp
* zkevm-rpc              8545->8545/tcp, 9091->9091/tcp
* zkevm-sequencer        6060->6060/tcp, 9092->9091/tcp
* zkevm-state-db         5432->5432/tcp


### Activate forced transactions

```bash
npx hardhat console --network goerli
```
```js
const zkevm = await ethers.getContractFactory('PolygonZkEVM')
const zkevmContract = zkevm.attach("_polygonZkEVMAddress_")

const provider = ethers.getDefaultProvider("") // set goerli RPC node
const privateKey = '' // Deployment Address from wallet.txt
const wallet = new ethers.Wallet(privateKey, provider);

const zkevmContractWallet = zkevm.connect(wallet)

await zkevmContract.activateForceBatches()
```
### Provide L1 Ether to the bridge

We are going to create a bridge transaction to send L1 Ether to the bridge.

```bash
mkdir -p ~/zkevm/initial-bridge
cd zkevm/initial-bridge

wget https://raw.githubusercontent.com/0xPolygonHermez/zkevm-bridge-service/develop/test/scripts/deposit/main.go

nano main.go
```
```go
l1BridgeAddr = "" // zkevm-contracts/deployments/goerli_*/deploy_output.json: polygonZkEVMBridgeAddress

l1AccHexAddress    = "" // zkevm-contracts/wallets.txt: sequencer address
l1AccHexPrivateKey = "" // zkevm-contracts/wallets.txt: sequencer prvkey
l1NetworkURL       = "http://X.X.X.X:8545" // set your public IP
```
```bash
go mod init example.com/deposit
go mod tidy
go run main.go
# 2023-06-07T06:29:14.211Z      INFO    initial-bridge/main.go:46       Success!        {"pid": 776268, "version": "v0.1.0"}
```

### Claim our L2 zkEther

Create a forzed claim to get the zkEther in L2.

```bash
mkdir -p ~/zkevm/initial-claim

cd zkevm/initial-claim
wget https://raw.githubusercontent.com/0xPolygonHermez/zkevm-bridge-service/develop/test/scripts/initialClaim/main.go
```

Open main.go and modify the following parameters:

```go
const (
        l2BridgeAddr = "" // zkevm-contracts/deployments/goerli_*/deploy_output.json: polygonZkEVMBridgeAddress
        zkevmAddr    = "" // zkevm-contracts/deployments/goerli_*/deploy_output.json: polygonZkEVMAddress

        accHexAddress    = "" // zkevm-contracts/wallets.txt: sequencer address
        accHexPrivateKey = "" // zkevm-contracts/wallets.txt: sequencer prvkey
        l1NetworkURL     = "http://X.X.X.X:8545" // public IP
        l2NetworkURL     = "http://X.X.X.X:8123" // public IP
        bridgeURL        = "http://X.X.X.X:8080" // public IP

        l2GasLimit = 1000000

        mtHeight      = 32
        miningTimeout = 180
)
```

```go
go mod init example.com/claim
go mod tidy
go get github.com/0xPolygonHermez/zkevm-bridge-service@4e1ca558144cac00fe0762329aaf7b3e9482b57a
go run main.go

// 2023-06-07T06:41:50.731Z     INFO    initial-claim/main.go:196       Success!!!!     {"pid": 783804, "version": "v0.1.0"}

```


# [Extra] L1 Goerli fullnode

We are going to use the Goerli testnet as layer 1 (L1). We need to do quite a lot of transactions in L1 so it is recommended to build your own fullnode. To do so, follow the following step>

## Requirements

### Resources

A minimal of **300GB** of free disk space is required to allocate a full Goerli node.

### Software
The requirements of this tutorial are the following commands:

* wget
* jq
* docker

```bash
sudo apt update -y
sudo apt install -y docker.io

sudo usermod -aG docker $USER
newgrp docker && newgrp $USER
```

### L1 account

An L1 Goerli address is necessary to be provisioned as the *suggested fee recipient*.

## Preparation

```bash
mkdir -p ~/goerli-node/docker-volumes/{geth,prysm}
cd ~/goerli-node/

nano docker-compose.yml
```

Create a *docker-compose.yml* file
```yml
services:
  geth:
    image: ethereum/client-go:stable
    container_name: goerli-execution
    command: |
      --goerli
      --http
      --http.vhosts=*
      --http.rpcprefix=/
      --http.corsdomain=*
      --http.addr 0.0.0.0
      --http.api eth,net,engine,admin
    volumes:
      - ./docker-volumes/geth:/root/.ethereum
    ports:
      - "0.0.0.0:${L1_RPC_PORT}:8545"
      - "0.0.0.0:30303:30303/udp"

  prysm:
    image: gcr.io/prysmaticlabs/prysm/beacon-chain:stable
    container_name: goerli-consensus
    command: |
      --prater
      --datadir=/data
      --jwt-secret=/geth/goerli/geth/jwtsecret
      --rpc-host=0.0.0.0
      --grpc-gateway-host=0.0.0.0
      --monitoring-host=0.0.0.0
      --execution-endpoint=/geth/goerli/geth.ipc
      --accept-terms-of-use
      --suggested-fee-recipient=${L1_SUGGESTED_FEE_RECIPIENT_ADDR}
      --checkpoint-sync-url=${L1_CHECKPOINT_URL**}
    volumes:
      - ./docker-volumes/prysm:/data
      - ./docker-volumes/geth:/geth

   ports:
      - "0.0.0.0:3500:3500"
      - "0.0.0.0:4000:4000"
      - "0.0.0.0:12000:12000/udp"
      - "0.0.0.0:13000:13000"
    depends_on:
      - geth
```

Create an account

nano .env
```yml
L1_RPC_PORT=8845
L1_SUGGESTED_FEE_RECIPIENT_ADDR=0x  # Put your Goerli account
L1_CHECKPOINT_URL=https://goerli.checkpoint-sync.ethpandaops.io
```

## Deployment

Finally, start the compose services and check the logs until everything is synchronized.

```bash
docker compose up -d

docker compose logs -f prysm
# Wait for the initial-sync goerli-consensus -> (eta 10min in our case)
#goerli-consensus  | time="2023-06-19 09:39:44" level=info msg="Synced up to slot 5888296" prefix=initial-sync

docker compose logs -f geth
# Wait for the initial download & sync (a couple of hours) 
#goerli-execution  | INFO [06-19|09:43:24.954] Syncing beacon headers                   downloaded=25600 left=9,177,918 eta=1h5m31.860s
#goerli-execution  | INFO [06-19|10:09:19.488] Syncing: state download in progress      synced=0.30% state=331.34MiB accounts=81053@20.52MiB slots=1,112,986@239.47MiB codes=11681@71.34MiB >
```

## Validation

Once both service logs show the sync completion and there are new blocks updating the system, you could verify the correctness of the RPC doing a call. For example, you could get the current block number with the following command:

```bash
printf "%d\n" $(curl -s -X POST --header "Content-Type: application/json"  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":83}' http://localhost:8845 | jq -r .result)
```

## [OPTIONAL] HTTPS Reverse Proxy

Remove exposed port in docker compose form geth and build a reverse proxy pointing this service. Some examples for reverse proxy service are nginx or traefik.

In our case, we already have a traefik on the VM. So we only need to add the public network and the correct labels to expose the service on YOUR_FQDN_NAME.
```yml
services:
  geth:
    ...
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=public"
      - "traefik.http.routers.traefik.rule=Host(`YOUR_FQDN_NAME`)"
      - "traefik.http.routers.traefik.tls.domains[0].main=YOUR_FQDN_NAME"
      - "traefik.http.routers.traefik.tls.certresolver=letsEncrypt"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.services.traefik.loadbalancer.server.port=8545"
    networks:
      - public
      - goerli

  prysm:
    ...
    networks:
      - goerli

networks:
  goerli:
  public:
    external: true
```