Deployment

Five containers, one per service, each configured from the environment.

ServiceDockerfileBuild contextPortPublic URL
Landingapps/landing/Dockerfilerepository root80tsamara.xyz
Appapps/app/Dockerfilerepository root3000app.tsamara.xyz
APIapps/api/Dockerfilerepository root8788api.tsamara.xyz
Indexerapps/indexer/Dockerfileapps/indexer8787indexer.tsamara.xyz
Docsapps/docs/Dockerfileapps/docs3000docs.tsamara.xyz

Configuration

ServiceVariableMeaning
App, at build timeNEXT_PUBLIC_API, NEXT_PUBLIC_INDEXERPublic URLs of the API and the indexer
App, at build timeNEXT_PUBLIC_RPCOptional Solana RPC endpoint; defaults to public devnet
APIISSUER_KEY, COMPLIANCE_KEYOperator keys as JSON byte arrays. Devnet keys only
APISUKUK_MINT, PAYMENT_MINTThe live series to serve
APIADMIN_TOKENRequired by the admin routes
APIINDEXERURL of the indexer, for the holder list
APISUMSUB_TOKEN, SUMSUB_SECRETOptional sandbox credentials
API, IndexerRPCOptional Solana RPC endpoint
IndexerSUKUK_MINT, PAYMENT_MINT, ISSUERThe series to index
DocsNEXT_PUBLIC_SITE_URLOptional canonical origin

The current deployment

The services run on one server under Coolify, built from main with the Dockerfiles above. The server's nginx proxies each hostname to a port published on the host.

Things that are easy to get wrong:

  • The API reaches the indexer over the Docker network, by a network alias. An alias takes effect on a full deploy, not a restart.
  • The app's public URLs are build variables. Changing them means rebuilding it.
  • The indexer answers only after it has caught up, a little after its container starts.
  • The app waits. If the API is unreachable, pages say so and retry every few seconds, so a restart of the server needs no reload from visitors.

The repository's docs/DEPLOY.md has the rest.

On this page