← Back to list

The SIGTERM vs. SIGINT Trade-Off: A Deep Dive into PgBouncer Rolling Restarts

Learn how to choose between SIGTERM and SIGINT to perform graceful PgBouncer rolling restarts without causing application downtime.

Leo Lin · 2025-08-19 16:35 · 0 claps · 3.6 min read
#postgresql #kubernetes #pgbouncer #devops #database
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud

The SIGTERM vs. SIGINT Trade-Off: A Deep Dive into PgBouncer Rolling Restarts

A few months ago, I published *Supercharge Your PostgreSQL with PgBouncer: A Comprehensive Guide to Performance and Efficiency*, a guide based on our real-world integration. While that story was a success, the journey didn’t end there. We soon ran into a perplexing issue with PgBouncer — one I believe is worth sharing.

The Mystery of the Midnight Outage

One night, an alert fired, signaling a partial service failure. But the system recovered within minutes — so quickly that our on-call engineer didn’t even have time to assess the full scope of the impact. Digging into the logs, we found the key point: a sudden spike in database connection failures. The strange part? Our CloudSQL database instance was perfectly healthy. No unusual traffic, no locks, no failover, no upgrades, no maintenance. It was completely stable.

Then, we spotted a clue. The age of our PgBouncer pod on Kubernetes had reset. It had clearly been restarted. A few tests in our staging environment confirmed our suspicion: restarting PgBouncer was the root cause. But that just led to a bigger question: Why?

Digging Deeper: How PgBouncer Handles a Rolling Restart

Since the problem was tied to restarts, our investigation immediately led us to our Kubernetes deployment configuration. I checked the deployment YAML and saw that the setup was likely based on *this popular example*. But as I checked the file, an unusual detail caught my eye:

# ...
lifecycle:
  preStop:
    exec:
      # Allow existing queries clients to complete within 120 seconds
      command: ["/bin/sh", "-c", "killall -INT pgbouncer && sleep 120"]

Instead of using the more common SIGTERM signal to terminate the process, our preStop hook was sending SIGINT. This immediately suggested that PgBouncer might handle these two signals differently.

To understand why, I take a quick at PgBouncer’s source code (*handle_sigterm, [handle_sigint](https://github.com/pgbouncer/pgbouncer/blob/c8772d4f35dd4d241ec17f27906ff9d9d8231fc7/src/main.c#L552C13-L552C26)*). Let’s break it down! PgBouncer sits between your application and your database, managing two sets of connections: one to the application and one to the database. When an application needs a connection, PgBouncer assigns it one from its pool.

This creates a shutdown dilemma: Which connections do you close first? The answer isn’t simple — it’s a trade-off based on your priorities.

  • SIGTERMPrioritizing Application Availability: When PgBouncer receives SIGTERM, it closes the application-facing connections first. This ensures any in-flight requests can complete using their existing database connections. The downside? During a rolling restart, new and old PgBouncer instances exist simultaneously, which can cause a temporary spike in connections to your database. This strategy protects the application.
  • SIGINTPrioritizing Database Stability: When it receives SIGINT, PgBouncer does the opposite: it closes the database-facing connections first. This guarantees you won't exceed your database's connection limit during a restart. The risk? An application might be holding a connection that PgBouncer has just severed from the database, leading to connection errors. This strategy protects the database.

So, Which Strategy Should You Choose?

There’s no single right answer, but I can walk you through our decision-making process. We evaluated our environment by answering two key questions:

  1. How much database connection buffer do you have at peak time?
  2. How long do your application connections live?

First, we analyzed our database connection usage during peak traffic. Our metrics showed that we had plenty of available connections, far more than the pool size of a single PgBouncer instance. This meant we could safely tolerate the temporary connection spike caused by SIGTERM during a rolling update without risking a database overload.

Next, we checked our application’s connection lifetime(e.g., pool_recycle SQLAlchemy). It was set to 60 seconds. This told us that within a minute, all application connections would naturally be closed and reopened, forcing them to migrate to the new PgBouncer pod.

With this information, we made our choice: SIGTERM was the right strategy for us. Then, we configured our preStop hook to give the system enough time to transition gracefully. We set the sleep duration to 70 seconds, based on this formula:

60s (connection lifetime) + 5s * 2 (readiness probe buffer) = 70s

The extra 10 seconds ensures that Kubernetes’ readiness probe has enough time to fail on the old pod, mark it as unhealthy , and remove it from the service endpoint before it is fully terminated. This prevents any traffic from being sent to a pod that is in the process of shutting down.

Appendix: Why We Run PgBouncer on Kubernetes

We’ve occasionally been asked why we chose to deploy PgBouncer on Kubernetes instead of on a dedicated virtual machine (VM). For our team, the decision came down to two key advantages:

  • A Unified Tech Stack: Our team is already deeply familiar with the Kubernetes ecosystem. Deploying PgBouncer here allows us to leverage our existing CI/CD pipelines, monitoring dashboards, and logging infrastructure. This creates a streamlined operational experience and reduces the learning curve.
  • Automatic Self-Healing: Kubernetes provides powerful high-availability features out of the box. If a PgBouncer pod crashes or becomes unresponsive, Kubernetes’ liveness probes automatically detect the failure and restart the container, ensuring service continuity with minimal manual intervention.

메타데이터
post_id
4f57c5cbfcb2
slug
the-sigterm-vs-sigint-trade-off-a-deep-dive-into-pgbouncer-rolling-restarts-4f57c5cbfcb2
url
https://medium.com/@CoyoteLeo/the-sigterm-vs-sigint-trade-off-a-deep-dive-into-pgbouncer-rolling-restarts-4f57c5cbfcb2
canonical_url
https://medium.com/@CoyoteLeo/the-sigterm-vs-sigint-trade-off-a-deep-dive-into-pgbouncer-rolling-restarts-4f57c5cbfcb2
author_url
https://medium.com/@CoyoteLeo
status
ok
fetched_at
2026-07-18 03:02:36