Operations
Deployment
Building the production images, configuration through environment variables, and running the production stack.
On this page
Production stack
A second Compose file, docker-compose.prod.yml, builds the API image and runs the production
topology. The API ships with a Dockerfile; the frontend builds to static assets served by your CDN
or a static host.
docker compose --env-file .env.prod -f docker-compose.prod.yml up -d --build
Configuration is environment variables
Configuration follows a simple rule: the values in appsettings.json are development placeholders,
and every secret or environment-specific value is overridden by an environment variable in production.
There is no Key Vault or Secrets Manager scaffolding to adopt; wire your platform’s secret mechanism
to environment variables and you are done.
Typical overrides:
Database__ConnectionStringCaching__RedisMessaging__RabbitMq__*,Storage__*(SeaweedFS/S3)Cors__AllowedOrigins__0Otel__Endpointfor your collectorAuditing__IpHashSaltwith a random secret
Compose sets the first CORS origin from FRONTEND_BASE_URL and requires AUDIT_IP_HASH_SALT. Fill in
.env.prod before starting the stack. The internal readiness probe checks the API and Postgres after a startup grace period.
The notification settings listed in .env.prod.example are forwarded into the API container too.
Leave unused Notifications__AllowedWebhookHosts__0 and __1 slots unset. Add matching indexed
entries to api.environment in docker-compose.prod.yml if you need more hosts. Adding another
application setting to .env.prod also requires a Compose mapping: --env-file alone does not
forward every variable into the container.
SMTP_REQUIRE_TLS=true (the default) enables STARTTLS for the API’s SMTP client and requires TLS in
Alertmanager. Grafana uses its separate SMTP_STARTTLS policy. Keep these enabled with your production
SMTP provider; the local Mailpit stack still uses plaintext.
Compose sets the API’s Loki__BaseUrl to http://loki:3100 on the internal network. Set GF_ROOT_URL
to the public Grafana HTTPS URL, including any path prefix. It configures both Grafana’s own root URL
and the browser links returned by the admin audit endpoints. The production fallback is
http://localhost:3000, matching the published Grafana port.
Database migrations
EF Core migrations are applied automatically on start in development. For production, run them as an
explicit step in your deploy pipeline so a rollout never races the schema. The included Compose stack deliberately
sets Database__MigrateOnStartup=true because it has no separate migration job. For multi-replica deployments, use
the image migration bundles and disable startup migrations. Run /app/efbundle-dataprotectionkeycontext
first, then /app/efbundle-appdbcontext, with the same configuration as the API container. Gate the
rollout on both succeeding. To apply the application context manually:
/app/efbundle-dataprotectionkeycontext --connection "$Database__ConnectionString"
/app/efbundle-appdbcontext --connection "$Database__ConnectionString"
On Kubernetes with Argo CD, run them in a Job that is a Sync hook in a sync wave after the secret,
database and ConfigMap and before the Deployment. A PreSync hook runs before those resources are
applied, so it misses a first install and any change to a hashed ConfigMap name. With
docker-compose.prod.yml, which has no separate step, set Database__MigrateOnStartup=true instead.
Behind a reverse proxy
Terminate TLS at your proxy or load balancer and forward the scheme and client address. Empty trust arrays preserve loopback-only defaults. Configure the proxy peer IP or proxy-only CIDR explicitly so the API accepts those headers; see reverse proxies. This keeps secure-cookie flags, redirects and audited IP addresses correct.
Production host ports for the API, Grafana and Alertmanager bind to loopback by default. Put the proxy on
the host or Compose network; change the *_BIND_ADDRESS values only when your network supplies the access boundary.
CI
Pull request checks build and test the API, and format, test, lint and build the SPA. Landing changes have a separate production-build check. Use the same workflows as the gate for your deploys: a green build is a deployable build.
Adapting it
Slicekit does not prescribe a host. The images are standard, the configuration is environment variables, and the observability exporters are OTLP, so point them at your infrastructure and ship.