# How Render handles zero-downtime deploys

- Date: 2026-04-01T10:52:15.873Z
- Tags: Platform
- URL: https://render.com/articles/how-render-handles-zero-downtime-deploys


## How zero-downtime deploys work

When you deploy a Render [web service](https://render.com/docs/web-services), Render keeps your application available while replacing the old version with the new one. In practice, avoiding dropped requests depends on both Render's deployment flow and your application's startup, health check, and shutdown behavior.

Render builds and starts a new instance of your application while the current instance keeps serving traffic. Once the new instance is ready, Render routes new requests to it and begins shutting down the older instance. Your application still needs to cooperate by reporting readiness accurately and by draining in-flight work cleanly when it receives termination signals.

Zero-downtime deploys apply to web services, private services, background workers, and cron jobs. Static sites also update with zero downtime but are backed by a CDN and don't involve service instances. Services with an attached [persistent disk](https://render.com/docs/disks) are the exception: adding a persistent disk disables zero-downtime deploys.

## The deploy sequence

Each deploy follows these steps in order:

1. Build the new artifact.
2. Run [pre-deploy commands](https://render.com/docs/pre-deploy-commands) (if configured) in a separate, temporary environment.
3. Boot the new instance alongside the active one.
4. Gate on health checks (or port binding) before routing traffic.
5. Switch traffic to the new instance.
6. After 60 seconds, send `SIGTERM` to the old instance.
7. If the old instance doesn't exit within the shutdown delay, send `SIGKILL`.

Render does not send traffic to the new instance until it is considered ready. If you configure a health check path, Render uses that endpoint to verify readiness. Otherwise, Render falls back to detecting that the service is listening on its assigned port, which is why an explicit health check is usually the safer choice.

For long-lived connections like WebSockets or Server-Sent Events (SSE), zero-downtime routing applies only to the initial HTTP handshake. When the old instance eventually terminates, active WebSocket connections break. Your clients should implement reconnection logic to re-establish these connections with the new instance.

```mermaid
sequenceDiagram
    participant User as HTTP Client
    participant LB as Render Load Balancer
    participant Old as Old Instance (v1)
    participant New as New Instance (v2)

    Note over Old, LB: V1 is Live
    User->>LB: GET /api/data
    LB->>Old: Routes request
    Old-->>User: 200 OK

    Note over New: V2 Deploy Initiated
    New->>New: Boot Application
    
    loop Health Check Gating
        LB->>New: GET /health
        New-->>LB: 503 (Booting/Connecting DB)
    end
    
    LB->>New: GET /health
    New-->>LB: 200 OK (Ready)
    
    Note over LB, New: Traffic Switched to V2
    User->>LB: GET /api/data
    LB->>New: Routes new request
    
    Note over Old: Graceful Shutdown (60s after traffic switch)
    Old->>Old: Receives SIGTERM
    Old->>Old: Finish in-flight requests
    Old->>Old: Close DB connections
    Old->>Old: Process Exits
```

## Configure health checks for readiness

Render supports HTTP [health checks](https://render.com/docs/health-checks) to verify that a new instance is ready to receive traffic. You can configure a health check path in the Render Dashboard or via `healthCheckPath` in your `render.yaml` file. If you do not configure one, Render falls back to checking whether the service has bound to its assigned port.

You must understand the difference between application liveness and application readiness. *Liveness* means your server process runs and binds to a port. *Readiness* means your server has established its required dependencies, such as database connections, cache clients, or configuration needed to serve real requests. A health check path gives Render a stronger readiness signal than port binding alone.

This minimal example demonstrates how an Express application might verify database readiness before passing a health check, returning standard HTTP status codes based on internal state variables:

```javascript pseudocode
const express = require('express');
const app = express();
// Simplification: In reality, use connection pooling state
let poolReady = false;

app.get('/health', (req, res) => {
  // Production: Add timeout handling for hanging DB checks
  if (poolReady) {
    res.status(200).send('Ready');
  } else {
    res.status(503).send('Booting');
  }
});
```

For production, add robust timeout limits, comprehensive dependency checks, and proper logging. *Please note that this code requires adaptation for your specific context; it intentionally omits authentication, detailed error parsing, comprehensive logging, and production routing structures.*

## Handle shutdown gracefully

Once the health check passes and Render switches traffic to the new instance, the old instance keeps running for 60 seconds before Render sends it a `SIGTERM` signal.

After receiving `SIGTERM`, your application has a configurable shutdown delay (default 30 seconds) to clean up. If the process is still running after the shutdown delay, Render sends `SIGKILL` to terminate it immediately. You can extend the shutdown delay up to 300 seconds via `maxShutdownDelaySeconds` in your `render.yaml` or through the Render API.

To prevent dropped connections, your application should catch `SIGTERM` and follow this sequence:

1. Immediately stop accepting new connections (for example, calling `server.close()` in Node.js).
2. Allow currently executing HTTP requests to finish writing their responses.
3. Explicitly sever keep-alive sockets.
4. Drain database connection pools before invoking `process.exit()`. 

If you implement a forced shutdown timer slightly shorter than your configured shutdown delay, your application can log incomplete drains before Render sends `SIGKILL`.

This demonstrates the core pattern for catching a `SIGTERM` and allowing in-flight requests to finish:

```javascript pseudocode
const server = app.listen(8080);
// Simplification: Assumes single server instance

process.on('SIGTERM', () => {
  console.log('SIGTERM caught. Halting new traffic.');
  
  server.close(() => {
    // Production: Ensure DB connections are also cleanly closed here
    console.log('Active requests drained.');
    process.exit(0);
  });
  
  setTimeout(() => process.exit(1), 25000);
});
```

For production, add error handling during the shutdown sequence and ensure you cleanly pause background jobs. *Please note that this code requires adaptation for your specific context; it intentionally omits advanced connection draining logic, Redis/Cache teardown, and cluster mode handling.*

## Common mistakes to avoid

A frequent mistake is hardcoding your health check endpoint to always return `200 OK` immediately on boot. If your application needs a few seconds to establish a database connection pool, returning `200` prematurely tells Render to route traffic to an instance that isn't ready. Those requests fail, causing perceived downtime.

Ignoring `SIGTERM` also causes dropped connections. Without a signal handler, your application keeps running until Render’s shutdown delay expires and Render sends `SIGKILL`, which terminates all in-flight requests and open sockets immediately.

Finally, don't confuse pre-deploy commands with your application's startup sequence. Pre-deploy commands run in a separate temporary environment, which makes them useful for tasks like database migrations. They cannot keep a web server running or preserve in-memory state for the instance that eventually serves traffic.

## Next steps

To learn more about deploying on Render, explore these resources:

- [Zero-downtime deploys](https://render.com/docs/deploys#zero-downtime-deploys): the full deploy sequence, including how Render handles overlapping deploys and rollbacks.
- [Health checks](https://render.com/docs/health-checks): configure a health check endpoint to verify readiness before Render routes traffic.
- [Pre-deploy commands](https://render.com/docs/pre-deploy-commands): run database migrations or other setup tasks before each deploy.
- [Blueprint specification](https://render.com/docs/blueprint-spec): define `healthCheckPath` and `maxShutdownDelaySeconds` in your `render.yaml`.

## Frequently asked questions

###### Does Render guarantee zero downtime for every deploy?

Render is designed to keep web services available during deploys, but the outcome still depends on your application. Accurate health checks, fast startup, and graceful `SIGTERM` handling all affect whether requests complete cleanly during an update.

###### What happens if I do not configure a health check path?

If you do not configure a health check path, Render falls back to checking whether your service has bound to its assigned port. That is enough to detect that the process is listening, but it is not always enough to prove your app is fully ready to serve real traffic.

###### Why should my health check return 503 during startup?

Returning `503` while dependencies are still initializing prevents Render from sending production traffic to an instance that is not ready yet. Once the app is actually ready, the same endpoint should return a successful status such as `200`.

###### What signal does Render send during shutdown?

Render waits 60 seconds after switching traffic, then sends `SIGTERM` so your application can stop accepting new work and finish in-flight requests. If the process does not exit within the configured shutdown delay (default 30 seconds, configurable up to 300 seconds), Render sends `SIGKILL` to terminate it forcefully.

###### Can zero-downtime deploys preserve WebSocket connections?

Not completely. Zero-downtime behavior mainly protects the HTTP request path during deployment. Existing WebSocket or SSE-style long-lived connections can still be interrupted when the old instance shuts down, so clients should implement reconnection logic.

###### What are pre-deploy commands best used for?

Use pre-deploy commands for setup tasks that must finish before the new version goes live, such as database migrations or asset preparation. They are not a substitute for your application's startup sequence and should not be used to hold runtime state for the serving instance.

