> For the complete documentation index, see [llms.txt](https://docs.sia.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sia.tech/store-your-data/setting-up-indexd/docker.md).

# Docker

This guide will walk you through setting up `indexd` using Docker Compose. This is the **recommended** way to self-host: Compose runs `indexd`, PostgreSQL, and a Caddy HTTPS reverse proxy together.

{% hint style="success" %}
If you just want to store files, you don't need to run `indexd` at all — [Sia Storage](https://sia.storage) gives you 50 GB free with nothing to run.
{% endhint %}

***

## Pre-requisites

* **Software Requirements:** Before installing `indexd`, you will need to install [Docker](https://www.docker.com/get-started/).
* **Hardware Requirements:** A stable setup that meets the following specifications is recommended.
  * A quad-core CPU
  * 8GB of RAM
  * 256 GB SSD for `indexd` and its PostgreSQL database
* **Network Access:** `indexd` interacts with the Sia network, so you need a stable internet connection and open network access to connect to the Sia blockchain.
* A domain to use for the public App API

{% hint style="info" %}
PostgreSQL and Caddy are provided by the Compose file below, so you do **not** need to install them separately.
{% endhint %}

## Create the compose file

Create a new file named `docker-compose.yml`. You can use the following as a template. PostgreSQL stores the `indexd` index, and Caddy exposes the application API over HTTPS.

```yml
services:
  caddy:
    depends_on:
      - indexd
    image: caddy:2
    restart: unless-stopped
    environment:
      INDEXD_DOMAIN: ${INDEXD_DOMAIN}
    ports:
      - 80:80/tcp
      - 443:443/tcp
      - 443:443/udp
    volumes:
      - ./caddy:/etc/caddy:ro
      - caddy-data:/data
      - caddy-config:/config

  postgres:
    image: postgres:18
    restart: unless-stopped
    shm_size: 128mb
    environment:
      POSTGRES_USER: indexd
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: indexd
    volumes:
      - postgres:/var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      timeout: 5s
      retries: 10
      start_period: 10s

  indexd:
    depends_on:
      postgres:
        condition: service_healthy
    image: ghcr.io/siafoundation/indexd:latest
    restart: unless-stopped
    ports:
      - 127.0.0.1:9980:9980/tcp # admin UI and API (kept local)
      - 9981:9981/tcp # public syncer
    expose:
      - 9982/tcp # application API (available only to Caddy)
    volumes:
      - indexd-data:/data

volumes:
  caddy-data:
  caddy-config:
  indexd-data:
  postgres:
```

{% hint style="warning" %}
Port `9980` is bound to `127.0.0.1` for the private admin UI and API. Port `9982` is not published on the host; only Caddy can reach it through the Compose network. Applications connect to Caddy on port `443`. Port `9981` should be publicly reachable by the Sia network.
{% endhint %}

### Create the Caddyfile

Create a directory named `caddy` beside `docker-compose.yml`, then create `caddy/Caddyfile` with the following contents:

```caddyfile
{$INDEXD_DOMAIN} {
  reverse_proxy indexd:9982
}
```

Caddy uses the domain from `.env`, obtains and renews its HTTPS certificate, and forwards application API requests to `indexd` over the private Compose network. Before starting the stack, point the domain's DNS records to this server and allow inbound TCP ports `80` and `443`. UDP port `443` enables HTTP/3.

## Set the environment variables

Create a file named `.env` in the same directory as your `docker-compose.yml`. Set the PostgreSQL password and the public domain Caddy will use for the application API:

```sh
POSTGRES_PASSWORD=your-secure-database-password
INDEXD_DOMAIN=indexd.example.com
```

You will enter the same database password during the `indexd` configuration step. For the application API advertise URL, enter the domain as a complete HTTPS URL, such as `https://indexd.example.com`.

## Get the container images

Download the container images:

```console
docker compose pull
```

![](/files/q1hgwIZJtRHx83vT2W77)

## Generate a recovery phrase

`indexd` uses a wallet to pay for storage contracts. If you don't already have a recovery phrase, generate one:

```console
docker compose run --rm indexd seed
```

{% hint style="warning" %}
Write your recovery phrase down and store it somewhere safe. It controls the wallet that funds your contracts and cannot be recovered if lost.
{% endhint %}

## Configuring `indexd`

Now launch the interactive configuration wizard:

```console
docker compose run --rm -it indexd config
```

The wizard will ask you for:

* Your **wallet recovery phrase** (use the one you generated above).
* An **admin password** used to unlock the `indexd` admin UI.
* A **database password** — enter the same value you set in `.env`.
* An **application API advertise URL** — the exact base URL applications will use to reach this indexer, described below.

When asked whether to configure **advanced settings**, answer `yes` and set the database connection so `indexd` can reach the PostgreSQL container:

| Setting          | Value           |
| ---------------- | --------------- |
| Database address | `postgres:5432` |
| Database user    | `indexd`        |
| Database name    | `indexd`        |
| SSL mode         | `disable`       |

![](/files/0iFbyWYLFdSRKxnRGS2b)

### Choose the application API advertise URL

The advertise URL is not the address `indexd` listens on. It is the external base URL that an application uses to reach the application API. `indexd` puts this URL into the application-approval flow and uses its hostname when verifying signed requests, so an incorrect value can break application authentication even when `indexd` itself is running.

For an indexer reached through an HTTPS reverse proxy, a typical configuration is:

```yml
applicationAPI:
  address: :9982
  advertiseURL: https://indexd.example.com
```

In this example, `indexd` listens on port `9982`, while applications connect to `https://indexd.example.com`. The reverse proxy terminates HTTPS and forwards requests to port `9982`.

The advertise URL must:

* Include the scheme, such as `https://`.
* Use the public hostname and port, if a non-standard port is required, that applications can actually reach.
* Use `https://` when a reverse proxy provides HTTPS, even if the proxy connects to `indexd` over HTTP.
* Be the same base URL entered in the application.
* Omit a trailing slash and API endpoint paths such as `/api` or `/auth/connect`.

Do not use `0.0.0.0`, a Docker service name, or another internal address. Use `localhost` or `127.0.0.1` only when the application runs on the same machine as `indexd`; on a phone or another computer, `localhost` refers to that device instead of the indexer.

{% hint style="warning" %}
The admin UI URL is not the advertise URL. Port `9980` serves the private admin API; applications connect to the application API on port `9982` or its HTTPS reverse-proxy URL.
{% endhint %}

## Running `indexd`

Now that you have `indexd` configured, start the services:

```console
docker compose up -d
```

![](/files/XqyAHdzzrGvSSSL31OhC)

Once `indexd` has started, you can access the admin UI by opening your browser and going to [http://localhost:9980](http://localhost:9980/).

![](/files/twAkfdncSz4izlHG23zm)

{% hint style="success" %}
`indexd` is now set up.
{% endhint %}

### Verify the advertise URL

From the device or network where the application will run, test the advertise URL you configured:

```console
curl -i https://indexd.example.com/auth/check
```

An unsigned request should reach `indexd` and return `401 Unauthorized` with a message about missing query parameters. A timeout, certificate error, or proxy error means the public URL is not ready.

To correct the URL, run the configuration wizard again, choose to change the existing value, and restart `indexd`:

```console
docker compose run --rm -it indexd config
docker compose restart indexd
```

## Fund your wallet

`indexd` uses a built-in wallet to pay storage providers for the contracts it forms on your behalf, so it needs Siacoin (SC) before it can store any data. After signing in, the **Welcome to Sia** checklist in the admin UI walks you through the remaining setup, including funding your wallet.

Open the **Wallet** section of the UI, copy your receiving address, and send Siacoin to it from an exchange or another wallet. See [Transferring Siacoins](/store-your-data/setting-up-indexd/transferring-siacoins.md) for the full send and receive flow.

Once your wallet shows a confirmed balance, `indexd` can begin forming contracts and storing data.

## Checking the container status

To check the status of the containers run:

```console
docker compose ps
```

## Checking the logs

To check the `indexd` logs run:

```console
docker compose logs indexd
```

## Upgrading `indexd`

New versions of `indexd` are released regularly and contain bug fixes and performance improvements. To upgrade to the newest version, run the following:

```console
docker compose pull && docker compose up -d
```

{% hint style="success" %}
`indexd` is now updated to the latest version.
{% endhint %}

## Next steps

Configure [backups and recovery](/store-your-data/setting-up-indexd/operations.md), then connect an [application](/store-your-data/setting-up-indexd/connect-application.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.sia.tech/store-your-data/setting-up-indexd/docker.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
