Environment Reference
The configuration variables a self-hosted Solana Developer Platform reads from .env.
The stack reads one .env file next to compose.yml. The configurator writes it, with the defaults and validation of the release you installed. This page covers how the file is organized and the variables you're most likely to set by hand.
The configurator groups the variables into sections:
| Section | What it covers |
|---|---|
| Basic | ENVIRONMENT. The deployment mode is always self_hosted. |
| Database | Bundled Postgres or an external database. |
| Cache | Bundled Redis or an external one. |
| Solana RPC | The devnet and mainnet RPC endpoints. |
| Authentication (Clerk) | Clerk keys, issuer, dashboard URL, and webhook secret. |
| Signing provider | Which signers to enable, the default one, and their credentials. |
| Fee payment | The Kora that sponsors each cluster's transaction fees, if any. |
| Secrets | App secrets, generated where you run the configurator. |
| Advanced | Image source, ports, public URLs, proxy trust, and optional features. |
Required variables
compose.yml refuses to start without these:
| Variable | Description |
|---|---|
POSTGRES_PASSWORD | Password for the bundled Postgres. Required even with an external DATABASE_URL, because the bundled postgres service still starts. |
API_KEY_PEPPER | Server-side pepper for API key hashes. Generate it with openssl rand -hex 32. |
CUSTODY_ENCRYPTION_KEY | Encrypts custody secrets at rest. Generate it with openssl rand -base64 32. |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | Clerk publishable key for the dashboard. |
CLERK_SECRET_KEY | Clerk secret key. |
The configurator also requires these. The stack starts without them, but most leave a core flow broken:
| Variable | Description |
|---|---|
CLERK_ISSUER | Clerk issuer URL. Without it, dashboard sessions can't be verified, so sign-in fails. |
FRONTEND_URL | The dashboard URL the browser loads. With ENVIRONMENT=production, the compose default, the API accepts only Clerk sessions issued to this origin, and won't start if it isn't an http(s) URL. |
SOLANA_RPC_DEFAULT_DEVNET_API_KEY_URL | The devnet endpoint Sandbox projects read and send through. Without it, they get 503 RPC_UNAVAILABLE. |
SIGNING_PROVIDER | The configurator writes local unless you pick another provider. The API reads it only at startup, where local (also its value when unset) requires SDP_DEPLOYMENT_MODE=self_hosted. |
CUSTODY_PRIVATE_KEY | With local signing selected: SDP offers the local signer only while this is set. |
CREDENTIAL_FINGERPRINT_PEPPER | Without it, saving or rotating provider credentials in the dashboard fails. Keep it stable. |
SOLANA_RPC_DEFAULT_MAINNET_API_KEY_URL is optional: without it, SDP refuses Production projects' chain reads and sends.
The configurator generates API_KEY_PEPPER, CREDENTIAL_FINGERPRINT_PEPPER, and CUSTODY_ENCRYPTION_KEY for you, and can generate POSTGRES_PASSWORD.
Database
The configurator's database choice (DATABASE_MODE) shapes the file, but isn't written to it.
- Bundled: set
POSTGRES_DB,POSTGRES_USER, andPOSTGRES_PASSWORD. The API andsdp-migratebuild their connection string from them. - External: set
DATABASE_URLto yourpostgresql://…string, and the API and migrations connect there. The bundledpostgrescontainer still starts, unused, so it still needsPOSTGRES_PASSWORD. The configurator hides that field and generates a value for it.
Cache
The cache choice (CACHE_MODE) isn't written to the file either. For an external cache, set REDIS_URL to your redis://… or rediss://… endpoint. The bundled redis container still starts, unused.
Signing providers
The configurator offers local, fireblocks, privy, coinbase_cdp, para, turnkey, and utila. Its list of providers to enable (SIGNING_PROVIDERS) only decides which credential fields it shows, and isn't written to the file. Each provider other than local needs its own credentials, such as an API key and secret for Fireblocks. Each project picks its signer when you set up custody in the dashboard.
Privy, Coinbase CDP, Para, and Turnkey are open to every organization. The others, local included, also need a per-organization activation: First devnet deployment shows how for the local signer.
For provider co-signers that run outside the stack, see External co-signers.
Fee payment
SDP pays transaction fees through a Kora on each project's cluster, and the stack doesn't bundle one. The configurator's choice of clusters (FEE_SPONSOR_CLUSTERS) isn't written to the file: it shows each chosen cluster's fields. Without a Kora, sends SDP sponsors, such as payment transfers, answer 503 PROVIDER_UNAVAILABLE.
| Variable | Description |
|---|---|
KORA_DEVNET_URL | The Kora that pays fees for Sandbox projects. |
KORA_DEVNET_RPC_URL | The Solana RPC endpoint that Kora reads. SDP prices and settles sponsored fees through it. Set it with KORA_DEVNET_URL: the API won't start with only one of the two. |
KORA_DEVNET_API_KEY | Optional. The API key your Kora requires, if any. |
KORA_DEVNET_CLOUD_RUN_AUDIENCE | Optional. For a private Kora on Cloud Run: SDP sends it a Google identity token for this audience, which needs the API on Google Cloud and the Kora URL on the audience's https origin. |
The KORA_MAINNET_* variables do the same for Production projects.
Advanced
| Variable | Default | Description |
|---|---|---|
SDP_IMAGE_REGISTRY | ghcr.io/solana-foundation/sdp | Registry the images come from. |
SDP_VERSION | latest | Image tag for every SDP service. Pin it to a release tag, such as vX.Y.Z, in production. |
SDP_API_PORT | 8787 | Host port for the API. |
SDP_WEB_PORT | 3000 | Host port for the dashboard. |
SDP_DOCS_PORT | 3001 | Host port for these docs. |
TRUST_PROXY_HEADERS | false | How to read X-Forwarded-For. true trusts the first address: use it only behind an ingress that replaces caller-supplied forwarding headers. gclb is for ingress restricted to a Google Cloud external Application Load Balancer, which appends the verified client address. With false, SDP can't see caller addresses: per-IP limits count every caller as one, and an IP allowlist refuses every request. |
SDP_API_BASE_URL (default http://sdp-api:8787) is how the dashboard's server reaches the API inside the Docker network. The browser-facing NEXT_PUBLIC_* URLs default to localhost, such as http://localhost:8787 for the API and http://localhost:3000 for the dashboard. Change them, and FRONTEND_URL, when you put the services behind your own domains.
compose.yml also reads SDP_RELEASE_CHANNEL, which the configurator doesn't set. It decides which modules and providers the API and dashboard run (experimental, beta, or stable), and defaults to experimental.