Blog / Engineering

Waking Up Databases Without Dead Connections

A published app waking from sleep held its first request 10–12 seconds on database connections that died while it slept. How we got it to 2.3 seconds.

Mythex Team · 2026-09-29 · 4 min read

Published apps on Mythex, and their databases, go to sleep when nobody is using them. When an app woke up, it could hold its first request for 10–12 seconds, because it tried to reuse database connections that had died while everything slept. We now give Spring Boot apps database settings that are safe across sleep, and tell the agent to build apps of any stack the same way. On the hosting platform, the first request after a wake went from 12.7 seconds to 2.3 seconds.

Why apps and databases sleep

A published Mythex app runs on a machine that suspends after a few idle minutes, and its database is serverless Postgres that sleeps after five idle minutes and wakes on the next request. That's what keeps a quiet app cheap: you're charged for what the database actually used, and a sleeping app spends nothing on compute. We wrote about the billing side in billing databases by what they actually use.

The catch is that the two sleep separately, and they don't agree about what a connection is.

The symptom: a slow first visit

Most server apps keep a connection pool: a handful of open database connections kept ready, so each request doesn't pay the cost of opening a new one. That's the right design for an app that's always on.

Here's what happens with sleep:

  1. The app is running, with a few idle connections in its pool.
  2. Nobody visits. The app's machine suspends — frozen, with its pool intact.
  3. The database goes to sleep and drops its end of every connection.
  4. A visitor arrives. The app wakes up exactly as it was, still holding connections the database no longer knows about.
  5. The pool hands out one of those connections. It has to find out it's dead — by trying it and waiting — before giving up on it.

In a Spring Boot app, validating those dead connections blocked the first request for around 10 seconds before it failed with "Connection is not available". To the visitor, the site simply hung, and then maybe showed an error.

Why Spring apps got it worst

Spring Boot is a popular Java framework, and it uses a connection pool called HikariCP by default. Spring can configure that pool from settings, including how many idle connections to keep.

But Mythex gives every app its database as a single DATABASE_URL in the form postgresql://user:pass@host/db. Spring can't read that format directly. So Spring apps built on Mythex were constructing their own pool in code by parsing that URL — and a pool built by hand ignores all of Spring's spring.datasource.* settings. Even if someone knew the right pool settings, there was nowhere they'd take effect.

The fix, part 1: settings Spring can use

When a project has a database, we now also give it environment variables Spring reads natively:

  • SPRING_DATASOURCE_URL, SPRING_DATASOURCE_USERNAME and SPRING_DATASOURCE_PASSWORD, converted from DATABASE_URL (with SSL required unless the URL says otherwise).
  • Pool settings that survive sleep:
SettingValueWhy
Minimum idle connections0Keep nothing open between requests, so there's nothing to go stale
Idle timeout30 secondsClose unused connections soon
Maximum lifetime2 minutesRetire every connection before it can get old
Connection timeout5 secondsDon't hang a request for long if the database is slow to answer

With no idle connections held, a woken app opens one fresh connection instead of wading through dead ones.

With these variables present, Spring configures the pool itself, and the settings apply. Anything the project has set itself always wins; we only fill in what's missing. Apps that aren't Spring ignore these variables.

The detail that almost made it do nothing

Spring maps environment variables onto its settings by a rule that's easy to get wrong. The setting minimum-idle has to be written as SPRING_DATASOURCE_HIKARI_MINIMUMIDLE — dashes dropped, words run together. Writing it as SPRING_DATASOURCE_HIKARI_MINIMUM_IDLE, which looks more natural, maps to a different, non-existent setting (minimum.idle) and is silently ignored.

That's the kind of mistake that passes every review and does nothing in production. So a test checks both that the values are set and that no variable uses the underscore-separated form.

The fix, part 2: every other stack

Spring isn't the only framework with a pool, and we can't pre-configure every library in every language. So the Mythex agent — the AI that writes your app — now gets a standing instruction for any stack: published apps and their databases sleep when idle, so a connection kept open can be dead on the next request. It should:

  • keep no idle database connections open, or check each one before use,
  • retry once on a connection error, and
  • use the database settings the platform provides in the environment, rather than building its own pool by parsing DATABASE_URL.

That last point closes the loop with part 1: an app built on the provided settings picks up the sleep-safe configuration automatically.

The result

Measured on the hosting platform we run published apps on, the first request after a wake went from 12.7 seconds to 2.3 seconds.

What we took from it

  • Every layer that sleeps needs to agree on what survives. A frozen app and a sleeping database each made a reasonable choice. Together, the app woke up holding state that was no longer true.
  • Defaults designed for always-on servers don't fit apps that scale to zero. Keeping connections warm is a good idea until everything around them goes cold.
  • Give frameworks their data in their own format. One URL was simple for us and forced every Spring app to hand-roll a pool that ignored its own configuration.
  • Test that a setting is spelled the way the framework reads it. A correct value under the wrong name does nothing, silently.

If you're new to how databases work on Mythex, see the database docs and our explainer on what Postgres is.

Keep reading

  • A Failed Republish Never Deletes a Working Backend — A failed republish could delete an app's live API, or leave its broken new version running. Three fixes to how Mythex undoes a publish that goes wrong.
  • Alerts That Fire Once, Not Once per Server — A once-a-day alert reached us five times in one afternoon. Why in-memory dedupe breaks with two servers and a deploy, and how shared state fixed it.
  • Moving to Bigger Workspaces Without Making Anyone Wait — A quarter of our workspaces were stuck at 1 GB after we moved to 2 GB. Our first fix made one person wait seven minutes. What we changed, and changed again.
  • Billing Databases by What They Actually Use — We used to estimate each app's database cost, and the estimate was wrong both ways. Now every database is billed from measured usage, hour by hour.
  • Catching Apps That Crash Right After Publishing — Three publishes passed our health check while their API crashed seconds into every boot. The cause: counting crashes in a list capped at five entries.
  • Keeping Image-Heavy Agent Turns Within Memory — An agent turn that kept looking at catalogue scans ran out of memory at 952 MB. Three separate causes, each holding images too long, and how we fixed them.

Start building free · Templates · Docs