Skip to content
Submit bugs here.

Please submit any typos you come across to GitHub issues.


Once Horizon is installed, you are ready to configure it. Note that Horizon fulfills three important, distinct roles:

  • serving requests like a regular web-based API,
  • ingesting ledgers from the Stellar network to keep its world-view up to date, and
  • transaction submission for interacting with the Stellar network.

Though we encourage operators to separate these responsibilities across instances for resilience and independent scaling, a single Horizon instance can perform all of these functions at once.

For the remainder of this guide, we will assume that you want a single, standalone Horizon instance that performs ingestion and allows transaction submission. We’ll cover ingestion in detail later if you want to read ahead and decide which approach is right for you.


Horizon can be configured by both command line flags and environment variables. To see Horizon’s list of available command line flags, their default values, and their corresponding environmental variable names, run:

stellar-horizon --help

You’ll see that Horizon defines a large number of flags; however, only a handful are required to get started:

  • The most important parameter, --db-url, specifies the Horizon database; its value should be a valid PostgreSQL Connection URI.
  • The other parameter, --history-archive-urls, specifies a set of comma-separated locations from which Horizon should download history archives.

With Ingestion

As outlined at the beginning, we presume you are interested in starting an ingesting instance. For this, you need to specify some additional flags:


Note that ingestion is enabled by default.

  • The first parameter, --captive-core-config-path, points to a Captive Core configuration file. This TOML file only requires a few fields (explained below) to get up and running.
  • The second parameter, --stellar-core-binary-path, is a filesystem path to a Stellar Core binary. Horizon will actually search your PATH for stellar-core by default, so if your environment is configured appropriately, you don’t need to pass this.
  • The third parameter, --captive-core-use-db, by default this value is false, which means Captive Core ingestion will run with ledger states stored in RAM. When set to true, enables Captive Core ingestion to store ledger states in local SQLite database rather than in memory (RAM). As of this writing, ledger states require approximately 8GB, but this will continue to increase as the ledger grows over time. The database location is determined by the DATABASE parameter within the --captive-core-config-path file. By default, it is set to sqlite3://stellar.db, which resolves to runtime directory location derived from --captive-core-storage-path.

Without Ingestion

If you aren’t configuring your Horizon instance to perform ingestion, it still needs awareness about what’s going on in the Stellar network to be useful. Thus, you need to point Horizon to a running Stellar Core instance:


This can be a Remote Captive Core instance with its underlying Core node exposed or a standalone Stellar-Core instance.

Manual Installation

Specifying command line flags every time you invoke Horizon can be cumbersome, so we recommend using environment variables. There are many tools you can use to manage them, such as direnv or dotenv.

For configuration related to Captive Core, you should prepare a separate TOML file and pass it to the --captive-core-config-path/CAPTIVE_CORE_CONFIG_PATH argument.

Package Manager Installation

If you installed Horizon via your package manager, the provided stellar-horizon-cmd wrapper will import a configuration from /etc/default/stellar-horizon and set up the environment accordingly. Hence, if you want to change things, edit the configuration file in /etc/default/stellar-horizon.

This script invokes Horizon with the stellar user, so make sure that permissions for this user are set up accordingly. For example: the --captive-core-storage-path (by default the current working directory) should be writable for this user; the user should be able to execute the horizon and stellar-core binaries; etc.

Note that the default configuration (located at /etc/default/stellar-horizon) provided by the package manager enables ingestion by default. Again, refer to the later Ingestion page to see what setup is right for you. If you want certain nodes dedicated exclusively to fulfilling requests, you should set this flag to false accordingly.

Preparing the Database

Before running the Horizon server, you must first prepare the Horizon database specified by the DATABASE_URL. This database will be used for all of the information produced by Horizon, most notably historical information about transactions that have occurred on the Stellar network.

To prepare a database for Horizon’s use, you must first ensure it is blank. It’s easiest to create a new database on your PostgreSQL server specifically for Horizon’s use (e.g. createdb horizon). Note that you may need to add a role for yourself (or the stellar user) through the postgres user if you’re starting from scratch. Next, install the schema by running stellar-horizon db init. This command will log any errors that occur.

Remember to update the appropriate DB-related flags or environment variables to configure Horizon as explained above.

Postgres Configuration

It is recommended to set random_page_cost=1 in Postgres’ configuration if you are using SSD storage. With this setting, Query Planner will make a better use of indices, especially for JOIN queries. We’ve noticed a huge speed improvement for some queries with this setting.

To improve availability of both ingestion and frontend servers it’s recommended to set the following values:

  • tcp_keepalives_idle: 10 seconds
  • tcp_keepalives_interval: 1 second
  • tcp_keepalives_count: 5

With the config above, if there are no queries from a given client for 10 seconds, Postgres should start sending TCP keepalive packets. It will retry 5 times every second. If there is no response from the client after that time it will drop the connection.

Configuring Captive Core

While a full Stellar Core node requires a complex configuration with lots of possible fields, the Captive Core configuration file can be kept extremely barebones. Most of the configuration will be generated automagically at runtime. Here’s is a minimal working example, operating under the assumption that you want to connect to the testnet and trust SDF’s validators exclusively:


HISTORY="curl -sf{0} -o {1}"

HISTORY="curl -sf{0} -o {1}"

HISTORY="curl -sf{0} -o {1}"

(For the remainder of this guide, we’ll assume this file lives at /etc/default/stellar-captive-core.toml.)

The minimum required fields are the [[HOME_DOMAINS]] and a set of [[VALIDATORS]].

If you wanted to adapt this and configure your nodes to work on the Stellar pubnet, you’ll need to think more carefully about the validators you want to trust in your quorum. As inspiration, here is the set of domains and validators that SDF includes in its pubnet quorum. You should also familiarize yourself with how to configure a proper quorum set; the Core documentation has more on this.

Captive Core’s functionality is controlled through this file. Note that while the Captive Core configuration looks like a subset of a traditional Stellar Core configuration file, you cannot use a traditional Stellar Core configuration file to configure Captive Core. The TOML format is preserved for operator ease of migrating from Horizon 1.x, but this is a fundamentally different architecture and should be treated as such.

Remote Captive Core

As mentioned earlier, we provide a library that wraps a Captive Core instance in an HTTP API that Horizon can consume remotely. This is an advanced (and still experimental) architecture, so refer to the Remote Captive Core page (up next) for more on setting that up.

Otherwise, jump ahead to Running Horizon!

Last updated May. 27, 2022

Page Outline