A production-oriented ASP.NET Core API starter with PostgreSQL, Entity Framework Core, JWT authentication, and a Users/RBAC service profile.
This repository is named aspnet-core-api-starter and follows the same generated-profile model as the Drogon API starter.
Use the generator when starting a new project. The destination must be new or empty:
./scripts/aspnet-starter init shop-api ../shop-apiThe default user-service profile includes the maintained Users/RBAC implementation, JWT authentication, EF Core migrations, Docker support, and tests. For a clean ASP.NET foundation without user-service:
./scripts/aspnet-starter init shop-api ../shop-api --profile minimalSupported profiles:
user-service(default): Users, roles, permissions, JWT authentication, PostgreSQL, migrations, and testsminimal: dependency-free ASP.NET Core API foundation with a health endpoint and baseline logging/tracing/metrics; no users, roles, permissions, EF Core, database, or authentication
The source repository itself is the complete user-service profile. The minimal profile lives in templates/minimal and is intentionally independent of the user-service implementation.
Both profiles require PORT at startup and report the exact missing or invalid
setting. The user-service profile additionally requires DB_URL,
JWT_SECRET_KEY (at least 32 bytes), JWT_ISSUER, and JWT_AUDIENCE.
Observability is part of the starter contract: logging, tracing, and metrics
work by default, while Sentry error tracking is enabled only when
SENTRY_DSN is configured and remains disabled without blocking startup.
The /metrics endpoint exposes low-cardinality Prometheus-compatible counters,
and responses include an X-Trace-Id correlation header.
Errors use RFC 9457-style application/problem+json responses with status,
title, detail, instance, and a traceId extension. Details are safe for
clients while logs retain exceptions for diagnostics.
The starter uses OpenTelemetry Protocol (OTLP) for vendor-neutral trace and
metric export. Set OTEL_EXPORTER_OTLP_ENDPOINT to an OTLP-compatible
collector or APM gateway, for example http://localhost:4317. The included
docker-compose.otel.yaml overlay starts a local collector for verification:
docker compose -f docker-compose.yaml -f docker-compose.otel.yaml up -d --buildSentry is an optional error-tracking adapter. Set SENTRY_DSN to enable it;
an unset or invalid value falls back to the standard logging reporter without
blocking startup. The IErrorReporter abstraction can be replaced by another
error tracker or APM adapter without changing request error handling.
Telemetry must not include passwords, JWTs, database credentials, or unnecessary personal data. Keep route labels and metric dimensions low-cardinality, and configure the collector or backend to enforce retention, access control, and environment-specific sampling.
See the project roadmap for milestones and task priorities. See the release checklist before production deployments. Existing release images can be promoted through the Promote release GitHub Actions workflow. Staging accepts SemVer prereleases; production accepts only stable SemVer versions and requires protected-environment approval. After promotion, the Verify promoted release workflow smoke-tests the image and a known-good rollback image against an ephemeral PostgreSQL database. The Record deployment workflow verifies the deployed digest and stores an auditable GitHub Deployment record with rollback details and operator notes. The daily Deployment drift monitor checks that record against GHCR and opens an issue when the deployed digest, rollback image, or attestation is invalid. Environment dashboards, alert rules, and incident response guidance are available in the observability and incident response runbook. Hourly synthetic endpoint monitoring and dependency-specific alert examples are documented in synthetic monitoring. Release annotations and automated availability budget reporting are documented in SLO burn-rate reporting. Quarterly reliability trends and SLO review steps are documented in reliability trends. Capacity scenarios and dependency SLO ownership are documented in capacity planning and dependency SLO ownership. Capacity tests include a guarded non-production PostgreSQL fixture and retain run summaries for comparison over time. See the architecture and extension points when adapting the starter to another identity provider, database, API design, or telemetry backend. See API clients and contracts for generated-client commands, C#/TypeScript examples, and the OpenAPI compatibility snapshot. See the observability guide for optional OTLP export, the replaceable error reporter, and the importable Grafana dashboard. See the email actions guide for optional password-reset and email-verification flows with a provider-neutral sender boundary. See the messaging guide for optional broker adapters and resource-change events without adding broker dependencies. See template versioning for generated metadata and the migration path between starter releases. See background jobs and outbox for the opt-in durable event processor. See integration adapters for contract-tested HTTP reference implementations. See performance and SLO guidance for the CI smoke test and production measurement baseline. See the database migration runbook for generated projects and production deployment sequencing.
The Markdown documentation is published automatically to
GitHub Pages from the
main branch with MkDocs Material by GitHub Actions.
Docker Compose is the included deployment baseline. Kubernetes manifests and Helm charts are intentionally not maintained by this starter; teams can choose their preferred orchestration platform and create those deployment assets for their environment.
- .NET SDK 8.0+
- PostgreSQL 14+
- Docker & Docker Compose (optional, for containerized setup)
dotnet tool install --global dotnet-efVerify installation:
dotnet ef --versionCreate a .env file by copying .env.example from the repository root.
ENV=Development
PORT=5070
# JWT
JWT_SECRET_KEY=CHANGE_ME_TO_A_SECURE_32_BYTE_MIN_SECRET
JWT_ISSUER=asp-shop-api
JWT_AUDIENCE=asp-shop-client
JWT_EXPIRY=300
JWT_REFRESH_EXPIRY=604800
AUTH_MAX_FAILED_ATTEMPTS=5
AUTH_LOCKOUT_MINUTES=15
# Optional error tracking
SENTRY_DSN=
# Optional vendor-neutral APM export (OTLP)
OTEL_EXPORTER_OTLP_ENDPOINT=
# Database
POSTGRES_USER=shopuser
POSTGRES_PASSWORD=shoppassword
POSTGRES_DB=shopdb
DB_URL=Host=postgres;Port=5432;Database=shopdb;Username=shopuser;Password=shoppassword
REDIS_CONNECTION=
⚠️ Important
JWT_SECRET_KEYmust be at least 32 bytes (256 bits) for HMAC-SHA256- Generate one with:
openssl rand -base64 32
- Never commit
.envfiles to version control
dotnet ef migrations add <MigrationName>Example:
dotnet ef migrations add InitialCreatedotnet ef database updatedotnet builddotnet watch run -- --logging:LogLevel:Default=DebugThe API will be available at:
http://localhost:5070
Swagger UI:
http://localhost:5070/swagger
docker compose up -d --buildThis will:
- Start PostgreSQL
- Run database migrations
- Launch the API container
To stop containers:
docker compose down- Authentication is handled using JWT tokens
- Tokens are issued via the
/auth/tokenendpoint - Protected endpoints require:
Authorization: Bearer <JWT_TOKEN>After the database is available, create the initial administrator without a prompt. The command applies pending migrations, is safe to rerun, and accepts flags or environment variables for deployment automation:
ADMIN_EMAIL=admin@example.com \
ADMIN_PASSWORD='change-this-in-a-secret-manager' \
dotnet run -- --create-adminOptional values are ADMIN_FIRST_NAME, ADMIN_LAST_NAME, and ADMIN_PHONE.
The equivalent flags are --admin-email, --admin-password,
--admin-first-name, --admin-last-name, and --admin-phone.
- Passwords are hashed using BCrypt
- Database access is handled via Entity Framework Core
- Controllers inherit from a shared
BaseController<T>for common CRUD operations
If you see:
key size must be greater than 256 bits
Your JWT_SECRET_KEY is too short. Generate a new one:
openssl rand -base64 32