Production Incident Review

Incident Review: A Service Blocked by a Kernel Semaphore Limit

An application service refused to start on RHEL 8 because the kernel's semaphore limit was lower than it needed, and its start-up script wasn't allowed to raise the limit itself. Why that write failed, and why the right fix belongs in the operating system's configuration, not in the service.

Module: 22 of 25 Level: Production Incident Review Time: 9 min read Published:
All modules in this track (25)

About this review. This is a real incident from a production platform running on RHEL 8 servers in AWS, written up from the engineering team's notes. Every server name, path, account, and product name is replaced with a generic one; the error, the values, and the fix are otherwise as recorded.

EnvironmentPre-production (QA), RHEL 8
When2025
Detected byThe service failing to start
ImpactThe application service couldn't start on the affected servers
Root causeThe kernel's semaphore limit (SEMMSL) was 1024; the application needs at least 2048
FixRaised the limit persistently at the operating-system level with sysctl. No application change was needed
Context

What a semaphore limit is

A semaphore is a counter the kernel keeps so that processes can coordinate, for example to take turns using shared memory. This application uses System V semaphores, the older interface that comes with fixed, system-wide limits. On Linux, those limits are four numbers in one setting:

sysctl kernel.sem
# kernel.sem = 1024   32000   32   256
#              SEMMSL SEMMNS  SEMOPM SEMMNI
FieldMeaning
SEMMSLMaximum semaphores in one set
SEMMNSMaximum semaphores on the whole system
SEMOPMMaximum operations in a single call
SEMMNIMaximum number of semaphore sets

The application requires SEMMSL of at least 2048. The servers had 1024.

Symptom

The service won't start

The service, run by systemd with an ExecStartPre= start-up script, failed every time it started. The investigation used the same steps as systemd: Services, Timers & Logs:

systemctl cat app-sync.service            # the unit, including its ExecStartPre script
journalctl -u app-sync.service -n 50 --no-pager
su - appsvc -c "/opt/app/sbin/app-sync"   # run the binary by hand, as the service user
Scroll sideways to see the full diagram →
Root cause

A start-up script that changed the kernel

The unit's start-up script tried to fix the limit itself, by writing the new value straight into the kernel:

/proc/sys/kernel/sem

That write failed with Permission denied. The team's notes attribute this to RHEL 8's security controls, which restrict services from changing kernel settings at runtime (systemd service hardening or SELinux). So the limit stayed at 1024, and the application refused to start.

The deeper problem is the design, not the denied write. A kernel setting that the application needs is a property of the server, and it should be in place before any service starts. A service that edits the kernel on every start is fragile (it fails exactly as it did here) and it needs far more privilege than an application should have.

Fix

Set it once, at the operating-system level

The limit was raised persistently with sysctl, keeping the other three values unchanged:

kernel.sem = 2048 32000 32 256

In practice that means a file under /etc/sysctl.d/, applied at boot and loaded immediately with sysctl --system, as in Performance & Kernel Tuning. With the limit in place before the service starts, its start-up script has nothing to change. The notes record that no application code changes were needed.

How to check a server yourself:

sysctl kernel.sem          # the current limits
ipcs -ls                   # the same limits, labelled
ipcs -s                    # semaphore sets that exist now, and their owners
Scroll sideways to see the full diagram →
Prevention

Lessons

  • Put kernel requirements in provisioning, next to the rest of the server's configuration: a sysctl.d file in the image or managed by configuration management, with a comment saying which application needs it and why.
  • Treat "a service writes to /proc/sys" as a warning sign. It means the service needs root-level power to start, and it hides a server requirement inside application scripts.
  • Read the vendor's system requirements as a checklist when building a new environment, including kernel limits, not only CPU, memory, and disk.
Practise it

Where this track teaches the skills

Check yourself

Pop quiz

Five questions: three on the ideas in this review, and two scenarios where you apply them. The order changes every time you take it, and 4 out of 5 passes.

5 questions. Decide on your answer to each, then check it.

  1. Concept

    In kernel.sem = 1024 32000 32 256, which number is SEMMSL?

    1. 1024, the maximum number of semaphores in one set
    2. 32000, the system-wide total
    3. 32, the operations per call
    4. 256, the number of sets
    Show answer

    1024, the maximum number of semaphores in one set

    The four values are SEMMSL, SEMMNS, SEMOPM, and SEMMNI, in that order. This application needed the first one raised to 2048.

  2. Concept

    Why is a service writing to /proc/sys at start-up a fragile design?

    1. /proc/sys is read-only on every system
    2. It needs root-level power, and security controls can block it, so the service fails to start
    3. Values written there are ignored
    4. It only works on Ubuntu
    Show answer

    It needs root-level power, and security controls can block it, so the service fails to start

    Kernel settings belong to the server, not to one service. Hardened systems restrict such writes, and a service shouldn't need that much privilege to start.

  3. Concept

    How do you make a kernel setting survive a reboot?

    1. echo it into /proc/sys once
    2. Put it in a file under /etc/sysctl.d/ and load it with sysctl --system
    3. Add it to the service's ExecStartPre
    4. Write it in ~/.bashrc
    Show answer

    Put it in a file under /etc/sysctl.d/ and load it with sysctl --system

    /proc/sys holds only the running value. Files in /etc/sysctl.d/ are applied at every boot, before services start.

  4. Scenario

    A vendor's application needs a larger kernel.sem on every new server. Where should that requirement live?

    1. In a wiki page for engineers to apply by hand
    2. In the application's start-up script
    3. In the server image or configuration management, as a sysctl.d file with a comment
    4. Nowhere; set it when the application fails
    Show answer

    In the server image or configuration management, as a sysctl.d file with a comment

    Provisioning it with the server makes every new server correct before the application starts, and the comment records why it exists.

  5. Scenario

    A service's start-up script fails with "Permission denied" writing a kernel setting, even though the service runs as root. What's a likely explanation on RHEL 8?

    1. The disk is full
    2. The root password expired
    3. Security controls such as systemd hardening or SELinux are confining the service
    4. The kernel setting doesn't exist
    Show answer

    Security controls such as systemd hardening or SELinux are confining the service

    Root inside a confined service isn't all-powerful. Hardening options and SELinux policies can block writes to kernel settings, which is why the requirement belongs outside the service.

Questions about this learning path?

I'm happy to go into more detail on any of this: how to practise troubleshooting safely, which fundamentals matter most early on, or how a break-fix lab is put together.

anupojuprudhvi@gmail.com