Conversation
Introduces a new distroless build stage to the Dockerfile, providing a minimal and secure production runtime for Ghost. This change includes: - Pre-initialization of Ghost content within the build stage to ensure compatibility with the distroless environment, replacing `docker-entrypoint.sh` functionality. - A new `distroless` stage based on `gcr.io/distroless/nodejs20-debian12`. - Configuration of environment variables and OCI labels specific to the distroless image. - Copying of Ghost installation with `nonroot` ownership (UID 65532) for enhanced security. - Direct execution of `node current/index.js` as the CMD, removing the need for a shell entrypoint in the distroless image.
WalkthroughSupport for a distroless production runtime has been added to the Ghost Docker build, including Dockerfile changes, a new README for distroless usage, and new Bash scripts for building and testing both Alpine and distroless images. The distroless image removes shell access and uses a non-root user for enhanced security. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant BuildScript as build-distroless.sh
participant Docker
participant DistrolessImage
participant GhostApp
User->>BuildScript: Run build-distroless.sh
BuildScript->>Docker: Build Alpine image (Dockerfile default)
Docker-->>BuildScript: Alpine image built
BuildScript->>Docker: Build distroless image (Dockerfile distroless stage)
Docker-->>BuildScript: Distroless image built
BuildScript->>User: Output image info and usage instructions
User->>DistrolessImage: Run container (test-distroless.sh)
DistrolessImage->>GhostApp: Start Node.js process
GhostApp->>DistrolessImage: Serve HTTP on port 2368
User->>GhostApp: Send HTTP request (health check)
GhostApp-->>User: Return HTTP response
sequenceDiagram
participant Dockerfile
participant BuilderStage
participant DistrolessStage
participant GhostContent
Dockerfile->>BuilderStage: Build Ghost, copy content, set permissions
BuilderStage->>DistrolessStage: Copy Ghost app and content
DistrolessStage->>GhostContent: Ensure content initialized, set ownership (UID 65532)
DistrolessStage->>DistrolessStage: Set WORKDIR, USER, VOLUME, EXPOSE, CMD
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
docker-entrypoint.sh functionality.
- A new distroless stage based on gcr.io/distroless/nodejs20-debian12.
- Configuration of environment variables and OCI labels specific to the distroless image.
- Copying of Ghost installation with nonroot ownership (UID 65532) for enhanced security.
- Direct execution of node current/index.js as the CMD, removing the need for a shell entrypoint in the distroless image.There was a problem hiding this comment.
Actionable comments posted: 10
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (4)
v5/Dockerfile(2 hunks)v5/README-distroless.md(1 hunks)v5/build-distroless.sh(1 hunks)v5/test-distroless.sh(1 hunks)
🧰 Additional context used
🪛 Hadolint (2.12.0)
v5/Dockerfile
[warning] 187-187: Use WORKDIR to switch to a directory
(DL3003)
[warning] 232-232: Always tag the version of an image explicitly
(DL3006)
🪛 Checkov (3.2.334)
v5/Dockerfile
[LOW] 232-232: Ensure the base image uses a non latest version tag
(CKV_DOCKER_7)
[LOW] 1-275: Ensure that HEALTHCHECK instructions have been added to container images
(CKV_DOCKER_2)
🪛 LanguageTool
v5/README-distroless.md
[uncategorized] ~226-~226: Use a comma before ‘but’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...`` #### Missing Content If Ghost starts but content is missing: ```bash # Verify co...
(COMMA_COMPOUND_SENTENCE_2)
[style] ~246-~246: Consider a different adjective to strengthen your wording.
Context: ...fic issues 4. Use the debug variant for deeper investigation ## References - [Google...
(DEEP_PROFOUND)
🪛 markdownlint-cli2 (0.17.2)
v5/README-distroless.md
8-8: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
15-15: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
29-29: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
30-30: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
33-33: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
34-34: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
37-37: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
38-38: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
43-43: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
44-44: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
48-48: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
49-49: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
59-59: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
60-60: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
64-64: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
65-65: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
81-81: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
82-82: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
100-100: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
101-101: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
121-121: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
122-122: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
167-167: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
170-170: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
171-171: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
177-177: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
178-178: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
186-186: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
187-187: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
193-193: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
195-195: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
215-215: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
217-217: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
225-225: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
227-227: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
232-232: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
234-234: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
243-243: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
253-253: Files should end with a single newline character
null
(MD047, single-trailing-newline)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build_edge
| # Pre-initialize Ghost content for distroless compatibility | ||
| # This replaces the functionality of docker-entrypoint.sh for distroless runtime | ||
| RUN set -eux; \ | ||
| cd "$GHOST_INSTALL"; \ | ||
| if [ -d "content.orig" ]; then \ | ||
| echo "Pre-initializing Ghost content for distroless runtime..."; \ | ||
| # Copy default content if target doesn't exist \ | ||
| for src in content.orig/*/ content.orig/themes/*; do \ | ||
| if [ -d "$src" ]; then \ | ||
| src="${src%/}"; \ | ||
| target="$GHOST_CONTENT/${src#*/content.orig/}"; \ | ||
| mkdir -p "$(dirname "$target")"; \ | ||
| if [ ! -e "$target" ]; then \ | ||
| echo "Copying $src to $target"; \ | ||
| cp -r "$src" "$target"; \ | ||
| fi; \ | ||
| fi; \ | ||
| done; \ | ||
| # Set proper ownership for distroless nonroot user (UID 65532) \ | ||
| chown -R 65532:65532 "$GHOST_CONTENT"; \ | ||
| chown -R 65532:65532 "$GHOST_INSTALL"; \ |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Refactor build step to use WORKDIR
Instead of using cd "$GHOST_INSTALL" inside the RUN, introduce a WORKDIR ${GHOST_INSTALL} before this RUN. This improves layer caching and readability.
🧰 Tools
🪛 Hadolint (2.12.0)
[warning] 187-187: Use WORKDIR to switch to a directory
(DL3003)
🤖 Prompt for AI Agents
In v5/Dockerfile around lines 185 to 205, the RUN command uses 'cd
"$GHOST_INSTALL"' to change directories. To improve layer caching and
readability, add a WORKDIR instruction with the value of $GHOST_INSTALL before
this RUN command, and remove the 'cd "$GHOST_INSTALL"' from the RUN script.
| # HEALTHCHECK must be done during the runtime | ||
|
|
||
| ENTRYPOINT [ "docker-entrypoint.sh" ] | ||
| CMD [ "node", "current/index.js" ] No newline at end of file | ||
| CMD [ "node", "current/index.js" ] |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Add a HEALTHCHECK for production images
Both Alpine and distroless stages currently lack a HEALTHCHECK. Adding one (e.g., HEALTHCHECK --interval=30s --timeout=5s CMD curl -f http://localhost:2368/ || exit 1) allows orchestrators to monitor Ghost’s readiness.
🤖 Prompt for AI Agents
In v5/Dockerfile around lines 224 to 227, add a HEALTHCHECK instruction to the
production image to enable container orchestrators to monitor Ghost's readiness.
Insert a line such as 'HEALTHCHECK --interval=30s --timeout=5s CMD curl -f
http://localhost:2368/ || exit 1' before the ENTRYPOINT to periodically check
the service health.
| # ---------------------------------------------- | ||
| # 6) LAYER distroless (Production runtime) | ||
| # ---------------------------------------------- | ||
| FROM gcr.io/distroless/nodejs20-debian12 AS distroless |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Pin distroless base image
The FROM gcr.io/distroless/nodejs20-debian12 instruction has no digest or patch version. Pinning to a fully qualified tag or digest ensures reproducible builds and mitigates supply-chain risks.
🧰 Tools
🪛 Checkov (3.2.334)
[LOW] 232-232: Ensure the base image uses a non latest version tag
(CKV_DOCKER_7)
🪛 Hadolint (2.12.0)
[warning] 232-232: Always tag the version of an image explicitly
(DL3006)
🤖 Prompt for AI Agents
In v5/Dockerfile at line 232, the distroless base image is referenced without a
digest or patch version, which can lead to non-reproducible builds. Update the
FROM instruction to pin the image to a specific digest or a fully qualified tag
with a patch version to ensure consistent and secure builds.
| set -e | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Enable full strict mode
Replace set -e with set -euo pipefail (or set -euxo pipefail) to catch undefined variables and pipeline failures.
🤖 Prompt for AI Agents
In v5/build-distroless.sh around lines 2 to 3, replace the existing `set -e`
command with `set -euo pipefail` to enable full strict mode. This change will
ensure the script exits on errors, treats unset variables as errors, and
properly handles pipeline failures, improving script robustness.
| docker build \ | ||
| --target distroless \ | ||
| --build-arg VERSION="$VERSION" \ | ||
| -t "$DOCKERHUB_USER/$APP_NAME:$VERSION-distroless" \ | ||
| -t "$DOCKERHUB_USER/$APP_NAME:distroless" \ | ||
| -t "$DOCKERHUB_USER/$APP_NAME:latest" \ | ||
| -f v5/Dockerfile \ | ||
| . |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Ensure up-to-date distroless images
Similarly, add --pull to the distroless build command:
-docker build \
+docker build --pull \
--target distroless \📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| docker build \ | |
| --target distroless \ | |
| --build-arg VERSION="$VERSION" \ | |
| -t "$DOCKERHUB_USER/$APP_NAME:$VERSION-distroless" \ | |
| -t "$DOCKERHUB_USER/$APP_NAME:distroless" \ | |
| -t "$DOCKERHUB_USER/$APP_NAME:latest" \ | |
| -f v5/Dockerfile \ | |
| . | |
| docker build --pull \ | |
| --target distroless \ | |
| --build-arg VERSION="$VERSION" \ | |
| -t "$DOCKERHUB_USER/$APP_NAME:$VERSION-distroless" \ | |
| -t "$DOCKERHUB_USER/$APP_NAME:distroless" \ | |
| -t "$DOCKERHUB_USER/$APP_NAME:latest" \ | |
| -f v5/Dockerfile \ | |
| . |
🤖 Prompt for AI Agents
In v5/build-distroless.sh around lines 31 to 38, the docker build command for
the distroless target is missing the --pull flag. Add --pull to the docker build
command to ensure it always pulls the latest base images before building,
keeping the distroless images up to date.
| docker build \ | ||
| --target final \ | ||
| --build-arg VERSION="$VERSION" \ | ||
| -t "$DOCKERHUB_USER/$APP_NAME:$VERSION-alpine" \ | ||
| -t "$DOCKERHUB_USER/$APP_NAME:alpine" \ | ||
| -f v5/Dockerfile \ | ||
| . | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Ensure up-to-date base images
Include --pull in the Alpine build to always fetch the latest base image:
-docker build \
+docker build --pull \
--target final \📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| docker build \ | |
| --target final \ | |
| --build-arg VERSION="$VERSION" \ | |
| -t "$DOCKERHUB_USER/$APP_NAME:$VERSION-alpine" \ | |
| -t "$DOCKERHUB_USER/$APP_NAME:alpine" \ | |
| -f v5/Dockerfile \ | |
| . | |
| docker build --pull \ | |
| --target final \ | |
| --build-arg VERSION="$VERSION" \ | |
| -t "$DOCKERHUB_USER/$APP_NAME:$VERSION-alpine" \ | |
| -t "$DOCKERHUB_USER/$APP_NAME:alpine" \ | |
| -f v5/Dockerfile \ | |
| . |
🤖 Prompt for AI Agents
In v5/build-distroless.sh around lines 18 to 25, the docker build command lacks
the --pull flag, which ensures the latest base image is fetched. Add the --pull
option to the docker build command to always pull the most recent Alpine base
image before building.
| set -e | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Recommend strict mode
Use set -euo pipefail (or set -euxo pipefail) instead of set -e to catch unset variables and pipeline errors.
🤖 Prompt for AI Agents
In v5/test-distroless.sh at lines 2 to 3, replace the existing 'set -e' with
'set -euo pipefail' to enable strict mode. This change will ensure the script
exits on unset variables and pipeline errors, improving robustness and error
detection.
| echo "🧹 Cleaning up existing test containers..." | ||
| docker rm -f "$CONTAINER_NAME" 2>/dev/null || true | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Ensure cleanup on exit
Add a cleanup trap so the test container is always removed:
trap 'docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1' EXIT🤖 Prompt for AI Agents
In v5/test-distroless.sh around lines 14 to 16, add a trap command to ensure the
test container is always removed on script exit. Insert the line trap 'docker rm
-f "$CONTAINER_NAME" >/dev/null 2>&1' EXIT near the beginning of the script to
guarantee cleanup happens regardless of how the script terminates.
| # Ghost Distroless Migration | ||
|
|
||
| This document describes the migration of the Ghost Docker image to use Google's distroless base image for enhanced security and reduced attack surface. | ||
|
|
||
| ## Overview | ||
|
|
||
| The Ghost Docker image now supports two runtime options: | ||
| - **Alpine-based** (original): Full-featured with shell access for debugging | ||
| - **Distroless** (new): Minimal, secure runtime without shell or package manager | ||
|
|
||
| ## Architecture | ||
|
|
||
| ### Multi-Stage Build Process | ||
|
|
||
| ``` | ||
| ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ | ||
| │ Build Stages │ │ Alpine Runtime │ │Distroless Runtime│ | ||
| │ │ │ │ │ │ | ||
| │ 1. mynode │───▶│ 5. final │ │ 6. distroless │ | ||
| │ 2. debug │ │ │ │ │ | ||
| │ 3. builder │ │ + Shell access │ │ + Minimal size │ | ||
| │ 4. (packages) │ │ + Debugging │ │ + Enhanced security│ | ||
| │ │ │ + Flexibility │ │ + No shell access │ | ||
| └─────────────────┘ └─────────────────┘ └─────────────────┘ | ||
| ``` | ||
|
|
||
| ### Key Changes | ||
|
|
||
| #### Base Image | ||
| - **From**: `node:20.19.2-alpine3.22` | ||
| - **To**: `gcr.io/distroless/nodejs20-debian12` | ||
|
|
||
| #### User Management | ||
| - **From**: `node` user (UID 1000) with `gosu` privilege dropping | ||
| - **To**: `nonroot` user (UID 65532) - no privilege dropping needed | ||
|
|
||
| #### Entrypoint Strategy | ||
| - **From**: Shell-based `docker-entrypoint.sh` with runtime content initialization | ||
| - **To**: Direct Node.js execution with build-time content initialization | ||
|
|
||
| ## Building Images | ||
|
|
||
| ### Build Both Versions | ||
| ```bash | ||
| ./v5/build-distroless.sh | ||
| ``` | ||
|
|
||
| ### Build Specific Versions | ||
| ```bash | ||
| # Alpine version (original) | ||
| docker build --target final -t ghostfire:alpine -f v5/Dockerfile . | ||
|
|
||
| # Distroless version (recommended for production) | ||
| docker build --target distroless -t ghostfire:distroless -f v5/Dockerfile . | ||
| ``` | ||
|
|
||
| ## Testing | ||
|
|
||
| ### Test Distroless Version | ||
| ```bash | ||
| ./v5/test-distroless.sh | ||
| ``` | ||
|
|
||
| ### Manual Testing | ||
| ```bash | ||
| # Run distroless container | ||
| docker run -d -p 2368:2368 --name ghost-test ghostfire:distroless | ||
|
|
||
| # Check if Ghost is running | ||
| curl http://localhost:2368 | ||
|
|
||
| # View logs (no shell access available) | ||
| docker logs ghost-test | ||
|
|
||
| # Clean up | ||
| docker rm -f ghost-test | ||
| ``` | ||
|
|
||
| ## Security Benefits | ||
|
|
||
| ### Distroless Advantages | ||
| - **Minimal Attack Surface**: No shell, package manager, or unnecessary binaries | ||
| - **Reduced CVE Exposure**: Fewer packages mean fewer potential vulnerabilities | ||
| - **Immutable Runtime**: Cannot install additional packages or modify system | ||
| - **Smaller Image Size**: ~50-100MB reduction compared to Alpine version | ||
|
|
||
| ### Security Comparison | ||
|
|
||
| | Feature | Alpine | Distroless | | ||
| |---------|--------|------------| | ||
| | Shell Access | ✅ bash/sh | ❌ None | | ||
| | Package Manager | ✅ apk | ❌ None | | ||
| | Debug Tools | ✅ Available | ❌ None | | ||
| | CVE Surface | 🟡 Medium | 🟢 Minimal | | ||
| | Image Size | 🟡 ~200MB | 🟢 ~150MB | | ||
| | Runtime Modification | 🔴 Possible | 🟢 Impossible | | ||
|
|
||
| ## Production Deployment | ||
|
|
||
| ### Recommended Usage | ||
| ```yaml | ||
| # docker-compose.yml | ||
| version: '3.8' | ||
| services: | ||
| ghost: | ||
| image: devmtl/ghostfire:distroless | ||
| ports: | ||
| - "2368:2368" | ||
| environment: | ||
| - NODE_ENV=production | ||
| volumes: | ||
| - ghost_content:/var/lib/ghost/content | ||
| restart: unless-stopped | ||
| security_opt: | ||
| - no-new-privileges:true | ||
| read_only: true | ||
| tmpfs: | ||
| - /tmp | ||
| ``` | ||
|
|
||
| ### Kubernetes Deployment | ||
| ```yaml | ||
| apiVersion: apps/v1 | ||
| kind: Deployment | ||
| metadata: | ||
| name: ghost-distroless | ||
| spec: | ||
| replicas: 1 | ||
| selector: | ||
| matchLabels: | ||
| app: ghost | ||
| template: | ||
| metadata: | ||
| labels: | ||
| app: ghost | ||
| spec: | ||
| securityContext: | ||
| runAsNonRoot: true | ||
| runAsUser: 65532 | ||
| fsGroup: 65532 | ||
| containers: | ||
| - name: ghost | ||
| image: devmtl/ghostfire:distroless | ||
| ports: | ||
| - containerPort: 2368 | ||
| securityContext: | ||
| allowPrivilegeEscalation: false | ||
| readOnlyRootFilesystem: true | ||
| capabilities: | ||
| drop: | ||
| - ALL | ||
| volumeMounts: | ||
| - name: ghost-content | ||
| mountPath: /var/lib/ghost/content | ||
| - name: tmp | ||
| mountPath: /tmp | ||
| volumes: | ||
| - name: ghost-content | ||
| persistentVolumeClaim: | ||
| claimName: ghost-content-pvc | ||
| - name: tmp | ||
| emptyDir: {} | ||
| ``` | ||
|
|
||
| ## Debugging | ||
|
|
||
| ### Distroless Debugging | ||
| Since distroless images don't have shell access, debugging requires different approaches: | ||
|
|
||
| #### 1. Use Debug Variant | ||
| ```bash | ||
| # Build with debug variant for troubleshooting | ||
| FROM gcr.io/distroless/nodejs20-debian12:debug AS distroless-debug | ||
| # ... rest of distroless stage | ||
| ``` | ||
|
|
||
| #### 2. Log Analysis | ||
| ```bash | ||
| # View container logs | ||
| docker logs <container-name> | ||
|
|
||
| # Follow logs in real-time | ||
| docker logs -f <container-name> | ||
| ``` | ||
|
|
||
| #### 3. External Debugging Tools | ||
| ```bash | ||
| # Use external tools to inspect running container | ||
| docker exec <container-name> cat /proc/1/status | ||
| docker inspect <container-name> | ||
| ``` | ||
|
|
||
| #### 4. Fallback to Alpine | ||
| For complex debugging, temporarily use the Alpine version: | ||
| ```bash | ||
| docker run -it --rm ghostfire:alpine sh | ||
| ``` | ||
|
|
||
| ## Migration Checklist | ||
|
|
||
| - [x] ✅ Multi-stage Dockerfile with distroless target | ||
| - [x] ✅ Content initialization moved to build stage | ||
| - [x] ✅ User management updated for nonroot user | ||
| - [x] ✅ Build scripts for both versions | ||
| - [x] ✅ Test scripts for validation | ||
| - [x] ✅ Documentation and deployment examples | ||
| - [ ] 🔄 Production testing and validation | ||
| - [ ] 🔄 CI/CD pipeline updates | ||
| - [ ] 🔄 Monitoring and alerting adjustments | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Common Issues | ||
|
|
||
| #### Content Permissions | ||
| If Ghost fails to start due to content permissions: | ||
| ```bash | ||
| # Check content ownership in Alpine version | ||
| docker run --rm -v ghost_content:/content ghostfire:alpine ls -la /content | ||
|
|
||
| # Fix permissions if needed | ||
| docker run --rm -v ghost_content:/content ghostfire:alpine chown -R 65532:65532 /content | ||
| ``` | ||
|
|
||
| #### Missing Content | ||
| If Ghost starts but content is missing: | ||
| ```bash | ||
| # Verify content initialization in build logs | ||
| docker build --target distroless --progress=plain -f v5/Dockerfile . 2>&1 | grep -i content | ||
| ``` | ||
|
|
||
| #### Performance Issues | ||
| Monitor startup time and resource usage: | ||
| ```bash | ||
| # Compare startup times | ||
| time docker run --rm ghostfire:alpine node --version | ||
| time docker run --rm ghostfire:distroless node --version | ||
| ``` | ||
|
|
||
| ## Support | ||
|
|
||
| For issues related to the distroless migration: | ||
| 1. Check the troubleshooting section above | ||
| 2. Review container logs for error messages | ||
| 3. Test with the Alpine version to isolate distroless-specific issues | ||
| 4. Use the debug variant for deeper investigation | ||
|
|
||
| ## References | ||
|
|
||
| - [Google Distroless Images](https://github.com/GoogleContainerTools/distroless) | ||
| - [Ghost.js Documentation](https://ghost.org/docs/) | ||
| - [Docker Multi-stage Builds](https://docs.docker.com/develop/dev-best-practices/dockerfile_best-practices/#use-multi-stage-builds) | ||
| - [Container Security Best Practices](https://cloud.google.com/architecture/best-practices-for-building-containers) No newline at end of file |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Improve Markdown formatting and readability
- Surround lists and headings with blank lines to satisfy lint rules (MD022, MD032).
- Specify languages for all fenced code blocks (e.g.,
bash,yaml) for syntax highlighting. - Ensure the document ends with a single trailing newline (MD047).
🧰 Tools
🪛 LanguageTool
[uncategorized] ~226-~226: Use a comma before ‘but’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...`` #### Missing Content If Ghost starts but content is missing: ```bash # Verify co...
(COMMA_COMPOUND_SENTENCE_2)
[style] ~246-~246: Consider a different adjective to strengthen your wording.
Context: ...fic issues 4. Use the debug variant for deeper investigation ## References - [Google...
(DEEP_PROFOUND)
🪛 markdownlint-cli2 (0.17.2)
8-8: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
15-15: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
29-29: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
30-30: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
33-33: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
34-34: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
37-37: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
38-38: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
43-43: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
44-44: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
48-48: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
49-49: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
59-59: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
60-60: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
64-64: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
65-65: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
81-81: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
82-82: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
100-100: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
101-101: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
121-121: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
122-122: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
167-167: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
170-170: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
171-171: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
177-177: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
178-178: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
186-186: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
187-187: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
193-193: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
195-195: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
215-215: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
217-217: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
225-225: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
227-227: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
232-232: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
234-234: Fenced code blocks should be surrounded by blank lines
null
(MD031, blanks-around-fences)
243-243: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
253-253: Files should end with a single newline character
null
(MD047, single-trailing-newline)
🤖 Prompt for AI Agents
In v5/README-distroless.md from lines 1 to 253, improve markdown formatting by
adding blank lines before and after all lists and headings to comply with lint
rules MD022 and MD032. Specify the language for every fenced code block (such as
bash, yaml) to enable proper syntax highlighting. Finally, ensure the document
ends with exactly one trailing newline to satisfy MD047.
| # Check content ownership in Alpine version | ||
| docker run --rm -v ghost_content:/content ghostfire:alpine ls -la /content | ||
|
|
||
| # Fix permissions if needed | ||
| docker run --rm -v ghost_content:/content ghostfire:alpine chown -R 65532:65532 /content | ||
| ``` | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Grammar: Insert missing comma
In the “Missing Content” snippet, add a comma before “but”:
-If Ghost starts but content is missing:
+If Ghost starts, but content is missing:Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In v5/README-distroless.md around lines 218 to 224, the sentence in the "Missing
Content" snippet is missing a comma before the conjunction "but." Edit the
sentence to insert a comma immediately before "but" to correct the grammar.
Introduces a new distroless build stage to the Dockerfile, providing a minimal and secure production runtime for Ghost.
This change includes:
docker-entrypoint.shfunctionality.distrolessstage based ongcr.io/distroless/nodejs20-debian12.nonrootownership (UID 65532) for enhanced security.node current/index.jsas the CMD, removing the need for a shell entrypoint in the distroless image.Summary by CodeRabbit
New Features
Documentation