Skip to content

Apache as the Main Process in a Docker Container #698

Description

@4Syno

Hi, the ownCloud Docker base image currently starts Apache through the Debian/Ubuntu apachectl wrapper:

exec apachectl -f /etc/apache2/apache2.conf -DFOREGROUND

Source: https://github.com/owncloud-docker/base/blob/master/v24.04/overlay/usr/bin/server#L21

apachectl is a shell script. It starts /usr/sbin/apache2 without replacing itself via exec. As a result, the process tree inside the container looks like this:

PID 1    /bin/sh /usr/sbin/apachectl -f /etc/apache2/apache2.conf -DFOREGROUND
PID 143  /usr/sbin/apache2 -f /etc/apache2/apache2.conf -DFOREGROUND

Docker sends the container stop signal to PID 1. In this process layout, SIGTERM reaches the apachectl shell, but not the actual Apache process.

Apache therefore continues running until Docker reaches the configured stop timeout and sends SIGKILL.

This appears to be the same underlying problem reported previously in:

Reproduction, Tested with:

owncloud/server:11.0.0
Base image: Ubuntu 24.04
Stop timeout: 60 seconds

The Docker events were:

kill owncloud signal=15
kill owncloud signal=9
die owncloud

Measured timing:

SIGTERM -> SIGKILL: 60.113 seconds
SIGKILL -> die: 0.203 seconds

During those 60 seconds, Apache continued to serve successful health checks.

On the next start, Apache reported:

AH00098: pid file /var/run/apache2/apache2.pid overwritten -- Unclean shutdown of previous Apache run?

The signal dispositions also showed that the apachectl shell running as PID 1 did not catch SIGTERM, while the Apache child process did.

Proposed change

Start Apache directly instead of using apachectl as the final process:

echo "Starting apache daemon directly..."
exec /usr/sbin/apache2 -f /etc/apache2/apache2.conf -D FOREGROUND

The resulting process tree is:

PID 1 /usr/sbin/apache2 -f /etc/apache2/apache2.conf -D FOREGROUND

Docker can then deliver SIGTERM directly to Apache:

docker stop
|
v
SIGTERM
|
v
apache2 (PID 1)
|
v
Apache shuts down
|
v
container exits

I tested this change first in an isolated fresh container and then in a production ownCloud 11.0.0 container using the normal ownCloud entrypoint and initialization path.

After the change, the Docker events were:

kill  owncloud  signal=15
die   owncloud

Result:

Stop duration: 0.227 seconds
Exit code:     0
OOMKilled:     false
SIGKILL:       not sent

Apache logged the expected shutdown:

AH00169: caught SIGTERM, shutting down

The container then started normally:

installed: true
version: 11.0.0.0
maintenance: false
needsDbUpgrade: false
health: healthy

There was no AH00098 unclean-shutdown warning after the restart.
The test was repeated twice in an isolated container, with stop durations of 0.238 and 0.191 seconds.

Tested workaround:

I tested the change without rebuilding the image by replacing /usr/bin/server with a read-only bind mount:

services:
  owncloud:
    volumes:
      - ${OWNCLOUD_DATA_PATH:?Set OWNCLOUD_DATA_PATH in .env}:/mnt/data:Z
      - ./overrides/server:/usr/bin/server:ro,Z

The replacement script keeps the existing entrypoint initialization and pre-server hooks. Only the final Apache invocation changes from:

exec apachectl -f /etc/apache2/apache2.conf -DFOREGROUND

to:

exec /usr/sbin/apache2 -f /etc/apache2/apache2.conf -D FOREGROUND

This bind mount is only a workaround used to validate the proposed upstream change. The preferred fix would be in the base image itself.

Reference implementation:

The official Apache Docker image follows the same process model. Its httpd-foreground script performs the required setup and then replaces itself with the actual Apache daemon:

exec httpd -DFOREGROUND "$@"

https://github.com/docker-library/httpd/blob/master/2.4/httpd-foreground

The exact setup differs because ownCloud uses the Ubuntu/Debian Apache packages, but the process-management principle is the same: the actual web server should be the container's main process.

Compatibility consideration

Starting /usr/sbin/apache2 directly bypasses setup performed by the Debian/Ubuntu apachectl wrapper, such as runtime-directory preparation and ulimit handling.

In the tested ownCloud startup path, the required Apache environment was already prepared and exported by the existing entrypoint scripts, and both a fresh container and the production container started successfully.

The base image should nevertheless verify that all required runtime directories, environment variables, permissions, and limits are prepared before the final exec /usr/sbin/apache2.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions