Skip to content

Add IdleTerminationTimeout to terminate Apache process when all workers are idle (useful for socket activation) #529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: trunk
Choose a base branch
from

Conversation

AlexAT
Copy link

@AlexAT AlexAT commented Apr 13, 2025

This is another patch that add somewhat handy feature when Apache is used with systemd socket activation.

Now, when Apache activates from systemd.socket, it has no means to terminate. So it will keep running and running and running, consuming resources even when there are no requests to the server.

This patch adds a new global configuration option, IdleTerminationTimeout, that allows to specify number of seconds we terminate in after all workers (processes or threads) become idle, serving no requests. Idle detection happens in idle server maintenance happening each second, so this timeout will happen X seconds (0 is possible) after the server detects itself as idle. If the server becomes non-idle during timeout interval, the interval is reset anew.

The termination is immediate and ungraceful (SIGTERM-alike), as all children/threads are idle and we do not need to clean up / wait for anything to complete.

So, after server detects itself as idle, times out and terminates, systemd socket activation starts again, waiting for new connections and potentially starting the server again when request comes.

Caveat: with socket activation or whatever there may be a split millisecond when the server is terminated but systemd socket activation is not yet running again. I've personally never hit this during my testing, but theoretically speaking, this is clearly possible. So the best way of using that is running socket-activated Apache behind haproxy doing TCP-only checks (so to not trigger process startup) that can potentially retry connection in case it hits this problematic split millisecond. This caveat is not related to any patch internals and cannot be amended httpd server wise.

The general idea behind doing that one was to move from running Apache+mpm_itk (which is getting obsolete and has its own issues) on shared hosting server to running single Apache instance per each hosted user, meaning credentials and everything in Apache configuration becomes tunable per user. The problem with that is that you need to run thousands of Apache instances even when there is only a small amount of active sites, consuming hellish bloody lot of memory. This one is intended to help to terminate unused httpd instances fast and only spawn them back by socket activation when specific site is becoming active again.

Also usable on low-memory VMs, raspberries, personal and other installations where you want to conserve RAM and only run httpd when you rarely access it.

How to use?

If you have Apache with systemd socket activation, this should be immediately usable.

Just add IdleTerminationTimeout 60 (or some other timeout value) to the Apache mpm configuration to make server terminate when completely idle, relinquishing control back to socket activation.

I used the following unit/socket files to test it (they use configuration from /etc/http/port-X/httpd.conf for specific port X.

[Unit]
Description=Apache httpd server socket (port %i)

[Socket]
ListenStream=%i
Accept=no
FlushPending=no
DeferAcceptSec=5
Service=httpd-socket@%i.service
TriggerLimitIntervalSec=0

[Install]
UpheldBy=sockets.target
[Unit]
Description=Apache httpd socket-activated daemon (port %i)
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=notify
Environment=LANG=C
ExecStartPre=-/usr/bin/rm -f /etc/httpd/port-%i/run/httpd.pid
ExecStart=/opt/httpd/bin/httpd -f /etc/httpd/port-%i/httpd.conf -DFOREGROUND
KillSignal=SIGWINCH
KillMode=mixed
Restart=no
StartLimitInterval=0
SyslogFacility=local1
LogLevelMax=notice
DevicePolicy=closed
KeyringMode=private
LockPersonality=yes
MemoryDenyWriteExecute=no
PrivateDevices=yes
PrivateTmp=true
ProtectClock=yes
ProtectControlGroups=yes
ProtectHome=read-only
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectSystem=yes
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native

#CPUAccounting=true
#MemoryAccounting=true
#BlockIOAccounting=true

LimitNOFILE=16384

# these defaults are not set, set them from service modifications
#CPUShares=1024
#MemoryLimit=1024M
#BlockIOWeight=500

[Install]
WantedBy=multi-user.target

This should be relatively easy to adjust for testing this kind of socket activation with idle termination.

@AlexAT
Copy link
Author

AlexAT commented Apr 13, 2025

Just to mention, this only touches base prefork/worker/event MPMs. Simple/MotorZ are not touched as they seem to be experimental and are not documented yet.

@AlexAT
Copy link
Author

AlexAT commented Apr 13, 2025

Attaching raw patch against 2.4.63 that may be used to compile your versions of it quickly.

httpd-2.4-idle-termination-2.4.63-20250413.patch

@johnsonomotunde
Copy link

Great work AlexAT,
Assuming you haven't save your work, would you still be able to get access to your project?

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.

2 participants