Standalone production
Use standalone when one Nona instance is enough. For most teams, it is the right production starting point: one self-hosted Nona instance, the simplest deployment model, one Docker-first service with persistent local data, and a straightforward place to start before introducing replication.
The simplest standalone deployment is one Docker container:
docker run -d \ --name nona \ --restart unless-stopped \ -p 18080:8080 \ -v nona-data:/var/lib/nona \ rywaredev/nona:latestThe API and admin UI are exposed on:
http://localhost:18080Compose example
Section titled “Compose example”If you want the repo’s compose example for the same standalone image:
docker compose -f deploy/compose/standalone-prod.yml up -dCompose file:
deploy/compose/standalone-prod.ymlWhen standalone is the right choice
Section titled “When standalone is the right choice”Choose standalone when:
- one instance is operationally sufficient
- your traffic profile does not require replica reads
- simplicity matters more than distributed read scaling
- you want to validate the product in production without extra topology
For many teams, standalone will stay the long-term deployment shape, not just the first step.
Configure the API port
Section titled “Configure the API port”The container listens on port 8080.
With plain Docker:
docker run -d \ --name nona \ --restart unless-stopped \ -p 8088:8080 \ -v nona-data:/var/lib/nona \ rywaredev/nona:latestWith Compose, NONA_API_PORT controls the host port:
NONA_API_PORT=8088 docker compose -f deploy/compose/standalone-prod.yml up -dWith that value, the API is exposed on:
http://localhost:8088Persistent data
Section titled “Persistent data”Mount a persistent volume at:
/var/lib/nonaKeep this volume when upgrading the container.
The mounted volume is what makes the deployment durable across restarts and upgrades, so it should be treated as production data.
JWT settings
Section titled “JWT settings”By default, Nona can generate and persist JWT settings. To pin them, pass the same values every time the container starts.
Example with plain Docker:
docker run -d \ --name nona \ --restart unless-stopped \ -p 18080:8080 \ -v nona-data:/var/lib/nona \ -e Jwt__Key=<your-secret-key> \ -e Jwt__Issuer=nona \ -e Jwt__Audience=nona \ rywaredev/nona:latestEquivalent Compose environment block:
environment: Jwt__Key: ${NONA_JWT_KEY:?set NONA_JWT_KEY} Jwt__Issuer: ${NONA_JWT_ISSUER:-nona} Jwt__Audience: ${NONA_JWT_AUDIENCE:-nona}Set NONA_JWT_KEY from your production secret store or .env file.
Operate
Section titled “Operate”docker psdocker logs -f nonadocker stop nonaIf you are using Compose instead:
docker compose -f deploy/compose/standalone-prod.yml psdocker compose -f deploy/compose/standalone-prod.yml logs -f nonadocker compose -f deploy/compose/standalone-prod.yml downIs standalone only for testing?
Section titled “Is standalone only for testing?”No.
For many teams, standalone is not only the first production step. It remains the long-term deployment shape.
What is the most important thing to preserve in standalone mode?
Section titled “What is the most important thing to preserve in standalone mode?”The persistent data mounted at /var/lib/nona.
That is the durable state you need to keep across restarts and upgrades.
Should I pin JWT settings in production?
Section titled “Should I pin JWT settings in production?”Usually yes, if you want the deployment to be easier to reason about operationally.
When should I leave standalone and move to replica mode?
Section titled “When should I leave standalone and move to replica mode?”Only when you already know that read-heavy traffic and operational requirements justify the added complexity.