Skip to content

Harden container runtime and graceful shutdown - #86

Merged
mxssl merged 1 commit into
mainfrom
chore/harden-container-runtime
Aug 1, 2026
Merged

Harden container runtime and graceful shutdown#86
mxssl merged 1 commit into
mainfrom
chore/harden-container-runtime

Conversation

@mxssl

@mxssl mxssl commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • replace the Alpine runtime stage with the minimal static-debian13:nonroot distroless image
  • pin both the Go builder and distroless runtime to multi-platform manifest digests
  • run the service explicitly as the distroless nonroot user
  • remove the unused Alpine packages, package installation layer, and redundant executable-permission layer
  • copy go.mod and go.sum before application sources so dependency downloads remain in a stable Docker layer
  • build a static Linux binary with -trimpath and -ldflags="-s -w"
  • add a .dockerignore that keeps repository metadata, local configuration, generated binaries, and development-only files out of the build context
  • handle both SIGINT and SIGTERM through the same bounded graceful-shutdown path

Why

The previous runtime image included an Alpine userspace and package manager even though ntwrk is compiled with CGO_ENABLED=0 and does not require dynamic libraries. It also ran the application as root and installed build packages that were not needed by the current module dependencies.

Docker normally stops containers with SIGTERM. The previous signal handler only called http.Server.Shutdown for SIGINT; its SIGTERM branch returned immediately, which could terminate active requests without allowing the HTTP server to drain.

Implementation details

The build still uses the official Go Alpine image, but the final stage now contains only the statically linked binary and the files supplied by distroless. Readable image tags remain next to their SHA-256 digests so dependency automation can identify updates while builds resolve reproducibly.

Shutdown now:

  1. waits for either an HTTP server failure or a termination signal;
  2. treats both SIGINT and SIGTERM as graceful shutdown requests;
  3. gives active requests up to 10 seconds to complete;
  4. force-closes the server if graceful shutdown exceeds that deadline;
  5. ignores the expected http.ErrServerClosed result and logs a clean stop.

Operational impact

  • The runtime has no shell or package manager. Interactive debugging should use a temporary distroless debug-nonroot image or external container diagnostics.
  • Runtime environment variables and network behavior are unchanged.
  • The application runs as nonroot:nonroot.
  • The resulting local image is approximately 8.18 MB.
  • The entrypoint is the absolute executable path /ntwrk.

Validation

  • go test ./...
  • go vet ./...
  • git diff --check
  • docker build --check . with no warnings
  • full image build through OrbStack
  • HTTP smoke request against the running container
  • docker stop / SIGTERM test confirming:
    • graceful shutdown logs app stopped
    • container exits with status 0
    • runtime user is nonroot:nonroot
    • entrypoint is /ntwrk

@mxssl
mxssl marked this pull request as ready for review August 1, 2026 23:08
@mxssl
mxssl merged commit 8bc04e6 into main Aug 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant