Session termination anatomy: debugging LiveZilla, PHP, and Linux sockets
Back to blog

Session termination anatomy: debugging LiveZilla, PHP, and Linux sockets

Updated 9/20/2026 · Published 9/19/2026 · 4 min · Infrastructure

When managing production web servers (Apache, PHP-FPM, and Nginx), routine application events often involve detailed mechanics across lower operating system layers.

Recently, I investigated how LiveZilla handles session teardowns and active connection terminations. While user interfaces present a simple "close chat" button, the underlying lifecycle triggers PHP handlers, MySQL transactions, Linux kernel socket state transitions, and file descriptor management.

Below is an analysis of the system calls (syscalls), memory and I/O implications, and practical commands to diagnose lingering ghost sockets.


1. How session termination operates in practice#

In LiveZilla, sessions terminate through three main pathways:

A. Explicit operator or visitor command#

  1. The client browser dispatches a POST request to server.php with action flags (such as p_act=close_chat).
  2. PHP processes the request and executes an UPDATE in MySQL (lz_chat_posts and lz_chat_sessions), marking the chat as closed.
  3. In Long Polling mode, the pending polling request is answered and client-side JavaScript loops are halted.

B. Inactivity timeout (logical garbage collection)#

When a visitor abruptly closes the browser tab:

C. WebSockets transport layer#

When operating over WebSockets:


2. I/o footprint, filesystem, and session storage#

During conversation termination, several storage operations occur:

  1. Transcript persistence: Message buffers are serialized, moved to history tables, or dispatched via email. If debug logging is enabled, synchronous fopen() and fwrite() calls execute.
  2. PHP session cleanup and inodes: PHP's session_destroy() unlinks temporary session files (sess_*). If PHP session garbage collection (session.gc_probability) is misconfigured, unpurged legacy files can exhaust filesystem inodes.

3. Investigating hung connections in CLOSE_WAIT#

A frequent production anomaly occurs when the application marks a session closed, yet the underlying TCP connection remains in CLOSE_WAIT.

Why does CLOSE_WAIT occur?#

When the remote client closes a connection, its kernel transmits a FIN packet. The Linux server kernel responds with an ACK and places the local socket in CLOSE_WAIT. From this moment, the kernel waits for the application process (Apache or PHP-FPM) to issue an explicit close() syscall.

If PHP hangs on a database lock or exceeds execution timeouts, the socket remains open, consuming server worker connection slots.

Terminal diagnostic commands#

To inspect sockets stuck in CLOSE_WAIT on web ports idiomatically:

ss -tanp state close-wait '( sport = :http or sport = :https )'

To list open network descriptors for a specific remote IP (capturing Apache, PHP-FPM, or Nginx workers alike):

lsof -nPi | grep 192.0.2.10

To trace real-time system calls on a suspected PHP-FPM worker:

strace -p <PHP_FPM_PID> -e trace=network,file,desc

The strace output reveals the complete sequence: recvfrom() (reading the close payload), sendto() (persisting changes to MySQL), and the final close() releasing the socket descriptor.


4. Webserver and kernel tuning guidelines#

Two common edge cases require attention:

A. Application-level fixes (the only real remedy for CLOSE_WAIT)#

Because the Linux kernel does not implement an automatic timeout for sockets in CLOSE_WAIT, clearing them requires the userland application to invoke close():

B. Kernel TCP tuning (sysctl)#

# /etc/sysctl.d/99-tcp-tuning.conf
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_fin_timeout = 30

Apply immediately with sudo sysctl -p /etc/sysctl.d/99-tcp-tuning.conf.

Tracing these lifecycles using ss, lsof, and strace ensures applications terminate sessions cleanly without leaking socket descriptors or memory.


Conclusion#

In this article, I analyzed the complete lifecycle of a LiveZilla session termination — from MySQL updates to socket-level close() calls — demonstrating that persistent CLOSE_WAIT sockets are almost never a kernel bug, but an application failing to release descriptors. With well-calibrated PHP-FPM timeouts and active socket inspection, web servers maintain healthy worker pools under heavy concurrent workloads.

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

CC BY-NC

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments