Operations
Configuration
How the template is configured: appsettings as development placeholders, environment-variable overrides in production, and the local port map.
On this page
One rule: placeholders in, environment variables out
Configuration follows a single principle. The values committed to appsettings.json are
development placeholders that let you clone and run the stack with docker compose up -d and no
extra setup. Every secret or environment-specific value is overridden by an environment variable
in any real environment.
There is no Key Vault or Secrets Manager scaffolding to adopt. Point your platform’s secret
mechanism (Docker secrets, Kubernetes Secret, your host’s env panel) at environment variables and
you are done. Nothing in the repo needs to change to deploy.
This page covers the API and SPA. The marketing site has its own customization guide: see customising the landing site.
The double-underscore convention
.NET maps environment variables onto the configuration tree by replacing each : with __ (two
underscores). A nested key in appsettings.json becomes a flat environment variable:
{
"ConnectionStrings": { "Postgres": "Host=localhost;..." },
"Cors": { "AllowedOrigins": ["http://localhost:3003"] }
}
ConnectionStrings__Postgres="Host=db;Port=5432;Database=slicekit;Username=...;Password=..."
Cors__AllowedOrigins__0="https://app.example.com"
Array entries are addressed by index (__0, __1). This is the standard ASP.NET Core binding, so
anything you can put in appsettings.json you can override from the environment without touching
code. Strongly-typed settings are bound the same way, see the settings pattern.
Common overrides
The values you will set in production, grouped by concern:
| Concern | Variables |
|---|---|
| Database | ConnectionStrings__Postgres |
| Cache/sessions | ConnectionStrings__Redis |
| Messaging | RabbitMq__Host, RabbitMq__Username, RabbitMq__Password |
| File storage | Storage__Endpoint, Storage__AccessKey, Storage__SecretKey, Storage__Bucket |
| CORS | Cors__AllowedOrigins__0, Cors__AllowedOrigins__1, … |
| OAuth | Authentication__Google__ClientId, Authentication__Google__ClientSecret |
| Telemetry | OTEL_EXPORTER_OTLP_ENDPOINT (the OTLP collector your traces and audit log flow to) |
Storage points at MinIO locally and any S3-compatible bucket in production (see file storage). OAuth client credentials are added per provider (see adding an OAuth provider). Telemetry exporters are plain OTLP, so they target your collector (see observability).
The local port map
docker compose up -d brings up the full local stack. The dev servers and infrastructure use a
fixed set of ports:
| Service | Port | Notes |
|---|---|---|
| Frontend | 3003 | Vite dev server; matches Cors:AllowedOrigins |
| API | 5076 / 5077 | http / https from Kestrel |
| Postgres | 5432 | |
| Redis | 6379 | |
| RabbitMQ | 5672 / 15672 | broker / management UI |
| Mailpit | 1025 / 8025 | SMTP / web UI |
| MinIO | 9000 / 9001 | S3 API / console |
| Grafana | 3010 | dashboards |
| Prometheus | 9090 | |
| Loki | 3100 | logs and the audit trail |
| Tempo | 3200 | traces |
| Alertmanager | 9093 |
The frontend dev server on 3003 is the origin allowed by Cors:AllowedOrigins, so the SPA and API
agree out of the box. If you change the frontend port, update that origin.
Production
In production you run the same images with environment variables supplied by your platform:
docker compose -f docker-compose.prod.yml up -d --build
Terminate TLS at your proxy or load balancer and forward the scheme and client address; the API trusts forwarded headers so cookies, redirects and audited IPs stay correct. See reverse proxy and deployment for the full topology.
Checklist
- Treat
appsettings.jsonas placeholders; never commit a real secret. - Override secrets and environment-specific values with environment variables, using
__for:. - Set
Cors__AllowedOriginsto your real frontend origin(s). - Point
OTEL_EXPORTER_OTLP_ENDPOINTandStorage__*at your infrastructure. - Run migrations as an explicit deploy step (see deployment).