> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackshift.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Sleep when idle

> A database nobody is using goes to sleep and wakes on the next connection. Its address, password and data stay the same.

> [!NOTE]
>
> **Not yet released.** This guide describes implemented changes awaiting rollout. Availability requires the corresponding backend and dashboard release.

## Goal

Let a quiet database sleep, and set up your app so the first connection after a sleep just works.

## Prerequisites

* A PostgreSQL database that can sleep (see What can’t sleep)
* An app whose database driver can retry a connection

## Workflow

**Step 1**

Turn it on in the database’s Settings, under Size, then Sleep when idle: pick a timer from 5 minutes to 24 hours and save. From the CLI, run `stackshift db sleep shop-db --after 15m`. Branches sleep after 5 minutes without any setup. Your main database stays awake until you turn this on.

**Step 2**

Set your driver’s pool as described in Driver settings: a health check or pre-ping, and an idle timeout shorter than your sleep timer.

**Step 3**

Check it. Once the timer has passed without traffic the database shows Sleeping. Connect again and the first connection takes a moment longer, then it works as usual.

**Step 4**

Before a batch job, wake the database yourself with `stackshift db wake shop-db --wait`.

## When a database counts as idle

Idle means no data went either way on any connection to the database, whether private, Shared, public or through the tunnel, for the whole timer. StackShift’s own health checks and dashboard views don’t count as use and never wake it. An app that polls the database every minute keeps it awake.

## How waking works

* The first connection after a sleep is held while the database starts. Your client sees a slower connect, not an error.
* As a target, a Starter database wakes in about a second and a Dedicated database in a few seconds. These are targets, not promises.
* Connections that arrive together are all held, up to 200 per database. Beyond that, and if the database hasn’t woken in time (30 seconds, or 60 on Dedicated), a connection gets the error below.
* The address, password and firewall rules stay in place while the database sleeps. Nothing changes for your app.

## Shared connections and direct connections

* Shared connections (pooled) on Starter survive sleep. Your app’s connections stay open, the first query waits for the database to wake and then runs, and nothing disconnects. If the wake fails, only that query gets the error and the connection stays open.
* Direct connections are closed when the database goes to sleep. On Dedicated databases, Shared connections close at sleep too.
* Branches use Shared connections by default. Keep migrations and admin tools on the direct address, and let them reconnect.

## The “database is waking up” error

If the database can’t wake in time, the connection is answered with PostgreSQL error `57P03`, “the database is waking up, try again in a few seconds”. Most drivers and ORMs treat this as retryable. Retry after a couple of seconds, a few times.

With `sslmode=verify-full` you still get this real error, not “server closed the connection”, because the answer is given with the database’s own certificate. Clients that skip TLS get the same error in plain text.

```ts Retry while it wakes theme={null}
async function withWakeRetry<T>(run: () => Promise<T>, attempts = 4): Promise<T> {
  for (let attempt = 1; ; attempt++) {
    try {
      return await run();
    } catch (error) {
      if ((error as { code?: string }).code !== "57P03" || attempt >= attempts) throw error;
      await new Promise((resolve) => setTimeout(resolve, attempt * 2000));
    }
  }
}
```

## Driver settings

Two settings matter for every driver. First, a health check or pre-ping, so a connection that was closed during sleep is replaced instead of failing your request. Second, an idle timeout below your sleep timer, so your pool lets go of idle connections before the database does. The shortest timer is 5 minutes, so 2 minutes is safe everywhere. Also allow a connect timeout of at least 30 seconds.

For node-postgres, set `idleTimeoutMillis` to 120000 and `connectionTimeoutMillis` to 30000, and add `pool.on("error", (error) => console.error(error))` so a connection closed in the background is logged instead of crashing your process. The other drivers are below.

```ts node-postgres theme={null}
import { Pool } from "pg";

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  idleTimeoutMillis: 120000,
  connectionTimeoutMillis: 30000,
});
pool.on("error", (error) => console.error(error));
```

```bash Prisma theme={null}
DATABASE_URL=postgresql://USER:PASSWORD@HOST:PORT/DB?connect_timeout=30&pool_timeout=30&max_idle_connection_lifetime=120
```

```python SQLAlchemy theme={null}
engine = create_engine(
    url,
    pool_pre_ping=True,
    pool_recycle=120,
    connect_args={"connect_timeout": 30},
)
```

```yaml Rails theme={null}
production:
  url: <%= ENV['DATABASE_URL'] %>
  idle_timeout: 120
  connect_timeout: 30
  checkout_timeout: 30
```

```go pgx theme={null}
config, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL"))
config.MaxConnIdleTime = 2 * time.Minute
config.HealthCheckPeriod = 30 * time.Second
config.ConnConfig.ConnectTimeout = 30 * time.Second
```

```php Laravel theme={null}
'pgsql' => [
    'url' => env('DATABASE_URL'),
    'options' => [PDO::ATTR_TIMEOUT => 30],
],

$rows = retry(4, fn () => DB::select('select 1'), 2000);
```

## What can’t sleep

* Databases with a standby or read copies, and databases that are themselves a standby or a copy. Turn the standby or copies off first.
* Databases in use for logical replication, as publisher or subscriber.
* Databases with active scheduled jobs (`pg_cron`). A sleeping database runs no scheduled jobs. You can allow sleep anyway by accepting that, with `allow_scheduled_jobs`.
* Redis, Valkey, MySQL, MariaDB and ClickHouse databases.
* If sleep can’t be turned on, the answer says why (`409 sleep_unavailable`).

## Cost and capacity

* Starter: sleep is included in your plan. Branches count toward how many can be awake at once on your plan.
* Dedicated billed hourly: a sleeping database pays for storage only. With monthly or yearly billing sleep is allowed but saves nothing, and the dashboard says so.
* A production database keeps its place while it sleeps, so waking it can’t fail for lack of room. A branch gives its place up and asks for it again when it wakes.
* Backups skip a sleeping database because nothing changes. One that slept longer than its backup interval is backed up when it next wakes.
* Once a day, each sleeping production database is woken at its quietest hour for a quick check, then put back to sleep, so a slow wake is caught before you meet it. The check shows in the database’s events.

## Turn sleep on or off, and wake it

* The database’s `sleep` shows `enabled`, `after_minutes`, `allow_scheduled_jobs` and a `state` of `awake`, `sleeping`, `asleep` or `waking`. The dashboard shows Available or Sleeping.
* Name a branch as `shop-db/fix-checkout` to change its sleep timer or wake it: `stackshift db sleep shop-db/fix-checkout --after 30m`.
* `POST .../wake?wait=true` answers `200` once the database is awake. Without `wait` it answers `202` straight away.
* In Terraform, add `sleep_after_minutes` to your existing `stackshift_database` resource, or to `stackshift_database_instance` for a Dedicated database.

```bash CLI theme={null}
stackshift db sleep shop-db --after 15m
stackshift db sleep shop-db --off
stackshift db wake shop-db --wait
```

```bash API theme={null}
curl -X PUT https://api.stackshift.cloud/api/v1/databases/$DATABASE_ID/sleep \
  -H "Authorization: Bearer $STACKSHIFT_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"enabled": true, "after_minutes": 15, "allow_scheduled_jobs": false}'

curl -X POST "https://api.stackshift.cloud/api/v1/databases/$DATABASE_ID/wake?wait=true" \
  -H "Authorization: Bearer $STACKSHIFT_API_KEY"
```

```hcl Terraform theme={null}
resource "stackshift_database" "shop" {
  name                = "shop-db"
  sleep_after_minutes = 15
}
```

## Availability

Sleep when idle is rolling out. If your database’s Settings page doesn’t show it yet, it isn’t switched on for your account.

## Expected result

> [!TIP]
>
> The database shows Sleeping when it’s idle, and the next connection from your app succeeds after a short wait with no change to its address or password.

## Common failures

> [!WARNING]
>
> * The first query after a quiet spell fails with “server closed the connection”: your pool kept a connection the database closed. Add a health check or pre-ping and an idle timeout below your sleep timer.
> * `57P03`: the database is still waking. Retry after a few seconds.
> * `502 wake_failed`: the database didn’t wake in time, for example because its storage is full. Open the database page for the reason, fix it, then run `stackshift db wake shop-db --wait`.
> * `402 plan_limit` on wake: too many branches are already awake on your plan. Delete or let one sleep first.
> * `409 sleep_unavailable`: the message says what is in the way, such as a standby, read copies or active scheduled jobs.
> * A database never sleeps: something is talking to it. Check polling jobs, uptime checks that run queries, and whether the timer is longer than you expect.

## Related guides

> [card] [Branches](https://docs.stackshift.cloud/databases/branches.md)
>
> A copy of your database you can change freely. It has its own address and password, and sleeps when nobody’s using it.

> [card] [Branches for previews and agents](https://docs.stackshift.cloud/databases/branches-for-previews-and-agents.md)
>
> Give every pull request and every agent task a database it can’t hurt production with, and try migrations there first.

> [card] [Database connections and tunneling](https://docs.stackshift.cloud/databases/credentials-pooling-and-usage.md)
>
> Use private, direct public, or StackShift tunnel endpoints with TLS and explicit client IP policy.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.