---
title: Configuration Reference
description:
  Environment variables, service inventory, and port assignments for all Private
  Channels services
---

## Services

Running a Private Channels instance means owning the following Docker Compose
services:

| Container                                  | Role                                                                    |
| ------------------------------------------ | ----------------------------------------------------------------------- |
| `private-channel-postgres-primary`         | Write node state database (PostgreSQL 16)                               |
| `private-channel-postgres-replica`         | Read node state database (streaming replica)                            |
| `private-channel-postgres-indexer`         | Indexer and operator database                                           |
| `private-channel-write-node`               | Receives and executes transactions (port 8900, loopback-only)           |
| `private-channel-read-node`                | Handles RPC read queries (port 8901, loopback-only)                     |
| `private-channel-gateway`                  | Routes requests between write and read nodes; enforces RBAC (port 8899) |
| `private-channel-indexer-solana`           | Watches Solana for `Deposit` events via Yellowstone gRPC                |
| `private-channel-indexer-private-channel`  | Watches the channel for `Burn` events via RPC polling                   |
| `private-channel-operator-solana`          | Mints channel tokens when deposits are confirmed                        |
| `private-channel-operator-private-channel` | Calls `ReleaseFunds` on Mainnet when withdrawals are confirmed          |
| `private-channel-auth`                     | Optional JWT/RBAC service (Docker profile: `auth`)                      |

Observability containers (`prometheus`, `grafana`, `cadvisor`) are also
included.

## Gateway

These have sensible defaults for the devnet Docker Compose setup. Override only
if running the gateway outside Docker or deploying to production.

| Env Var                         | Default                                                             | Required            | Description                                        |
| ------------------------------- | ------------------------------------------------------------------- | ------------------- | -------------------------------------------------- |
| `GATEWAY_PORT`                  | `8899` (devnet)                                                     | No                  | Listen port                                        |
| `GATEWAY_WRITE_URL`             | `http://write-node:8900` (devnet env file value; no binary default) | **Yes**             | Write node URL - `sendTransaction` routes here     |
| `GATEWAY_READ_URL`              | `http://read-node:8901` (devnet env file value; no binary default)  | **Yes**             | Read node URL - all other RPC methods route here   |
| `GATEWAY_CORS_ALLOWED_ORIGIN`   | `*`                                                                 | No                  | `Access-Control-Allow-Origin` header value         |
| `JWT_SECRET`                    | -                                                                   | No                  | Enables RBAC; must match auth service `JWT_SECRET` |
| `AUTH_DATABASE_URL`             | -                                                                   | If `JWT_SECRET` set | Auth DB for wallet ownership checks                |
| `AUTH_DATABASE_MAX_CONNECTIONS` | `10`                                                                | No                  | Auth DB connection pool size                       |
| `METRICS_PORT`                  | `9101`                                                              | No                  | Prometheus metrics port                            |
| `RUST_LOG`                      | `info`                                                              | No                  | Log level                                          |

> `GATEWAY_WRITE_URL` and `GATEWAY_READ_URL` use Docker Compose service
> hostnames (`write-node`, `read-node`) when running in Docker. Use `localhost`
> equivalents only when running the gateway binary directly outside Docker.
>
> The gateway binary's own built-in default for `GATEWAY_PORT` is `8898`.
> `.env.example` and `.env.devnet` both set it to `8899`, which is what the
> Docker Compose setup actually uses (`versions.env` holds only toolchain and
> image version pins, not service ports). The `8898` default only applies if you
> run the binary directly with no env file.

<Callout type="warning">
  `GATEWAY_CORS_ALLOWED_ORIGIN` defaults to `*`, which allows any origin to call
  the gateway from a browser. Restrict this to your application's actual origin
  before any production deployment.
</Callout>

## Write Node and Read Node

The full write node and read node configuration reference is in
[`docs/CONFIG.md`](https://github.com/solana-foundation/solana-private-channels/blob/main/docs/CONFIG.md)
in the repository.

## Auth Service

| Env Var                         | Required            | Description                                                                                                                  |
| ------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `JWT_SECRET`                    | **Yes**             | HS256 signing secret; must match the gateway's `JWT_SECRET`                                                                  |
| `AUTH_PORT`                     | No (default `8903`) | Listen port                                                                                                                  |
| `AUTH_DATABASE_URL`             | **Yes**             | PostgreSQL connection string for the auth database                                                                           |
| `CORS_ALLOWED_ORIGIN`           | No (default `*`)    | `Access-Control-Allow-Origin` header value for this service (independent of the gateway's own `GATEWAY_CORS_ALLOWED_ORIGIN`) |
| `AUTH_DATABASE_MAX_CONNECTIONS` | No (default `10`)   | Auth DB connection pool size                                                                                                 |

## Service Port Reference

| Service          | Host Port  | Binding                                                                           |
| ---------------- | ---------- | --------------------------------------------------------------------------------- |
| Gateway          | 8899       | All interfaces                                                                    |
| Write node       | 8900       | Loopback only                                                                     |
| Read node        | 8901       | Loopback only                                                                     |
| Streamer         | 8902       | Internal only (`expose`, not published to the host); not part of the devnet stack |
| Auth service     | 8903       | All interfaces                                                                    |
| Operator metrics | 9102, 9103 | All interfaces                                                                    |
| Grafana          | 37429      | All interfaces                                                                    |
| Prometheus       | 9090       | All interfaces                                                                    |
| cAdvisor         | 8080       | All interfaces                                                                    |

> **Security note:** Write-node and read-node ports are bound to loopback
> (`127.0.0.1`) only. Gateway, Auth service, operator metrics, Grafana,
> Prometheus, and cAdvisor are all published to every network interface in the
> devnet Docker Compose setup. Firewall these at the host or network level for
> anything beyond local development; RBAC only covers the gateway's own JSON-RPC
> methods, not these other services. The Streamer is the exception: it is an
> unauthenticated internal feed that Docker never publishes to the host.

## Network Requirements

See
[`docs/TECHNICAL_REQUIREMENTS.md`](https://github.com/solana-foundation/solana-private-channels/blob/main/docs/TECHNICAL_REQUIREMENTS.md)
in the repository for firewall rules and bandwidth requirements.
