Skip to content
Slicekit

Operations

Deployment

Building the production images, configuration through environment variables, and running the production stack.

View .md
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__ConnectionString
  • Caching__Redis
  • Messaging__RabbitMq__*, Storage__* (SeaweedFS/S3)
  • Cors__AllowedOrigins__0
  • Otel__Endpoint for your collector
  • Auditing__IpHashSalt with 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.

Live demo

Explore the running app

No sign-up. Pick an account to open the demo with its credentials prefilled, or open it and sign in yourself.

Open without signing in

Credentials are prefilled. Shared password Slicekit-Demo-2026!.