devops
Installation
SKILL.md
DevOps - containers, CI/CD, and safe deploys for the .NET/Angular house
The pipeline is production code - a broken workflow blocks every merge and a leaked secret is an incident, not a warning. This is the delivery-surface map for the house stacks (ASP.NET Core, Angular, and their SQL/data layer). It pairs with dotnet-aspire (orchestration), dotnet-migrate (migration mechanics), and dotnet-security / database-security (secret handling; the crypto primitives are dotnet-cryptography). The rule under all of it - the build is reproducible, the secret never touches an image or a log, and every deploy is reversible.
Docker - reproducible, minimal, non-root
- Multi-stage build - an SDK stage compiles and publishes, a slim runtime stage copies only the published output; the SDK image never ships.
- Order the layers for the cache - copy the project and lock files and restore BEFORE copying the source, so a source edit does not bust the restore layer. A Dockerfile that copies everything then restores never hits the cache.
- Mount a persistent package cache in the restore layer -
RUN --mount=type=cache,target=/root/.nuget/packages dotnet restore(and the npm cache) - so the cache survives even when the copy-lockfile-then-restore layer is busted. - When a build genuinely needs a secret - a private NuGet-feed PAT during restore - pass it with
RUN --mount=type=secret,id=...so it never lands in a layer or image history, distinct from the runtime secrets pulled from the store. - Pin the base image by digest, never a floating :latest or a bare major tag - a moving tag makes the build non-reproducible and is a supply-chain hole. Prefer a chiseled or distroless .NET runtime image (no shell, minimal CVE surface).
- Pin the BuildKit frontend on the Dockerfile's first line -
# syntax=docker/dockerfile:1(to a digest for a fully locked build) - so an untrusted or moving frontend cannot run build-time code you never vetted; and treatbuildx--sbom/--provenanceattestations as metadata, not signatures - sign the image with cosign if you need provenance you can verify. - Build multi-arch images with
buildx --platform linux/amd64,linux/arm64when developers are on Apple Silicon but production runs x64 - a locally-built image is otherwise the wrong architecture for the server. - Run as a non-root USER, mount the root filesystem read-only where the app allows, and keep a .dockerignore that excludes bin, obj, node_modules, .git, and every secret-bearing file.
- Harden past non-root at runtime - drop all Linux capabilities, set no-new-privileges, cap memory / CPU / PID count, and keep the default seccomp profile plus an AppArmor or SELinux profile instead of reaching for
--privileged, so a compromised or leaking process cannot escalate, exhaust PIDs, or starve the host. The full checklist with the compose keys:references/docker-hardening.md. - Give the container a HEALTHCHECK and proper PID-1 signal handling (an init shim) so the orchestrator can tell ready from dead and a SIGTERM drains rather than kills.
The shape in one Dockerfile - multi-stage, cache-ordered, digest-pinned, non-root: