Skip to content

🐛 fix: prefork children exit immediately in Docker containers#4133

Merged
ReneWerner87 merged 2 commits into
gofiber:mainfrom
meruiden:fix/prefork-pid1-detection
Mar 15, 2026
Merged

🐛 fix: prefork children exit immediately in Docker containers#4133
ReneWerner87 merged 2 commits into
gofiber:mainfrom
meruiden:fix/prefork-pid1-detection

Conversation

@meruiden

@meruiden meruiden commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #4132

watchMaster() kills child processes when os.Getppid() == 1, assuming the parent died and the child was reparented to init. In Docker containers, the master process commonly is PID 1, so children immediately see Getppid() == 1 and call os.Exit(1) after the first 500ms tick.

Fix

Store the original parent PID at startup and detect parent death by checking if the parent PID changed, rather than comparing against a hardcoded value of 1.

// Before
for range time.NewTicker(watchInterval).C {
    if os.Getppid() == 1 {
        os.Exit(1)
    }
}

// After
masterPID := os.Getppid()
for range time.NewTicker(watchInterval).C {
    if os.Getppid() != masterPID {
        os.Exit(1)
    }
}

Testing

  • All existing prefork tests pass
  • Verified prefork works in Docker containers without needing an external init process (tini)

watchMaster() assumed PID 1 always means the parent died and the child
was reparented to init. In Docker containers, the master process itself
commonly runs as PID 1, causing all children to exit after the first
500ms health check.

Fix by storing the original parent PID at startup and detecting parent
death by checking if the parent PID changed, rather than comparing
against a hardcoded value of 1.

Fixes gofiber#4132

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical bug affecting prefork child processes when running in Docker containers. Previously, child processes would prematurely exit due to an incorrect assumption about the master process's PID. The fix introduces a more robust mechanism for detecting master process termination, ensuring stable operation within containerized environments.

Highlights

  • Docker Compatibility Fix: Addressed an issue where prefork child processes in Docker containers would immediately exit. This occurred because the master process, often PID 1 in Docker, caused os.Getppid() == 1 to incorrectly trigger an exit condition for children.
  • Improved Parent Process Tracking: Modified the watchMaster() function to store the initial parent PID at startup. It now detects master process death by checking if the parent PID changes from this original value, providing a more robust mechanism than hardcoding a check for PID 1.
Changelog
  • prefork.go
    • Updated the watchMaster function to correctly identify when the master process has exited, especially in containerized environments where the master might be PID 1.
    • Changed the logic from checking os.Getppid() == 1 to comparing os.Getppid() against the initial masterPID.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@ReneWerner87 ReneWerner87 added this to v3 Mar 7, 2026
@ReneWerner87 ReneWerner87 added this to the v3 milestone Mar 7, 2026
@coderabbitai

coderabbitai Bot commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 43101173-b561-461a-8852-87a13aede197

📥 Commits

Reviewing files that changed from the base of the PR and between a2e1f82 and f40dfd0.

📒 Files selected for processing (1)
  • prefork.go

Walkthrough

Replaces the hardcoded PID-1 check with dynamic parent tracking: the child captures the master's PID at startup and uses it in watchMaster(masterPID int) to detect parent death by comparing subsequent Getppid() values against the stored master PID (Windows code path adjusted to use provided PID).

Changes

Cohort / File(s) Summary
Prefork parent tracking
prefork.go
Reworked master-death detection: child captures master's PID and calls watchMaster(masterPID int); watchMaster now compares os.Getppid() against the stored masterPID (Windows path updated to use masterPID). Removed hardcoded ppid == 1 check.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I stored the parent's number tight,
No longer fooled by PID-one's might,
Children watch the PID they knew,
And only exit when it’s true.
A tiny hop, a safer night.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the bug fix (prefork children exiting immediately) and specifies the context (Docker containers), accurately summarizing the main change.
Description check ✅ Passed The PR description provides a clear problem statement, specific before/after code comparison, and testing verification. However, it does not follow the repository's required template structure with checklist items.
Linked Issues check ✅ Passed The code changes implement the exact fix suggested in issue #4132: capturing the original parent PID at startup and detecting parent death by PID change rather than comparing against hardcoded 1.
Out of Scope Changes check ✅ Passed All changes in prefork.go are directly related to the parent PID detection mechanism described in issue #4132, with no extraneous modifications.
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 unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request fixes a bug where child processes in a prefork setup would exit immediately when running inside a Docker container. The fix correctly changes the logic to check if the parent PID has changed, instead of checking if it is 1. While the core logic of the fix is correct for its intended purpose, I've identified a race condition where the child process may fail to detect the master's death if the master exits at a specific moment during child process initialization. I've left a comment with a suggestion to make the implementation more robust by eliminating this race condition.

Comment thread prefork.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes prefork child processes exiting immediately when running inside Docker containers where the master process is commonly PID 1, by changing the master-liveness check in watchMaster() to detect PPID changes instead of comparing against a hardcoded PID value.

Changes:

  • Update watchMaster() to store the initial parent PID and exit only when the PPID changes.
  • Expand inline documentation to explain the reparenting-based detection approach and the Docker PID 1 scenario.

Comment thread prefork.go
Comment thread prefork.go Outdated
Comment thread prefork.go Outdated
- Capture masterPID in caller before spawning goroutine to eliminate
  race condition window between goroutine scheduling and Getppid() call
- Use masterPID for Windows FindProcess path too, for consistency
- Update comment to mention subreapers, not just init/PID 1

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov

codecov Bot commented Mar 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.05%. Comparing base (c493e59) to head (f40dfd0).
⚠️ Report is 26 commits behind head on main.

Files with missing lines Patch % Lines
prefork.go 60.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4133      +/-   ##
==========================================
- Coverage   91.10%   91.05%   -0.06%     
==========================================
  Files         119      119              
  Lines       11385    11386       +1     
==========================================
- Hits        10372    10367       -5     
- Misses        642      647       +5     
- Partials      371      372       +1     
Flag Coverage Δ
unittests 91.05% <60.00%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

@ReneWerner87 ReneWerner87 merged commit e6353ef into gofiber:main Mar 15, 2026
23 of 24 checks passed
@welcome

welcome Bot commented Mar 15, 2026

Copy link
Copy Markdown

Congrats on merging your first pull request! 🎉 We here at Fiber are proud of you! If you need help or want to chat with us, join us on Discord https://gofiber.io/discord

@github-project-automation github-project-automation Bot moved this to Done in v3 Mar 15, 2026
@ReneWerner87

Copy link
Copy Markdown
Member

@meruiden How does the fasthttp prefork work? Is there the same issue there? We’re planning to switch directly to this version soon.

@meruiden

Copy link
Copy Markdown
Contributor Author

@ReneWerner87 I checked fasthttp's prefork — it doesn't have the same issue, but it has the inverse problem.

fasthttp's prefork.go has no watchMaster() at all. Children never poll os.Getppid(), so they won't falsely exit in Docker (the bug this PR fixes). However, that means if the master process dies unexpectedly, children become orphans — they get reparented to PID 1 and keep running silently with no supervision.

I've opened a PR to fix it: valyala/fasthttp#2158

It uses the same pattern as this PR — store the initial PPID at startup and exit when it changes.

@ReneWerner87 ReneWerner87 removed this from the v3 milestone Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

🐛 [Bug]: Prefork children exit immediately in Docker containers (PID 1 detection)

4 participants