Skip to content

chore(docker): switch runtime to aspnet:10.0-alpine (#456)#458

Merged
nanotaboada merged 1 commit intomasterfrom
chore/docker-aspnet-alpine
Apr 8, 2026
Merged

chore(docker): switch runtime to aspnet:10.0-alpine (#456)#458
nanotaboada merged 1 commit intomasterfrom
chore/docker-aspnet-alpine

Conversation

@nanotaboada
Copy link
Copy Markdown
Owner

@nanotaboada nanotaboada commented Apr 8, 2026

Summary

  • Switches the runtime base image from mcr.microsoft.com/dotnet/aspnet:10.0 (Debian) to mcr.microsoft.com/dotnet/aspnet:10.0-alpine, reducing image size by ~100–150 MB
  • Replaces apt-get with apk and groupadd/useradd with addgroup/adduser for Alpine compatibility
  • Refactors scripts/entrypoint.sh: adds a log() helper with timestamp prefix and replaces raw echo calls

Closes #456

Test plan

  • dotnet build --configuration Release passes
  • dotnet test --settings .runsettings — all 41 tests pass
  • docker compose down -v && docker compose build && docker compose up — container starts, health check goes green, API responds on port 9000

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Changed
    • Runtime image switched to an Alpine-based build to reduce image size and improve portability.
    • Startup logging enhanced with timestamped messages and a clear API endpoint display at boot for easier operational visibility.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 8, 2026

Walkthrough

Switched the Docker runtime image to mcr.microsoft.com/dotnet/aspnet:10.0-alpine, replaced Debian tooling with Alpine equivalents in the runtime stage, added DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true, and refactored scripts/entrypoint.sh to use a timestamped log() helper and print the API base URL; changelog updated.

Changes

Cohort / File(s) Summary
Alpine Migration
Dockerfile
Runtime stage base image changed to mcr.microsoft.com/dotnet/aspnet:10.0-alpine; apt-getapk add --no-cache curl; groupadd/useraddaddgroup/adduser; added ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true; preserved /storage ownership and exposed port/healthcheck/entrypoint behavior.
Entrypoint Refactor
scripts/entrypoint.sh
Replaced echo calls with a new log() helper that prefixes messages with [ENTRYPOINT] and an ISO timestamp; computes and logs a user-friendly API base URL (rewriting +/0.0.0.0localhost) before exec "$@".
Changelog update
CHANGELOG.md
Added [Unreleased]Changed entries documenting the Alpine base image switch and entrypoint logging additions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
Switch runtime base image to aspnet:10.0-alpine [#456]
Replace apt-get with apk and update runtime tooling [#456]
Replace groupadd/useradd with Alpine equivalents [#456]
Validate/configure globalization behavior [#456] ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true is set (forces invariant mode) but no indication of testing or adding ICU libs if needed.

Out-of-scope changes

Code Change Explanation
Timestamped logging function and API URL logging (scripts/entrypoint.sh) These logging enhancements are not requested in #456 (which is scoped to Docker runtime migration) and introduce new startup logging behavior beyond the migration objective.

Possibly related PRs

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title follows Conventional Commits format with 'chore(docker):' prefix, is under 80 characters (60 chars), and clearly describes the main change: switching the runtime base image to Alpine.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/docker-aspnet-alpine
  • 🛠️ sync documentation: Commit on current branch
  • 🛠️ sync documentation: Create PR
  • 🛠️ enforce http error handling: Commit on current branch
  • 🛠️ enforce http error handling: Create PR
  • 🛠️ idiomatic review: Commit on current branch
  • 🛠️ idiomatic review: Create PR
  • 🛠️ verify api contract: Commit on current branch
  • 🛠️ verify api contract: Create PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nanotaboada nanotaboada changed the title chore(docker): switch runtime to aspnet:10.0-alpine (#456) chore(docker): switch runtime to aspnet:10.0-alpine (#456) Apr 8, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
scripts/entrypoint.sh (1)

32-32: Consider deriving the logged API URL from env to avoid future drift.

Line 32 hardcodes http://localhost:9000; if ASPNETCORE_URLS changes, logs can become misleading while the app still works.

🔧 Optional refactor
+API_BASE_URL="$(echo "${ASPNETCORE_URLS:-http://localhost:9000}" | sed 's#://\+#://localhost#')"
-log "🔌 API endpoints | http://localhost:9000"
+log "🔌 API endpoints | $API_BASE_URL"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/entrypoint.sh` at line 32, The log message currently hardcodes
"http://localhost:9000" in the entrypoint script; change it to derive the API
URL from the ASPNETCORE_URLS environment variable (falling back to
"http://localhost:9000" if unset) and use that value in the call to the log
function so the printed endpoint always reflects the actual runtime binding; if
ASPNETCORE_URLS can contain multiple entries, extract the first URL (or the one
matching http/https) before passing it to log.
CHANGELOG.md (1)

57-60: Prefer concrete before/after image-size numbers over an estimated range.

Line 58’s ~100–150 MB is useful but hard to verify later; exact measured values (with command used) make release notes reproducible.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 57 - 60, Update the CHANGELOG entry that currently
says "~100–150 MB" for the runtime image size by replacing the estimated range
with exact measured sizes for both the old image
(`mcr.microsoft.com/dotnet/aspnet:10.0`) and the new image
(`mcr.microsoft.com/dotnet/aspnet:10.0-alpine`) and append the command used to
measure them (so readers can reproduce the measurement); edit the bullet that
mentions switching the runtime base image and include the concrete "before: X
MB, after: Y MB (measured with: <command>)" phrasing to keep the release notes
precise and verifiable.
Dockerfile (1)

25-31: Globalization behavior should be explicitly configured for Alpine runtime.

After switching to Alpine on line 25, the Dockerfile does not declare a globalization strategy. Alpine lacks the ICU libraries by default, which can cause runtime failures for any culture-dependent operations. While the codebase shows minimal actual culture-sensitive API usage, the absence of an explicit configuration (icu-libs + DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false, or explicit DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true) leaves behavior undefined and can lead to unexpected locale issues if the application evolves.

Suggested fix (if ICU support is needed)
-RUN apk add --no-cache curl
+RUN apk add --no-cache curl icu-libs
+ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false

Alternatively, if invariant mode is acceptable, add ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true to make the decision explicit.

Reference: https://learn.microsoft.com/en-us/dotnet/core/docker/container-images

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 25 - 31, The Alpine runtime image lacks ICU so
globalization is undefined; update the Dockerfile's runtime stage (FROM
mcr.microsoft.com/dotnet/aspnet:10.0-alpine) to explicitly configure
globalization: either install ICU libraries (apk add icu-libs and related
packages) and set ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false, or explicitly
set ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true if invariant mode is
acceptable; modify the RUN/ENV lines in the runtime stage accordingly to make
the globalization strategy explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@CHANGELOG.md`:
- Around line 57-60: Update the CHANGELOG entry that currently says "~100–150
MB" for the runtime image size by replacing the estimated range with exact
measured sizes for both the old image (`mcr.microsoft.com/dotnet/aspnet:10.0`)
and the new image (`mcr.microsoft.com/dotnet/aspnet:10.0-alpine`) and append the
command used to measure them (so readers can reproduce the measurement); edit
the bullet that mentions switching the runtime base image and include the
concrete "before: X MB, after: Y MB (measured with: <command>)" phrasing to keep
the release notes precise and verifiable.

In `@Dockerfile`:
- Around line 25-31: The Alpine runtime image lacks ICU so globalization is
undefined; update the Dockerfile's runtime stage (FROM
mcr.microsoft.com/dotnet/aspnet:10.0-alpine) to explicitly configure
globalization: either install ICU libraries (apk add icu-libs and related
packages) and set ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false, or explicitly
set ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true if invariant mode is
acceptable; modify the RUN/ENV lines in the runtime stage accordingly to make
the globalization strategy explicit.

In `@scripts/entrypoint.sh`:
- Line 32: The log message currently hardcodes "http://localhost:9000" in the
entrypoint script; change it to derive the API URL from the ASPNETCORE_URLS
environment variable (falling back to "http://localhost:9000" if unset) and use
that value in the call to the log function so the printed endpoint always
reflects the actual runtime binding; if ASPNETCORE_URLS can contain multiple
entries, extract the first URL (or the one matching http/https) before passing
it to log.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 93413cfa-48c5-445d-b48b-17f99b73313b

📥 Commits

Reviewing files that changed from the base of the PR and between 4f7593f and 5eadb5d.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • Dockerfile
  • scripts/entrypoint.sh

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@nanotaboada nanotaboada force-pushed the chore/docker-aspnet-alpine branch from 5eadb5d to c2fcd77 Compare April 8, 2026 18:51
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Apr 8, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
scripts/entrypoint.sh (2)

32-36: IPv6 wildcard [::] and * are not handled.

The sed replacement covers + and 0.0.0.0, but ASP.NET Core also accepts [::] (IPv6 any) and * as wildcard hosts. Since the Dockerfile explicitly sets http://+:9000, this is fine for the current setup—but if someone overrides ASPNETCORE_URLS with an IPv6 binding, the log will show the raw wildcard.

♻️ Optional: handle additional wildcards
 _raw_url=$(printf '%s' "${ASPNETCORE_URLS:-http://+:9000}" | cut -d';' -f1)
-API_URL=$(printf '%s' "$_raw_url" | sed 's|+|localhost|g; s|0\.0\.0\.0|localhost|g')
+API_URL=$(printf '%s' "$_raw_url" | sed 's|+|localhost|g; s|0\.0\.0\.0|localhost|g; s|\[::\]|localhost|g; s|\*|localhost|g')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/entrypoint.sh` around lines 32 - 36, The current derivation of
API_URL replaces only '+' and '0.0.0.0' but misses IPv6 and '*' wildcards;
update the replacement pipeline that builds API_URL (variables _raw_url and
API_URL) so the sed command also normalizes IPv6 any '[::]' and '*' to
'localhost' (and ensure the '\[::\]' pattern is matched correctly), then call
log "🔌 API endpoints | $API_URL" as before.

4-9: Consider POSIX compliance for the local keyword.

The shebang is #!/bin/sh, but local is not part of POSIX. While this works on Alpine (BusyBox ash supports local), it could break if run on a strict POSIX-only shell. Since this script is specifically for the Alpine container, this is acceptable—just be aware if portability becomes a concern.

Also, return 0 on line 8 is unnecessary since echo already returns 0 on success.

♻️ Optional: remove redundant return
 log() {
     local message="$1"
     echo "[ENTRYPOINT] $(date '+%Y/%m/%d - %H:%M:%S') | $message"
-    return 0
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/entrypoint.sh` around lines 4 - 9, The log() function uses the
non-POSIX local keyword and an unnecessary return 0; make it POSIX-safe by
removing local and assigning the parameter directly (e.g., message="$1") or by
referencing "$1" inline, and delete the trailing "return 0" so the function
becomes: log() { message="$1"; echo "[ENTRYPOINT] $(date '+%Y/%m/%d - %H:%M:%S')
| $message"; } — update the log function accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@scripts/entrypoint.sh`:
- Around line 32-36: The current derivation of API_URL replaces only '+' and
'0.0.0.0' but misses IPv6 and '*' wildcards; update the replacement pipeline
that builds API_URL (variables _raw_url and API_URL) so the sed command also
normalizes IPv6 any '[::]' and '*' to 'localhost' (and ensure the '\[::\]'
pattern is matched correctly), then call log "🔌 API endpoints | $API_URL" as
before.
- Around line 4-9: The log() function uses the non-POSIX local keyword and an
unnecessary return 0; make it POSIX-safe by removing local and assigning the
parameter directly (e.g., message="$1") or by referencing "$1" inline, and
delete the trailing "return 0" so the function becomes: log() { message="$1";
echo "[ENTRYPOINT] $(date '+%Y/%m/%d - %H:%M:%S') | $message"; } — update the
log function accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 126c3d81-c55f-4f1b-9b28-cdbbb385c757

📥 Commits

Reviewing files that changed from the base of the PR and between 5eadb5d and c2fcd77.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • Dockerfile
  • scripts/entrypoint.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md

@nanotaboada nanotaboada merged commit 1d3c483 into master Apr 8, 2026
9 checks passed
@nanotaboada nanotaboada deleted the chore/docker-aspnet-alpine branch April 8, 2026 19:23
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.

Switch runtime base image to aspnet:10.0-alpine to reduce image size

1 participant