Running an API node

Set up and run a local API node with Docker

Introduction

This procedure demonstrates how to run a local API node using Docker images. There are several components that must be configured and run in a specific order for the local API node to work.

For this procedure, the order in which the services are brought up is very important. In order to start the API node successfully, you need to bring up the services in the following order:

  1. postgres
  2. stacks-blockchain-api
  3. stacks-blockchain

When bringing down the API node, you should bring the services down in the exact reverse order in which they were brought up, to avoid losing data.

Prerequisites

Running a node has no specialized hardware requirements. Users have been successful in running nodes on Raspberry Pi boards and other system-on-chip architectures. In order to complete this procedure, you must have the following software installed on the node host machine:

Firewall configuration

In order for the API node services to work correctly, you must configure any network firewall rules to allow traffic on the ports discussed in this section. The details of network and firewall configuration are highly specific to your machine and network, so a detailed example isn't provided.

The following ports must open on the host machine:

Ingress:

  • postgres (open to localhost only):
    • 5432 TCP
  • stacks-blockchain-api
    • 3999 TCP
  • stacks-blockchain (open to 0.0.0.0/0):
    • 20443 TCP
    • 20444 TCP

Egress:

  • 8332
  • 8333
  • 20443-20444

These egress ports are for syncing stacks-blockchain and Bitcoin headers. If they're not open, the sync will fail.

Step 1: initial setup

In order to run the API node, you must download the Docker images and create a directory structure to hold the persistent data from the services. Download and configure the Docker images with the following commands:

docker pull blockstack/stacks-blockchain-api && docker pull blockstack/stacks-blockchain && docker pull postgres:alpine docker network create stacks-blockchain > /dev/null 2>&1docker pull blockstack/stacks-blockchain-api && docker pull blockstack/stacks-blockchain && docker pull postgres:alpinedocker network create stacks-blockchain > /dev/null 2>&1

Create a directory structure for the service data with the following command:

mkdir -p ./stacks-node/{persistent-data/postgres,persistent-data/stacks-blockchain,bns,config} && cd stacks-nodemkdir -p ./stacks-node/{persistent-data/postgres,persistent-data/stacks-blockchain,bns,config} && cd stacks-node

Step 2: running Postgres

The postgres:alpine Docker container can be run with default settings. You must set the password for the user to postgres with the POSTGRES_PASSWORD environment variable. The following command starts the image:

docker run -d --rm \ --name postgres \ --net=stacks-blockchain \ -e POSTGRES_PASSWORD=postgres \ -v $(pwd)/persistent-data/postgres:/var/lib/postgresql/data \ -p 5432:5432 postgres:alpinedocker run -d --rm \ --name postgres \ --net=stacks-blockchain \ -e POSTGRES_PASSWORD=postgres \ -v $(pwd)/persistent-data/postgres:/var/lib/postgresql/data \ -p 5432:5432 postgres:alpine

You can verify the running Postgres instance on port 5432 with the command

docker ps --filter name=postgresdocker ps --filter name=postgres

Step 3: running Stacks blockchain API

The stacks-blockchain-api image requires several environment variables to be set. To reduce the complexity of the run command, you should create a new .env file and add the following to it using a text editor:

NODE_ENV=production GIT_TAG=master PG_HOST=postgres PG_PORT=5432 PG_USER=postgres PG_PASSWORD=postgres PG_DATABASE=postgres STACKS_CHAIN_ID=0x00000001 V2_POX_MIN_AMOUNT_USTX=90000000260 STACKS_CORE_EVENT_PORT=3700 STACKS_CORE_EVENT_HOST=0.0.0.0 STACKS_BLOCKCHAIN_API_PORT=3999 STACKS_BLOCKCHAIN_API_HOST=0.0.0.0 STACKS_BLOCKCHAIN_API_DB=pg STACKS_CORE_RPC_HOST=stacks-blockchain STACKS_CORE_RPC_PORT=20443 BNS_IMPORT_DIR=/bns-dataNODE_ENV=productionGIT_TAG=masterPG_HOST=postgresPG_PORT=5432PG_USER=postgresPG_PASSWORD=postgresPG_DATABASE=postgresSTACKS_CHAIN_ID=0x00000001V2_POX_MIN_AMOUNT_USTX=90000000260STACKS_CORE_EVENT_PORT=3700STACKS_CORE_EVENT_HOST=0.0.0.0STACKS_BLOCKCHAIN_API_PORT=3999STACKS_BLOCKCHAIN_API_HOST=0.0.0.0STACKS_BLOCKCHAIN_API_DB=pgSTACKS_CORE_RPC_HOST=stacks-blockchainSTACKS_CORE_RPC_PORT=20443BNS_IMPORT_DIR=/bns-data

The PG_HOST and STACKS_CORE_RPC_HOST variables define the container names for postgres and stacks-blockchain. You may wish to alter those values if you have named those containers differently than this guide.

Start the stacks-blockchain-api image with the following command:

docker run -d --rm \ --name stacks-blockchain-api \ --net=stacks-blockchain \ --env-file $(pwd)/.env \ -v $(pwd)/bns:/bns-data \ -p 3700:3700 \ -p 3999:3999 \ blockstack/stacks-blockchain-apidocker run -d --rm \ --name stacks-blockchain-api \ --net=stacks-blockchain \ --env-file $(pwd)/.env \ -v $(pwd)/bns:/bns-data \ -p 3700:3700 \ -p 3999:3999 \ blockstack/stacks-blockchain-api

You can verify the running stacks-blockchain-api container with the command:

docker ps --filter name=stacks-blockchain-apidocker ps --filter name=stacks-blockchain-api

Step 4: running Stacks blockchain

In order for the API to be functional, the stacks-blockchain-api container must have data from a running stacks-blockchain instance. First create the ./config/Config.toml file and add the following content to the file using a text editor:

[node] working_dir = "/root/stacks-node/data" rpc_bind = "0.0.0.0:20443" p2p_bind = "0.0.0.0:20444" bootstrap_node = "02da7a464ac770ae8337a343670778b93410f2f3fef6bea98dd1c3e9224459d36b@seed-0.mainnet.stacks.co:20444,02afeae522aab5f8c99a00ddf75fbcb4a641e052dd48836408d9cf437344b63516@seed-1.mainnet.stacks.co:20444,03652212ea76be0ed4cd83a25c06e57819993029a7b9999f7d63c36340b34a4e62@seed-2.mainnet.stacks.co:20444" wait_time_for_microblocks = 10000 [[events_observer]] endpoint = "stacks-blockchain-api:3700" retry_count = 255 events_keys = ["*"] [burnchain] chain = "bitcoin" mode = "mainnet" peer_host = "bitcoin.blockstack.com" username = "blockstack" password = "blockstacksystem" rpc_port = 8332 peer_port = 8333 [connection_options] read_only_call_limit_write_length = 0 read_only_call_limit_read_length = 100000 read_only_call_limit_write_count = 0 read_only_call_limit_read_count = 30 read_only_call_limit_runtime = 1000000000[node]working_dir = "/root/stacks-node/data"rpc_bind = "0.0.0.0:20443"p2p_bind = "0.0.0.0:20444"bootstrap_node = "02da7a464ac770ae8337a343670778b93410f2f3fef6bea98dd1c3e9224459d36b@seed-0.mainnet.stacks.co:20444,02afeae522aab5f8c99a00ddf75fbcb4a641e052dd48836408d9cf437344b63516@seed-1.mainnet.stacks.co:20444,03652212ea76be0ed4cd83a25c06e57819993029a7b9999f7d63c36340b34a4e62@seed-2.mainnet.stacks.co:20444"wait_time_for_microblocks = 10000[[events_observer]]endpoint = "stacks-blockchain-api:3700"retry_count = 255events_keys = ["*"][burnchain]chain = "bitcoin"mode = "mainnet"peer_host = "bitcoin.blockstack.com"username = "blockstack"password = "blockstacksystem"rpc_port = 8332peer_port = 8333[connection_options]read_only_call_limit_write_length = 0read_only_call_limit_read_length = 100000read_only_call_limit_write_count = 0read_only_call_limit_read_count = 30read_only_call_limit_runtime = 1000000000

The [[events_observer]] block configures the instance to send blockchain events to the API container that you started previously.

Start the stacks-blockchain container with the following command:

docker run -d --rm \ --name stacks-blockchain \ --net=stacks-blockchain \ -v $(pwd)/persistent-data/stacks-blockchain:/root/stacks-node/data \ -v $(pwd)/config:/src/stacks-node \ -p 20443:20443 \ -p 20444:20444 \ blockstack/stacks-blockchain \ /bin/stacks-node start --config /src/stacks-node/Config.tomldocker run -d --rm \ --name stacks-blockchain \ --net=stacks-blockchain \ -v $(pwd)/persistent-data/stacks-blockchain:/root/stacks-node/data \ -v $(pwd)/config:/src/stacks-node \ -p 20443:20443 \ -p 20444:20444 \ blockstack/stacks-blockchain \/bin/stacks-node start --config /src/stacks-node/Config.toml

You can verify the running stacks-blockchain container with the command:

docker ps --filter name=stacks-blockchaindocker ps --filter name=stacks-blockchain

Step 5: verifying the services

You can now verify that each of the services is running and talking to the others.

To verify the database is ready:

  1. Connect to the Postgres instance with the command psql -h localhost -U postgres. Use the password from the POSTGRES_PASSWORD environment variable you set when running the container.
  2. List current databases with the command \l
  3. Disconnect from the database with the command \q

To verify the stacks-blockchain tip height is progressing use the following command:

curl -sL localhost:20443/v2/info | jqcurl -sL localhost:20443/v2/info | jq

If the instance is running you should recieve terminal output similar to the following:

{ "peer_version": 402653184, "pox_consensus": "89d752034e73ed10d3b97e6bcf3cff53367b4166", "burn_block_height": 666143, "stable_pox_consensus": "707f26d9d0d1b4c62881a093c99f9232bc74e744", "stable_burn_block_height": 666136, "server_version": "stacks-node 2.0.11.1.0-rc1 (master:67dccdf, release build, linux [x86_64])", "network_id": 1, "parent_network_id": 3652501241, "stacks_tip_height": 61, "stacks_tip": "e08b2fe3dce36fd6d015c2a839c8eb0885cbe29119c1e2a581f75bc5814bce6f", "stacks_tip_consensus_hash": "ad9f4cb6155a5b4f5dcb719d0f6bee043038bc63", "genesis_chainstate_hash": "74237aa39aa50a83de11a4f53e9d3bb7d43461d1de9873f402e5453ae60bc59b", "unanchored_tip": "74d172df8f8934b468c5b0af2efdefe938e9848772d69bcaeffcfe1d6c6ef041", "unanchored_seq": 0, "exit_at_block_height": null }{ "peer_version": 402653184, "pox_consensus": "89d752034e73ed10d3b97e6bcf3cff53367b4166", "burn_block_height": 666143, "stable_pox_consensus": "707f26d9d0d1b4c62881a093c99f9232bc74e744", "stable_burn_block_height": 666136, "server_version": "stacks-node 2.0.11.1.0-rc1 (master:67dccdf, release build, linux [x86_64])", "network_id": 1, "parent_network_id": 3652501241, "stacks_tip_height": 61, "stacks_tip": "e08b2fe3dce36fd6d015c2a839c8eb0885cbe29119c1e2a581f75bc5814bce6f", "stacks_tip_consensus_hash": "ad9f4cb6155a5b4f5dcb719d0f6bee043038bc63", "genesis_chainstate_hash": "74237aa39aa50a83de11a4f53e9d3bb7d43461d1de9873f402e5453ae60bc59b", "unanchored_tip": "74d172df8f8934b468c5b0af2efdefe938e9848772d69bcaeffcfe1d6c6ef041", "unanchored_seq": 0, "exit_at_block_height": null}

Verify the stacks-blockchain-api is receiving data from the stacks-blockchain with the following command:

curl -sL localhost:3999/v2/info | jqcurl -sL localhost:3999/v2/info | jq

If the instance is configured correctly, you should recieve terminal output similar to the following:

{ "peer_version": 402653184, "pox_consensus": "e472cadc17dcf3bc1afafc6aa595899e55f25b72", "burn_block_height": 666144, "stable_pox_consensus": "6a6fb0aa75a8acd4919f56c9c4c81ce5bc42cac1", "stable_burn_block_height": 666137, "server_version": "stacks-node 2.0.11.1.0-rc1 (master:67dccdf, release build, linux [x86_64])", "network_id": 1, "parent_network_id": 3652501241, "stacks_tip_height": 61, "stacks_tip": "e08b2fe3dce36fd6d015c2a839c8eb0885cbe29119c1e2a581f75bc5814bce6f", "stacks_tip_consensus_hash": "ad9f4cb6155a5b4f5dcb719d0f6bee043038bc63", "genesis_chainstate_hash": "74237aa39aa50a83de11a4f53e9d3bb7d43461d1de9873f402e5453ae60bc59b", "unanchored_tip": "74d172df8f8934b468c5b0af2efdefe938e9848772d69bcaeffcfe1d6c6ef041", "unanchored_seq": 0, "exit_at_block_height": null }{ "peer_version": 402653184, "pox_consensus": "e472cadc17dcf3bc1afafc6aa595899e55f25b72", "burn_block_height": 666144, "stable_pox_consensus": "6a6fb0aa75a8acd4919f56c9c4c81ce5bc42cac1", "stable_burn_block_height": 666137, "server_version": "stacks-node 2.0.11.1.0-rc1 (master:67dccdf, release build, linux [x86_64])", "network_id": 1, "parent_network_id": 3652501241, "stacks_tip_height": 61, "stacks_tip": "e08b2fe3dce36fd6d015c2a839c8eb0885cbe29119c1e2a581f75bc5814bce6f", "stacks_tip_consensus_hash": "ad9f4cb6155a5b4f5dcb719d0f6bee043038bc63", "genesis_chainstate_hash": "74237aa39aa50a83de11a4f53e9d3bb7d43461d1de9873f402e5453ae60bc59b", "unanchored_tip": "74d172df8f8934b468c5b0af2efdefe938e9848772d69bcaeffcfe1d6c6ef041", "unanchored_seq": 0, "exit_at_block_height": null}

Once the API is running, you can use it to interact with other API endpoints.

Stopping the API node

As discussed previously, if you want to bring down your API node, you must stop the services in the reverse order that you started them. Performing the shutdown in this order ensures that you don't lose any data while shutting down the node.

Use the following commands to stop the local API node:

docker stop stacks-blockchain docker stop stacks-blockchain-api docker stop postgresdocker stop stacks-blockchaindocker stop stacks-blockchain-apidocker stop postgres

Additional reading

Previous
Running a regtest node
Next
Stacking using the Stacks CLI
Powered by