This example creates an Ethereum Mainnet RPC endpoint with the Quicknode provider. Terraform and OpenTofu use the same .tf files. Select your CLI in the command tabs below. For a map of supported resources, see Infrastructure as Code.
You need an account that can create endpoints, an Admin API key, and Terraform or OpenTofu installed. Creating an endpoint can incur usage or plan charges. Use a new label so the endpoint is easy to identify and remove after testing.
To get a key, sign in to the Quicknode Dashboard, open the account menu at the bottom left, and select API Keys. Click Add API Key, choose the Admin role and enable Admin API access, then create and copy the key.
Create and call an endpoint
Export the API key in your shell, then save the following as main.tf in an empty directory. See the quicknode_endpoint resource reference for its available arguments.
export QUICKNODE_API_KEY="your-api-key"
terraform {
required_providers {
quicknode = {
source = "quicknode/quicknode"
version = "~> 0.4.0"
}
}
}
provider "quicknode" {}
variable "endpoint_label" {
description = "A label for the example endpoint."
type = string
default = "iac-docs-ethereum"
}
resource "quicknode_endpoint" "example" {
chain = "eth"
network = "mainnet"
multichain = false
status = "active"
label = var.endpoint_label
}
data "quicknode_endpoint_urls" "example" {
endpoint_id = quicknode_endpoint.example.id
}
output "endpoint_id" {
value = quicknode_endpoint.example.id
}
output "rpc_url_redacted" {
value = quicknode_endpoint.example.safe_http_url
}
output "rpc_url" {
value = data.quicknode_endpoint_urls.example.http_url_with_token
sensitive = true
}
- Terraform
- OpenTofu
terraform init
terraform fmt -check
terraform validate
terraform plan
terraform apply
terraform output rpc_url_redacted
tofu init
tofu fmt -check
tofu validate
tofu plan
tofu apply
tofu output rpc_url_redacted
The redacted URL contains REPLACE_WITH_TOKEN. The working URL is marked sensitive and is omitted from ordinary output. To verify the endpoint, read it into a shell variable and make one JSON-RPC call:
- Terraform
- OpenTofu
RPC_URL="$(terraform output -raw rpc_url)"
curl -sS "$RPC_URL" \
-H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
unset RPC_URL
RPC_URL="$(tofu output -raw rpc_url)"
curl -sS "$RPC_URL" \
-H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
unset RPC_URL
A successful response has a hexadecimal block number in result. Treat the URL as a credential: do not paste it into logs, screenshots, or a repository. Protect the Terraform state as well, because it can contain token-bearing URLs.
Add JWT authentication
quicknode_endpoint_jwt registers a public signing key; it does not create or sign caller tokens. Keep the private key with the application that signs JWTs. This example uses RS256 and a short-lived token, as in the Quicknode JWT guide.
First, generate a key pair locally. Do not commit signer.private.pem:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out signer.private.pem
openssl pkey -in signer.private.pem -pubout -out signer.pub.pem
Add the following as jwt.tf beside main.tf, then rerun plan and apply with your selected CLI. This first apply registers the key while the endpoint's original token still works.
resource "quicknode_endpoint_jwt" "signer" {
endpoint_id = quicknode_endpoint.example.id
name = "docs-signer"
kid = "docs-signer-1"
public_key = file("${path.module}/signer.pub.pem")
}
Once the key is registered, add this block to quicknode_endpoint.example in main.tf and apply again:
security_options = {
jwts = true
tokens = false
}
The kid in the caller's JWT header must match docs-signer-1. Save the following as sign-jwt.mjs beside main.tf. It signs a five-minute RS256 token with signer.private.pem using Node.js:
import { readFileSync } from 'node:fs'
import { sign } from 'node:crypto'
const privateKey = readFileSync('signer.private.pem')
const now = Math.floor(Date.now() / 1000)
const header = Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT', kid: 'docs-signer-1' })).toString('base64url')
const payload = Buffer.from(JSON.stringify({ iat: now, exp: now + 300 })).toString('base64url')
const signingInput = `${header}.${payload}`
const signature = sign('RSA-SHA256', Buffer.from(signingInput), privateKey).toString('base64url')
process.stdout.write(`${signingInput}.${signature}\n`)
With token authentication disabled, call the endpoint host without the token path. You can test the authenticated call with:
- Terraform
- OpenTofu
RPC_URL="$(terraform output -raw rpc_url)"
JWT_URL="$(RPC_URL="$RPC_URL" node -p 'new URL(process.env.RPC_URL).origin')"
JWT="$(node sign-jwt.mjs)"
curl -sS "$JWT_URL" \
-H "Authorization: Bearer $JWT" \
-H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
unset RPC_URL JWT_URL JWT
RPC_URL="$(tofu output -raw rpc_url)"
JWT_URL="$(RPC_URL="$RPC_URL" node -p 'new URL(process.env.RPC_URL).origin')"
JWT="$(node sign-jwt.mjs)"
curl -sS "$JWT_URL" \
-H "Authorization: Bearer $JWT" \
-H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
unset RPC_URL JWT_URL JWT
For an endpoint that already serves traffic, register the new JWT key and plan the caller cutover before disabling token authentication. Disabling tokens can interrupt callers still using them. If the first JWT request returns 401 immediately after applying the change, wait a few seconds and try again while the new setting propagates.
Optional: use your own domain
Domain masking also requires a domain you control and a DNS CNAME. Follow the domain masking setup guide for DNS and plan prerequisites. Add the following resource first, replacing the example hostname, and apply it. See the quicknode_endpoint_domain_mask resource reference for its available arguments.
resource "quicknode_endpoint_domain_mask" "rpc" {
endpoint_id = quicknode_endpoint.example.id
domain = "rpc.example.com"
}
After DNS points to the endpoint and you have checked it, add domain_masks = true to the endpoint's security_options block and apply again. Preserve any JWT or token settings you already manage in that block. Adding the domain entry before enabling enforcement avoids changing a serving endpoint too early.
When finished, review the destroy plan and remove the endpoint and its managed security entries:
- Terraform
- OpenTofu
terraform plan -destroy
terraform destroy
tofu plan -destroy
tofu destroy