.env contains ASPNETCORE_ENVIRONMENT=Development, which gets
loaded via env_file into every service. With ASPNETCORE_ENVIRONMENT=Development, ASP.NET Core loads appsettings-org.Development.json — which has a Kestrel:Endpoints block binding BOTH http://localhost:5000 AND https://localhost:5001. The HTTPS endpoint has no cert on a fresh host, so Kestrel crashes with:
fail: Microsoft.Extensions.Hosting.Internal.Host[11]
Unable to configure HTTPS endpoint. No server certificate
was specified, and the default developer certificate could
not be found or is out of date.
Fix: add an explicit ASPNETCORE_ENVIRONMENT=Production to each
runtime service's environment: block. In Compose, literal
environment: entries override env_file: entries with the same
name, so this wins over .env's Development value.
Production-loaded appsettings-org.json has no Kestrel block, so
Kestrel uses only the ASPNETCORE_URLS env var we already set
(http://+:5000 etc.) — HTTP only, no HTTPS crash.
The runtime services were trying to bind HTTPS even though the
compose file only mapped HTTP ports. Symptom on first start:
fail: Microsoft.Extensions.Hosting.Internal.Host[11]
Hosting failed to start
System.InvalidOperationException: Unable to configure
HTTPS endpoint. No server certificate was specified,
and the default developer certificate could not be found
or is out of date.
The root cause is appsettings-org.Development.json which sets
Site.Authority = https://localhost:5001
combined with Kestrel's auto-detection of https_port from the
listening URL. ASP.NET Core 9 promotes any 'Authority' / 'Url'
property to a Kestrel binding target unless ASPNETCORE_URLS is
explicitly set.
Fix: add an environment: block on each runtime service pinning
ASPNETCORE_URLS to the HTTP-only form, plus empty
ASPNETCORE_HTTPS_PORT to suppress auto-detection. This forces
HTTP-only binding on the 'machine vierge' criterion of Jalon 0,
where no cert is available.
For production HTTPS, the existing commented # - '5001:5001' /
# volumes: /etc/letsencrypt blocks stay the way to enable it:
uncomment the port, uncomment the volume, and add a
Kestrel:Endpoints:Https block in appsettings-org.json that
points at the Let's Encrypt cert files.
After the multi-stage refactor of Dockerfile, the three separate
Dockerfile.runtime* files are obsolete: every runtime image is
now a stage of the main Dockerfile.
Delete Dockerfile.runtime, Dockerfile.runtime.blogs,
Dockerfile.runtime.api, and rewrite docker-compose.yaml so that
each service points at the corresponding target via build.target:
web -> web-runtime (port 5000)
api -> api-runtime (port 5002)
blogs -> blogs-runtime (port 5004)
The build tag is passed through build.args.BUILD_ENV_TAG, which
the Dockerfile declares as an ARG with the same default as
before.
The shared multi-stage Dockerfile is now the single source of
truth for both the build-env image (used by the APK workflow)
and the three runtime images.
The previous commit referenced ./appsettings-org.json at the repo
root, but the actual file lives at src/Yavsc.Org/appsettings-org.json
(next to its template appsettings-org-template.json).
Without this fix, 'docker compose up' fails with:
failed to stat /home/paul/Workspace/yavsc/appsettings-org.json:
stat ...: no such file or directory
Rewrite docker-compose.yaml to match the runtime architecture
introduced in the previous commits:
- 4 services: db (postgres:16), web (Yavsc.Org), api (Yavsc.Api),
blogs (Yavsc.Blogs). Each runtime service builds from its own
Dockerfile.runtime* with a pinned BUILD_ENV_IMAGE.
- Healthcheck on db via pg_isready. web/api/blogs wait for
service_healthy before starting (was: bare depends_on which
races the DB on cold boot).
- Two named networks: yavsc-internal (db + runtimes) and
yavsc-public (runtimes only). Compose v2 default, but the
split makes the intent explicit and lets the operator
externalise the public network if needed.
- Each runtime service injects appsettings-org.json via the
yavsc_appsettings BuildKit secret (file: ./appsettings-org.json).
No appsettings in the build context.
- HTTPS ports (5001, 5003, 5005) and the /etc/letsencrypt volume
mount are commented out — uncomment them in production when
Kestrel:Certificates is configured in appsettings-org.json.
- The old POSTGRES_* build args on the web service are gone: the
build env no longer needs DB credentials (only the runtime does,
via env_file).