Deploying an Airnode on Google Cloud
This guide demonstrates the deployment of an Airnode followed by an off-chain HTTP Gateway request. Configuration files are provided with only minor changes to be made. The latest release of the Airnode deployer image will be used to deploy the off-chain component of Airnode (a.k.a., the node) to GCP.
This Airnode contains a single API operation (GET /simple/price
) from CoinGecko which returns the current value of a coin. This guide does not detail the overall configuration of an Airnode, it is just a quick start guide then lends itself to understanding an Airnode deployment.
Please note that this tutorial does not involve the blockchain nor an RRP (request-response protocol) call from a smart contract. If you wish to make an RRP call, please see the guides Making an RRP Request and Calling an Airnode.
Configuration Files
An Airnode deployment on GCP uses the Docker deployer image which requires three files as input: config.json, secrets.env, and gcp.json. The config.json
and secrets.env
files have been created and only require a few minor changes to make the deployment of the Airnode successful. The changes are needed to supply a GCP project ID, a chain provider url, and a mnemonic.
1. Install Prerequisites
Install the Docker Desktop and launch it.
2. Project Folder
Download the quick-start-gcp.zip project folder. Extract it into any location.
quick-start-gcp
├── config.json
└── secrets.env
2
3
3. GCP Project Setup & Credentials
First create a GCP project (or use an existing GCP project) where the Airnode will be deployed. Once the project is created, add the project ID to the secrets.env file.
Make sure you have billing enabled for your project. To do so, you will need to pair the project with your bank card, although no charges will be incurred since the resource usage fits well within the free tier limit.
In order for Airnode to deploy successfully, you need to enable the App Engine Admin API specifically for the project. After enabling it, wait a few minutes before deploying the Airnode for this change to take effect.
Create a new service account from the IAM and admin > Service accounts menu. Grant this account access to the project by adding the role
Owner
during creation.Once the new service account is created, click on it to bring up its management page. Select the KEYS tab and add a new access key of type JSON for this account. Download the key file and place in the root of the
/quick-start-gcp
project directory. Rename itgcp.json
.
4. Prepare Configuration Files
Prepare the three configuration files. The Airnode deployer image looks for config.json
, secrets.env
, and gcp.json
in the project root directory and writes receipt.json
to the project root directory.
config.json
config.json
{
"chains": [
{
"authorizers": {
"requesterEndpointAuthorizers": [],
"crossChainRequesterAuthorizers": [],
"requesterAuthorizersWithErc721": [],
"crossChainRequesterAuthorizersWithErc721": []
},
"authorizations": {
"requesterEndpointAuthorizations": {}
},
"id": "11155111",
"providers": {
"myChainProvider": {
"url": "${CHAIN_PROVIDER_URL}"
}
},
"type": "evm",
"options": {
"gasPriceOracle": [
{
"gasPriceStrategy": "latestBlockPercentileGasPrice",
"percentile": 60,
"minTransactionCount": 20,
"pastToCompareInBlocks": 20,
"maxDeviationMultiplier": 2
},
{
"gasPriceStrategy": "providerRecommendedGasPrice",
"recommendedGasPriceMultiplier": 1.2
},
{
"gasPriceStrategy": "providerRecommendedEip1559GasPrice",
"baseFeeMultiplier": 2,
"priorityFee": {
"value": 3.12,
"unit": "gwei"
}
},
{
"gasPriceStrategy": "constantGasPrice",
"gasPrice": {
"value": 10,
"unit": "gwei"
}
}
]
},
"maxConcurrency": 100
}
],
"nodeSettings": {
"cloudProvider": {
"type": "gcp",
"region": "us-east1",
"disableConcurrencyReservations": false,
"projectId": "${PROJECT_ID}"
},
"airnodeWalletMnemonic": "${AIRNODE_WALLET_MNEMONIC}",
"heartbeat": {
"enabled": false
},
"httpGateway": {
"enabled": true,
"maxConcurrency": 20,
"corsOrigins": []
},
"httpSignedDataGateway": {
"enabled": false
},
"oevGateway": {
"enabled": false
},
"logFormat": "plain",
"logLevel": "DEBUG",
"nodeVersion": "0.14.1",
"stage": "quick-gcp"
},
"triggers": {
"rrp": [
{
"endpointId": "0x6db9e3e3d073ad12b66d28dd85bcf49f58577270b1cc2d48a43c7025f5c27af6",
"oisTitle": "CoinGecko Basic Request",
"endpointName": "coinMarketData",
"cacheResponses": false
}
],
"http": [
{
"endpointId": "0x6db9e3e3d073ad12b66d28dd85bcf49f58577270b1cc2d48a43c7025f5c27af6",
"oisTitle": "CoinGecko Basic Request",
"endpointName": "coinMarketData"
}
],
"httpSignedData": []
},
"templates": [],
"ois": [
{
"oisFormat": "2.3.2",
"title": "CoinGecko Basic Request",
"version": "1.0.0",
"apiSpecifications": {
"servers": [
{
"url": "https://api.coingecko.com/api/v3"
}
],
"paths": {
"/simple/price": {
"get": {
"parameters": [
{
"in": "query",
"name": "ids"
},
{
"in": "query",
"name": "vs_currencies"
}
]
}
}
},
"components": {
"securitySchemes": {}
},
"security": {}
},
"endpoints": [
{
"name": "coinMarketData",
"operation": {
"method": "get",
"path": "/simple/price"
},
"fixedOperationParameters": [],
"reservedParameters": [
{
"name": "_type",
"fixed": "int256"
},
{
"name": "_path",
"fixed": "api3.usd"
},
{
"name": "_times",
"fixed": "1000000"
}
],
"parameters": [
{
"name": "coinIds",
"operationParameter": {
"in": "query",
"name": "ids"
}
},
{
"name": "coinVs_currencies",
"operationParameter": {
"in": "query",
"name": "vs_currencies"
}
}
]
}
]
}
],
"apiCredentials": []
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
This file requires no changes on your part. It has been created with just one API endpoint and configured to listen to requests on the Sepolia test network, though this tutorial will not make any such requests. There are a few variables this file will interpolate from secrets.env
.
secrets.env
secrets.env
CHAIN_PROVIDER_URL=""
AIRNODE_WALLET_MNEMONIC=""
PROJECT_ID=""
2
3
There are three values config.json
interpolates from secrets.env
. Add values for each of these fields.
CHAIN_PROVIDER_URL
: A blockchain provider url (including its API key) from a provider such as Infura. Use a url for the Sepolia test network.AIRNODE_WALLET_MNEMONIC
: Provide the seed phrase (mnemonic) to a new digital wallet. The wallet does not need to be funded. Use the Admin CLI command generate-airnode-mnemonic to create one.shnpx @api3/airnode-admin generate-airnode-mnemonic
1PROJECT_ID
: Project ID of your GCP project. During step #3 above you should have added the project ID to thesecrets.env
file.
gcp.json
During step #3 above, the gcp.json
file should have been placed into the /quick-start-gcp
project folder root.
5. Deploy
Make sure Docker is running and then execute the deployer image from the root of the quick-start-gcp
folder. A receipt.json
file will be created upon completion. It contains some deployment information and is used to remove the Airnode.
Warning about simultaneous deployments
Avoid running multiple deployments simultaneously as doing so might result in a broken deployment. If this occurs, the standard removal approach may not succeed and Manual Removal may be required.
Run the following command to deploy the Airnode. Normally (for Linux/Mac/WSL2) the deployer image deploy
command is run by the user root. This may cause permission issues when the receipt.json
file is generated. Optionally you can specify the UID (user identifier) and GID (group identifier) that the deployer image should use. Do so by setting the environment variables USER_ID and GROUP_ID, otherwise omit the line containing these variables.
docker run -it --rm \
-e USER_ID=$(id -u) -e GROUP_ID=$(id -g) \
-v "$(pwd):/app/config" \
api3/airnode-deployer:latest deploy
2
3
4
:: For Windows, use CMD (and not PowerShell).
docker run -it --rm ^
-v "%cd%:/app/config" ^
api3/airnode-deployer:latest deploy
2
3
4
5
Make note of the HTTP gateway URL
in your output shown below as it will be different. You will need it to test the Airnode.
✔ Deployed Airnode 0x6A6cF2d0094c73b7aBb22Cd6196824BCBB830125 tutorial-gcp to gcp us-east1
ℹ Outputted config/receipt.json
This file does not contain any sensitive information.
ℹ HTTP gateway URL: https://airnode-6a6cf2d-tutorial-gcp-httpgw-4fhnl4fi.ue.gateway.dev
2
3
4
6. Test the Airnode
After a successful deployment the Airnode can be tested directly using its off-chain HTTP Gateway. As a reminder, this is independent of the blockchain and RRP contract.
HTTP Gateway
Looking at the config.json
code snippet below shows that the HTTP gateway is configured for the Airnode. Furthermore, the endpoint for /simple/price
(with an endpointId
of 0x6...af6
) is present in triggers.http[n]
. Only those endpoints added to the http
array can be tested using the HTTP gateway.
Expand to view: HTTP gateway and endpoint ID
"nodeSettings": {
...
"httpGateway": {
"enabled": true, // The gateway is activated for this Airnode
"maxConcurrency": 20,
"corsOrigins": []
},
...
},
"triggers": {
"rrp": [
{
"endpointId": "0x6db9e3e3d073ad12b66d28dd85bcf49f58577270b1cc2d48a43c7025f5c27af6",
"oisTitle": "CoinGecko Basic Request",
"endpointName": "coinMarketData",
"cacheResponses": false
}
],
"http": [
{
"endpointId": "0x6db9e3e3d073ad12b66d28dd85bcf49f58577270b1cc2d48a43c7025f5c27af6",
"oisTitle": "CoinGecko Basic Request",
"endpointName": "coinMarketData",
}
],
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Execute Endpoint
Use CURL to execute a HTTP gateway request for the CoinGecko endpoint /simple/price
.
As an alternative to CURL, an app such as Insomnia or Postman can be used. Windows users can also use Windows Subsystem for Linux (WSL2) to run CURL on Linux.
In order to test an endpoint, make a HTTP POST request with the Content-Type
header set to application/json
, the endpoint parameters in the request body, and the endpointId
as a path parameter.
-X
: POST-H
: TheContent-Type
using the value ofapplication/json
.-d
: Use request body data to pass the endpoint parameter key/value pair.url
:<httpGatewayUrl>
: The HTTP gateway URL as displayed in the terminal at the end of an Airnode deployment, less the:endpointId
placeholder.0x6db9...c27af6
: Passed as a path parameter, theendpointId
to call. The value originates fromtriggers.rrp[0].endpointId
in theconfig.json
file.
Request
curl -v \
-X POST \
-H 'Content-Type: application/json' \
-d '{"parameters": {"coinIds": "api3", "coinVs_currencies": "usd"}}' \
'<httpGatewayUrl>/0x6db9e3e3d073ad12b66d28dd85bcf49f58577270b1cc2d48a43c7025f5c27af6'
2
3
4
5
curl -v ^
-X POST ^
-H "Content-Type: application/json" ^
-d "{\"parameters\": {\"coinIds\": \"api3\", \"coinVs_currencies\": \"usd\"}}" ^
"<httpGatewayUrl>/0x6db9e3e3d073ad12b66d28dd85bcf49f58577270b1cc2d48a43c7025f5c27af6"
2
3
4
5
Response
{
"rawValue": { "api3": { "usd": 1.18 } },
"encodedValue": "0x0000000000000000000000000000000000000000000000000000000000120160",
"values": ["1180000"]
}
2
3
4
5
Note the JSON response field values
is the API3 price multiplied by 1e6
, which results from setting the _times
reserved parameter to 1000000
in config.json
. This manipulation is necessary in order to correctly handle floating point numbers.
rawValue
: The API's response to Airnode. Presented by the HTTP gateway as a convenience. This is never sent to a requester on-chain.encodedValue
: This is the only field that gets sent to a requester (smart contract) on-chain. It is the encoded bytes of thevalues
field. A requester must decode it to read the response values.values
: A array of values after they are extracted and converted from theencodedValue
to the target type, in this caseapi3.usd
from_path
in reservedParameters. The HTTP gateway provides this as a convenience and never sends the decodedvalues
to a requester on-chain.
7. Remove the Airnode
When you are done with this guide you can remove the deployed Airnode. The following command uses the receipt.json
file that was created when the Airnode was deployed.
docker run -it --rm \
-v "$(pwd):/app/config" \
api3/airnode-deployer:latest remove-with-receipt
2
3
:: For Windows, use CMD (and not PowerShell)
docker run -it --rm ^
-v "%cd%:/app/config" ^
api3/airnode-deployer:latest remove-with-receipt
2
3
4
5
Post Removal
After removing an Airnode it may be necessary to wait several minutes before deploying or redeploying Airnode again to the same GCP project. GCP takes several minutes to complete its behind the scenes clean-up of configured resources.
Summary
You have deployed an Airnode on GCP with its HTTP gateway enabled. The Airnode, upon deployment, started contacting the AirnodeRrpV0
contract on the Sepolia test network to gather any requests made by requesters to this Airnode. However this guide did not address making a request on-chain as its purpose was to quickly deploy a functional Airnode. See the guides Making an RRP Request and Calling an Airnode to learn how your smart contract can make an RRP call to an Airnode.
Finally the API integration was tested using the Airnode's off-chain HTTP gateway. You made a CURL request (using HTTP) to the HTTP gateway. Airnode queried the API provider and sent back a response. All of this was performed without accessing the blockchain.
Learn more about GCP resources that Airnode uses in the Cloud Resources doc.
FLEX_END_TAG