Skip to content

P1: Harden Docker image — tini PID 1, HEALTHCHECK, pinned digest (#171)#189

Open
dkijania wants to merge 1 commit into
mainfrom
feat/container-hardening
Open

P1: Harden Docker image — tini PID 1, HEALTHCHECK, pinned digest (#171)#189
dkijania wants to merge 1 commit into
mainfrom
feat/container-hardening

Conversation

@dkijania

Copy link
Copy Markdown
Contributor

What & why

Part of the production-readiness epic (#163). Closes #171.

The runtime container ran npm start as PID 1, which forwards signals poorly — so SIGTERM didn't cleanly reach node and the new graceful shutdown (#170) couldn't run. It also had no container healthcheck and used a floating node:20-alpine tag.

Changes

  • tini as PID 1: run node build/src/index.js directly under tini, so node receives SIGTERM (graceful shutdown) and zombies are reaped.
  • HEALTHCHECK: hits the built-in /healthcheck endpoint (honours $PORT, defaults to 8080).
  • Pinned base image by digest on both stages (node:20-alpine@sha256:fb4cd12c…) for reproducible, tamper-evident builds — bump via Dependabot (P1: Supply chain — Dependabot, npm audit gate, image scan, SBOM #175).
  • .dockerignore to keep the build context lean.

Verification (built and run locally)

  • Image builds cleanly.
  • Runs as non-root (uid=1001 nodeuser).
  • tini 0.19.0 is the init/entrypoint; node v20.20.2 present.
  • Default CMD runs node build/src/index.js under tini — reaches buildContext (errors only on the expected missing PG_CONN), confirming the entrypoint chain.

No app code changed; lint and prettier --debug-check . pass.

🤖 Generated with Claude Code

The runtime container ran `npm start` as PID 1, which forwards signals poorly —
so SIGTERM didn't cleanly reach node and the new graceful shutdown couldn't run.
It also had no container healthcheck and used a floating `node:20-alpine` tag.

- Run `node build/src/index.js` directly under `tini` as PID 1, so the process
  receives SIGTERM and shuts down gracefully (and zombies are reaped).
- Add a `HEALTHCHECK` hitting the built-in `/healthcheck` endpoint (honours
  $PORT).
- Pin both stages to the `node:20-alpine` image digest for reproducible,
  tamper-evident builds (bump via Dependabot — #175).
- Add a `.dockerignore` to keep the build context lean.

Verified locally: the image builds; runs as non-root (uid 1001); tini 0.19.0 is
PID 1; `node build/src/index.js` is the entrypoint chain (reaches buildContext).

Closes #171.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QSuak9smCHbp4N17xjjLF6
@dkijania dkijania added production-readiness Work toward making the API production-ready / publicly available P1 Strongly recommended before GA labels Jun 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P1 Strongly recommended before GA production-readiness Work toward making the API production-ready / publicly available

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P1: Container hardening — node as PID 1 / tini, HEALTHCHECK, pin digest

1 participant