Linux Signals: Understanding SIGTERM and SIGINT

How Shutdowns and Kills Affect Your Applications

What Are Linux Signals?

Think of Linux signals as a messaging system between your operating system and running programs (processes). Just like you might tap someone on the shoulder to get their attention, Linux uses signals to communicate with programs. These signals tell programs things like “please stop what you’re doing” or “time to shut down gracefully.”

In the DevOps world, understanding signals is crucial because you’ll frequently need to start, stop, restart, and manage applications running on servers. Knowing how these signals work helps you control applications properly without causing data loss or system instability.

The Two Most Important Signals: SIGTERM and SIGINT

SIGINT (Signal Interrupt) - The Polite “Please Stop”

What it is: SIGINT stands for “Signal Interrupt” and has the number 2 in the system.

How it works: This is the signal sent when you press Ctrl+C in your terminal. It’s like politely asking a program to stop what it’s doing.

Real-world example:

1
2
3
4
5
6
7
8
9
# Start a long-running command
$ ping google.com
PING google.com (172.217.160.142): 56 data bytes
64 bytes from 172.217.160.142: icmp_seq=0 ttl=118 time=12.4 ms
64 bytes from 172.217.160.142: icmp_seq=1 ttl=118 time=11.8 ms
# Press Ctrl+C to send SIGINT
^C
--- google.com ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss

What happens: The program receives the signal and can choose to:

  • Clean up its resources (close files, save data)
  • Display a summary (like the ping statistics above)
  • Shut down gracefully

SIGTERM (Signal Terminate) - The Gentle Shutdown Request

What it is: SIGTERM stands for “Signal Terminate” and has the number 15 in the system.

How it works: This is the default signal sent by the kill command. It’s like giving a program a formal notice to shut down.

Real-world example:

1
2
3
4
5
6
7
8
9
# Find a process ID
$ ps aux | grep nginx
user     12345  0.0  0.1  nginx: master process

# Send SIGTERM to gracefully shut down nginx
$ kill 12345
# or explicitly specify SIGTERM
$ kill -15 12345
$ kill -TERM 12345

What happens: The program receives the signal and typically:

  • Finishes processing current requests
  • Saves any important data
  • Closes network connections properly
  • Cleans up temporary files
  • Exits cleanly

Key Differences Between SIGINT and SIGTERM

AspectSIGINTSIGTERM
Common triggerCtrl+C in terminalkill command
PurposeInteractive interruptionProgrammatic termination
Default behaviorStop current operationGraceful shutdown
Can be ignored?Yes, by the programYes, by the program
Signal number215

Why These Signals Matter in DevOps

1. Application Deployment

When you deploy a new version of an application, you need to stop the old version first:

1
2
3
4
5
6
7
# Good practice - graceful shutdown
$ kill -TERM $(pgrep myapp)
$ sleep 5  # Give it time to shut down
$ ./myapp-new-version &

# Bad practice - forceful kill (can cause data loss)
$ kill -9 $(pgrep myapp)  # SIGKILL - cannot be ignored!

2. Container Management

Docker and Kubernetes use these signals extensively:

1
2
3
4
# Docker sends SIGTERM first, then SIGKILL after timeout
$ docker stop mycontainer

# In Kubernetes, pods receive SIGTERM before termination

3. Service Management

System services use these signals for proper lifecycle management:

1
2
3
4
5
# systemd sends SIGTERM to stop services gracefully
$ systemctl stop nginx

# Traditional init scripts often use kill commands
$ /etc/init.d/apache2 stop

How Programs Handle These Signals

Programs can be written to handle signals in different ways:

Well-Behaved Programs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import signal
import sys
import time

def signal_handler(signum, frame):
    print(f"Received signal {signum}")
    print("Cleaning up...")
    # Close database connections
    # Save current work
    # Clean up temporary files
    print("Goodbye!")
    sys.exit(0)

# Register signal handlers
signal.signal(signal.SIGINT, signal_handler)   # Handle Ctrl+C
signal.signal(signal.SIGTERM, signal_handler)  # Handle kill command

# Main program loop
while True:
    print("Working...")
    time.sleep(1)

Poorly-Behaved Programs

Some programs might:

  • Ignore these signals completely
  • Take too long to respond
  • Not clean up properly before exiting

The Nuclear Option: SIGKILL

When SIGTERM and SIGINT don’t work, there’s SIGKILL (signal 9):

1
2
3
# The forceful approach - cannot be ignored
$ kill -9 12345
$ kill -KILL 12345

Warning: SIGKILL immediately terminates a process without giving it a chance to clean up. This can lead to:

  • Data corruption
  • Orphaned resources
  • Incomplete transactions
  • System instability

Practical DevOps Scenarios

Scenario 1: Deploying a Web Application

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# Step 1: Find the current process
$ ps aux | grep mywebapp
user 12345 mywebapp

# Step 2: Send graceful shutdown signal
$ kill -TERM 12345

# Step 3: Wait for graceful shutdown
$ sleep 10

# Step 4: Check if it's still running
$ ps aux | grep mywebapp

# Step 5: If still running, force kill
$ kill -9 12345

# Step 6: Start new version
$ ./mywebapp-v2 &

Scenario 2: Handling Stuck Processes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Try graceful shutdown first
$ kill -TERM 12345

# Wait a reasonable time
$ sleep 30

# Check if process is still alive
$ kill -0 12345 2>/dev/null && echo "Still running" || echo "Process stopped"

# If still running, escalate to SIGKILL
$ kill -9 12345

Best Practices for DevOps

  1. Always try SIGTERM first - Give programs a chance to shut down gracefully
  2. Wait before escalating - Allow reasonable time for cleanup (10-30 seconds)
  3. Use timeouts - Don’t wait forever; have a plan B
  4. Monitor signal handling - Ensure your applications handle signals properly
  5. Test your shutdown procedures - Verify that your apps clean up correctly
  6. Document signal behavior - Know how your applications respond to different signals

Common Tools and Commands

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Send signals to processes
$ kill -TERM 12345        # Send SIGTERM
$ kill -INT 12345         # Send SIGINT
$ kill -9 12345           # Send SIGKILL (force)

# Kill processes by name
$ pkill -TERM nginx       # Send SIGTERM to all nginx processes
$ killall -INT python     # Send SIGINT to all python processes

# Check if a process is still running
$ kill -0 12345 && echo "Running" || echo "Not running"

# List all available signals
$ kill -l

Troubleshooting Signal Issues

Problem: Process doesn’t respond to SIGTERM

  • Solution: Check if the program properly handles the signal
  • Workaround: Use SIGKILL as last resort

Problem: Application doesn’t clean up properly

  • Solution: Improve signal handling in your application code
  • Workaround: Add cleanup scripts to run after termination

Problem: Containers take too long to stop

  • Solution: Optimize your application’s shutdown process
  • Workaround: Reduce Docker’s stop timeout

Conclusion

Understanding SIGTERM and SIGINT is fundamental for anyone working in DevOps. These signals are your primary tools for managing application lifecycles in a controlled, predictable way. By using them properly, you can:

  • Deploy applications without data loss
  • Manage server resources efficiently
  • Troubleshoot problematic processes
  • Maintain system stability

Remember: always be gentle first (SIGTERM/SIGINT), then firm if necessary (SIGKILL). Your applications, your data, and your users will thank you for taking the time to shut things down properly.

The key to mastering Linux signals is practice. Start experimenting with them in safe environments, and soon you’ll be managing complex production systems with confidence!