Skip to content

fix(s6): make web services wait for their config oneshots (fixes root-mode startup race)#689

Open
LorenzoRogai wants to merge 1 commit into
serversideup:mainfrom
LorenzoRogai:fix/root-mode-fpm-nginx-startup-race
Open

fix(s6): make web services wait for their config oneshots (fixes root-mode startup race)#689
LorenzoRogai wants to merge 1 commit into
serversideup:mainfrom
LorenzoRogai:fix/root-mode-fpm-nginx-startup-race

Conversation

@LorenzoRogai

Copy link
Copy Markdown

Problem

When a container built on the s6 images runs as root, php-fpm and the web server (nginx/apache2) are brought up in parallel with the entrypoint oneshots that configure them — the long-running services declare no dependency on those oneshots.

As root this is a race:

  • php-fpm reads its pool config before 5-fpm-pool-user appends user/group, failing with:
    ALERT: [pool www] user has not been defined
    ERROR: failed to post process the configuration
    ERROR: FPM initialization failed
    
  • the web server starts before 10-init-webserver-config renders its config, e.g. nginx: [emerg] open() "/etc/nginx/nginx.conf" failed (2: No such file or directory).

s6 restarts the crashed longruns, so the container eventually recovers — which is exactly why this is intermittent and hard to reproduce (see #425). But it emits alarming errors on every boot that loses the race, slows startup, and leaves a brief window with no service.

Fix

docker-php-serversideup-s6-init now adds a dependency from each web-facing longrun to the entrypoint oneshot that configures it, after the oneshots are created:

add_startup_dependency php-fpm 5-fpm-pool-user
add_startup_dependency nginx 10-init-webserver-config
add_startup_dependency apache2 10-init-webserver-config

The entrypoint oneshots are chained in alphabetical order, so depending on one transitively waits for all earlier ones (php-fpm5-fpm-pool-user; the web servers → 10-init-webserver-config, which already sits after 5-*). Each dependency is added only when both the service and the oneshot exist, so cli/fpm/frankenphp images — and images where a user removes a script — are unaffected. It does not make services wait on later app oneshots such as 50-laravel-automations, so a failing app hook won't block the web server from starting.

Testing

  • Reproduced as root on 8.4-fpm-nginx-alpine: first boot shows the fpm + nginx errors, then self-heals.
  • With the fix: 3/3 clean boots — no errors, php-fpm/nginx reach "ready to handle connections" on the first attempt.
  • Rootless (default) behavior is unchanged (5-fpm-pool-user is a no-op when not root; the dependency just orders startup).

Refs #425

…-mode startup race

When a container built on the s6 images runs as root, php-fpm and the web
server (nginx/apache2) are brought up in parallel with the entrypoint
oneshots that configure them, because the long-running services have no
dependency on those oneshots.

As root this races:
- php-fpm reads its pool before `5-fpm-pool-user` appends `user`/`group`,
  failing with "ALERT: [pool www] user has not been defined" ->
  "ERROR: FPM initialization failed".
- the web server starts before `10-init-webserver-config` renders its
  config (e.g. nginx: open() "/etc/nginx/nginx.conf" failed).

s6 restarts the crashed services so the container eventually recovers, which
is why the failure is intermittent and hard to reproduce (see discussion
serversideup#425), but it emits alarming errors, slows startup, and leaves a brief window
with no service.

docker-php-serversideup-s6-init now adds a dependency from each web service to
the entrypoint oneshot that configures it, appending to the existing flat
`dependencies` file. The oneshots are chained in alphabetical order, so
depending on one transitively waits for all earlier ones (php-fpm ->
5-fpm-pool-user; nginx/apache2 -> 10-init-webserver-config). Entries are
de-duplicated and appended newline-safely (nginx's shipped `dependencies` has
no trailing newline). Dependencies are only added when both the service and
the oneshot exist, so cli/fpm/frankenphp images and images that remove a
script are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@LorenzoRogai
LorenzoRogai force-pushed the fix/root-mode-fpm-nginx-startup-race branch from 46d9ba2 to c7befe0 Compare July 22, 2026 14:05
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