Step-by-step guide to registering as an operator with Ava Protocol AVS and running the operator software using Docker.
This guide will walk you through the process of registering as an operator to Ava OProtocol AVS and running the Ava Protocol software.
This guide focus on running everything with-in docker container. If you don't want to use docker container, you can follow other guide instead
An operator need to be onboard and setup their own operator with EigenLayer, following the official document.
Operating System: Linux, MacOS
CPU: x64/arm.
vCPUs: 1
Memory: 1GiB
Storage: 100GB
EC2 Equivalent: c6gd.medium, m6g.medium, c7a.medium, c6g.large
Expected Network Utilization:
Incoming Ports: 9190, 9191 for mainnet; 9290, 9291 for Sepolia. The two sets differ so you can run both on one host. Open your firewall for whichever you use, and if you customize the docker compose ports then adjust accordingly.
Outgoing Ports: 57376
If your cloud providers support Arm CPU, we suggest to use Arm because it's more cost effective.
git clone git@github.com:AvaProtocol/ap-operator-setup.git
cd ap-operator-setup
The repository has two directories. Pick the one for the network you are running:
| Directory | Network | AVS registration chain | Use it for |
|---|---|---|---|
ethereum | Ethereum mainnet | Ethereum | Production. Real stake, real slashing. |
sepolia | Sepolia + Base Sepolia | Sepolia | Testing your setup before committing mainnet stake. |
Both dial the same aggregator address. The gateway is a single deployment that
routes per-chain internally, so which network you operate on is decided by the AVS
you registered against and the chains: block in your config — not by the address
you dial.
Inside your network's directory, you will need to prepare two files: .env and config.yaml.
Make sure you are in the ethereum (or sepolia) directory, then prepare the .env file
cp .env.example .env
Then, edit it and fill in these information:
Specify your operator’s keystore location and password. These are to be used to commit to your registered operator.
- ECDSA_KEYSTORE_PATH=
- ECDSA_KEY_PASSWORD=
- BLS_KEYSTORE_PATH=
- BLS_KEY_PASSWORD=
Besides, the DB_PATH is to specify the local path to store your operator’s data for our AVS.
- DB_PATH=
We don't use high io so you can store on a normal volume such as gp3 with 3000 IOPS on EC2.
If the default ports of PUBLIC_NODEAPI_PORT and PUBLIC_METRICS_PORT were used by different processes, you can also set them to any available ports in your env file too. Make sure to also open firewall to allow traffic incoming to these 2 ports. The default value are:
Mainnet: 9190 for the metric port, 9191 for the node api port. Sepolia: 9290 for the metric port, 9291 for the node api port.
Next, we will create config.yaml file for operator:
cp config.yaml.example config.yaml
Change the value of operator_address to your own operator wallet address, and
confirm the aggregator endpoint reads:
aggregator_server_ip_port_address: "aggregator.avaprotocol.org:57376"
A single gateway serves every chain, so this one address is correct for all of
them. Port 2206 was retired with the previous per-chain aggregators and no
longer accepts connections.
This step is only needed to be done once per operator. Also, recall that you would need to cd into your network's directory before running anything.
docker compose run ap-operator register --config=/app/config.yaml
To check the registration status at any given time you can also do:
docker compose run ap-operator status --config=/app/config.yaml
Ensure that you successfully register your operator before moving to step 3.
At this point, you're ready to run your AVS. Optionally you can set up alias keys (see the operator guide) to avoid direct access to the EigenLayer ECDSA operator key. You can also switch to the alias key setup at a later time. There is no requirement on following the exact ordering.
Make sure you are under your network's directory (./ethereum or ./sepolia).
Run the following command to start the operator
docker compose pull
docker compose up --force-recreate -d
Once the operator is up and running, the output log will look like below.
docker compose up --force-recreate -d
[+] Running 1/0
✔ Container ap_operator Created
✔ Container ap_operator Started
To view the operator log itself, you can do:
docker compose logs -f
The log should appear similar to this:
ap_operator | {"level":"info","ts":1719529804.5644045,"caller":"operator/operator.go:263","msg":"Connect to aggregator aggregator.avaprotocol.org:57376"}
ap_operator | {"level":"info","ts":1719529804.8751178,"caller":"operator/operator.go:307","msg":"Operator info","operatorId":[74,60,26,85,160,147,136,79,102,183,189,62,99,76,192,151,203,7,97,85,230,236,25,160,46,242,83,194,177,93,63,163],"operatorAddr":"0x2273e70Ea0F159985a9312e875839CbF242f162e","operatorG1Pubkey":"E([13980129839750270625587959504067205960106881892608925358182969477593110597180,2713793992502006479543294653290264953732656227600455037615150886215476630684])","operatorG2Pubkey":"E([10006440951214432193970386287330007898372605552301114697229665952718363326438+2917899138783614023915162275072742305856792653861495716209344717215206657922*u,20465317265628248898772842070116958367267377808142334627836040792686631701030+11895853732396257221594908719294998059804388586884333547663795174064486592588*u])"}
ap_operator | {"level":"info","ts":1719529805.3309655,"caller":"operator/operator.go:330","msg":"Starting operator."}
ap_operator | {"level":"info","ts":1719529805.3310997,"caller":"nodeapi/nodeapi.go:104","msg":"Starting node api server at address 0.0.0.0:9010"}
ap_operator | {"level":"info","ts":1719529805.33198,"caller":"metrics/eigenmetrics.go:81","msg":"Starting metrics server at port 0.0.0.0:9090"}
ap_operator | {"level":"info","ts":1719529805.3321455,"caller":"nodeapi/nodeapi.go:238","msg":"node api server running","addr":"0.0.0.0:9010"}
To perform auto update, we use
watchtower. It watches ap_operator container and check for the docker image update.
The ap_operator uses image tag avaprotocol/ap-avs:latest and not pin to a version. Once a new image is pushed to docker hub and tag with latest, watchtower detects that a new image has been pushed, it will pull the new avaprotocol/ap-avs:latest docker image. Then it will recreate the operator container using the new docker image.
Obviously this kind of update will only work when there is no config change required. If there is a configuration change that requires you to set a flag in the config, then we will need to perform a manually config change and then update. However, we try hard to make the update painless and majority of time we can let it auto updated.
To run the auto update simply perform:
# make sure you're inside the `watchtower` directory.
# then bring up docker compose
docker compose up -d
you should see something like this
✔ Container watchtower Started
To check the log you can do:
docker compose logs -f
And it should show up like this:
❯ docker compose logs -f
watchtower | time="2024-07-03T00:01:58Z" level=info msg="Watchtower 1.7.1"
watchtower | time="2024-07-03T00:01:58Z" level=info msg="Using no notifications"
watchtower | time="2024-07-03T00:01:58Z" level=info msg="Only checking containers which name matches \"ap_operator\""
watchtower | time="2024-07-03T00:01:58Z" level=info msg="Scheduling first run: 2024-07-03 08:01:58 +0000 UTC"
watchtower | time="2024-07-03T00:01:58Z" level=info msg="Note that the first check will be performed in 7 hours, 59 minutes, 59 seconds"
You can read more about the watchtower with advanced feature such as Slack notification and build your own docker compose based on our file here.
# pull the lastest change from our repository
git pull
# cd into your network's directory (ethereum for mainnet, sepolia for testnet)
cd ethereum
# then issue a pull command to fetch latest image
docker compose pull
# finally restart the container with the new image
docker compose up --force-recreate -d
The compose file resolves the image as ${AP_OPERATOR_IMAGE:-avaprotocol/ap-avs:latest}.
If you have pinned AP_OPERATOR_IMAGE in your .env, docker compose pull fetches
that pinned tag and will not move you onto a newer release — check the variable
first if an update appears to do nothing. Run released tags (avaprotocol/ap-avs:latest,
or a specific vX.Y.Z); the main-* and staging-* tags are unreleased builds and
are not intended for operators.
If you want to configure auto update, check our instruction in watchtower
There are several ways to verify that your operator is properly connected to the aggregator and functioning correctly:
The most reliable way to verify your operator's connection status is to use the status command:
# mainnet
docker exec ap_operator_ethereum ap-avs status --config=/app/config.yaml
# sepolia
docker exec ap_operator_sepolia ap-avs status --config=/app/config.yaml
Note:
ap-avsis the name of the operator program filename defined in the Docker image (avaprotocol/ap-avs:latest).
This command will show detailed information about your operator, including:
Example output:
{"level":"info","ts":1747089700.5383499,"caller":"operator/operator.go:199","msg":"starting operator","version":"1.5.0","commit":"63bc1de4f0cb7ae6b6d2390a154b852359da17be"}
{"level":"info","ts":1747089701.277407,"caller":"operator/operator.go:262","msg":"detect EigenLayer on chain id 11155111"}
{"level":"info","ts":1747089703.49693,"caller":"operator/operator.go:375","msg":"Connect to aggregator aggregator.avaprotocol.org:57376"}
{"level":"info","ts":1747089703.496955,"caller":"operator/operator.go:458","msg":"attempt connect to aggregator","aggregatorAddress":"aggregator.avaprotocol.org:57376"}
{"level":"info","ts":1747089703.4970033,"caller":"operator/operator.go:465","msg":"connected to aggregator","aggregatorAddress":"aggregator.avaprotocol.org:57376"}
{"level":"info","ts":1747089703.5477848,"caller":"operator/operator.go:386","msg":"Operator info","operatorId":[185,233,179,238,203,233,97,253,9,241,188,88,137,29,216,23,19,148,188,206,141,18,221,221,205,237,203,53,32,209,13,42],"operatorAddr":"0x997e5d40a32c44a3d93e59fc55c4fd20b7d2d49d","signerAddr":"0x997E5D40a32c44a3D93E59fC55C4Fd20b7d2d49D","operatorG1Pubkey":"E([15789008173331395157264428572780114720479261082645898324102422503216881495306,9854416317604314384620556886101178508865652991048571373873567709449834471808])","operatorG2Pubkey":"E([14769851261819437570450241685466295357262291383177580127881502937784555365035+6807621162694010296234859332341115003523494784170742240370377455374932704462*u,17165481903016912557293840267811427985881685249125489900856363525752535580237+21463585826917002309639607977412863935271056092026521454249614494808379587595*u])","prmMetricsEndpoint":"0.0.0.0:9090/metrics/"}
{"level":"info","ts":1747089703.547821,"caller":"operator/registration.go:120","msg":"checking operator status"}
{
"EcdsaAddress": "0x997E5D40a32c44a3D93E59fC55C4Fd20b7d2d49D",
"PubkeysRegistered": true,
"G1Pubkey": "E([15789008173331395157264428572780114720479261082645898324102422503216881495306,9854416317604314384620556886101178508865652991048571373873567709449834471808])",
"G2Pubkey": "E([14769851261819437570450241685466295357262291383177580127881502937784555365035+6807621162694010296234859332341115003523494784170742240370377455374932704462*u,17165481903016912557293840267811427985881685249125489900856363525752535580237+21463585826917002309639607977412863935271056092026521454249614494808379587595*u])",
"RegisteredWithAvs": true,
"OperatorId": "b9e9b3eecbe961fd09f1bc58891dd8171394bcce8d12ddddcdedcb3520d10d2a"
}
The key things to verify in this output:
"connected to aggregator" message indicates successful connection"PubkeysRegistered": true confirms your keys are registered with EigenLayer"RegisteredWithAvs": true confirms your operator is registered with Ava Protocol AVSYou can examine the operator logs to verify connection status:
docker logs ap_operator_ethereum -n 100 -f # or ap_operator_sepolia
Look for these key log entries:
"Connect to aggregator aggregator.avaprotocol.org:57376" - Initial connection attempt"connected to aggregator" - Successful connection"Subscribe to aggregator to get check" - Subscription to receive tasksYour operator should appear on the telemetry dashboard, which displays all connected operators:
https://api.avaprotocol.org/telemetry
One dashboard covers every network.
The dashboard shows:
If you have access to your operator's server, you can check the metrics endpoint:
curl http://localhost:9090/metrics | grep ping
Look for metrics like ap_num_ping_total which tracks successful pings to the aggregator.
To confirm your operator can monitor and trigger tasks, check for these log entries:
"received new block trigger", "received new event trigger", or "received new cron trigger" - Indicates your operator is receiving task definitions"block trigger", "event trigger", or "time trigger" - Indicates your operator is detecting and processing trigger conditions