Skip to content

Fix Knex Connection Pool – Pool Exhaustion or Timeout

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about Fix Knex Connection Pool. We cover key concepts, practical examples, and best practices.

Your application runs fine for a while, then queries start failing with "Timeout: acquiring a connection" or "Connection pool error: Pool is full". Restarting the app fixes it temporarily, but the issue returns under load.

Wrong ❌

// knexfile.js
module.exports = {
  client: 'postgresql',
  connection: {
    host: 'localhost',
    database: 'mydb',
    user: 'postgres',
    password: 'password',
  },
  // ❌ No pool configuration β€” uses defaults (min: 2, max: 10)
};

Under 50 concurrent requests:

Error: Knex: Timeout acquiring a connection. The pool is full.

Knex's default pool (`min: 2, max: 10`) is exhausted. Connections are acquired but never released back to the pool.

## Right βœ…

```<a href="/programming-languages/javascript/">javascript</a>
// knexfile.js
module.exports = {
  client: '<a href="/databases/postgresql/">postgresql</a>',
  connection: {
    host: 'localhost',
    database: 'mydb',
    user: 'postgres',
    password: 'password',
    application_name: 'myapp',             // identify in pg_stat_activity
  },
  pool: {
    min: 2,
    max: 20,                               // increased for concurrency
    idleTimeoutMillis: 30000,              // 30s idle β†’ release
    acquireTimeoutMillis: 10000,           // wait 10s for a connection
    createTimeoutMillis: 5000,             // wait 5s to create a connection
    createRetryIntervalMillis: 200,        // retry every 200ms
    propagateCreateError: false,           // don't crash on first error
  },
  acquireConnectionTimeout: 15000,         // overall timeout
};

**Debug connection leaks:**

```javascript
const knex = require('knex')(config);

// Before each query, log the pool state
console.log({
  used: knex.client.pool.numUsed(),       // connections in use
  free: knex.client.pool.numFree(),       // idle connections
  pending: knex.client.pool.numPending(), // waiting to be created
});

// Or use the pool's event emitter (tarn.js)
knex.client.pool.on('pool:acquire', (resource) => {
  console.log('Acquired connection:', resource.__knexUid);
});

knex.client.pool.on('pool:release', (resource) => {
  console.log('Released connection:', resource.__knexUid);
});

**Always release connections:**

```<a href="/programming-languages/javascript/">javascript</a>
// ❌ Wrong β€” never released
app.get('/users', async (req, res) => {
  const users = await knex('users').select('*');
  res.json(users);
  // The connection is released automatically when the query completes
  // BUT if you use knex.client.acquireConnection(), you must release it:
});

// βœ… Correct β€” raw connection management
app.get('/users', async (req, res) => {
  const conn = await knex.client.acquireConnection();
  try {
    const result = await conn.query('SELECT * FROM users');
    res.json(result.rows);
  } finally {
    knex.client.releaseConnection(conn);    // must release
  }
});

**Monitor connections in <a href="/databases/postgresql/">PostgreSQL</a>:**

```sql
SELECT state, count(*)
FROM pg_stat_activity
WHERE application_name = 'myapp'
GROUP BY state;

**Set up a pool eviction check:**

```<a href="/programming-languages/javascript/">javascript</a>
pool: {
  min: 2,
  max: 20,
  idleTimeoutMillis: 30000,
  reapIntervalMillis: 1000,               // check for idle connections every 1s
  log: (msg, level) => console.log(`[pool] ${level}: ${msg}`),
},

## Root Cause

Knex creates a connection pool using `tarn.js`. Under high concurrency, if connections aren't released (or the pool is too small), all connections are busy, and new queries wait for `acquireTimeoutMillis` before timing out.

## Prevention

- Match pool size to application concurrency and database limits.
- Always use `knex()` directly (not raw `acquireConnection`) β€” Knex auto‑releases.
- Monitor `pg_stat_activity` for connection leaks.
- Set `application_name` to identify your app's connections.


## Common Mistakes with connection pool

1. **Mixing let bindings with <- bindings in do notation, producing type errors**
2. **Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors**
3. **Non-exhaustive pattern matches that compile with warnings then crash at runtime**

These mistakes appear frequently in real-world KNEX code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

## Practice Exercise

**Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.**

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

## FAQ

<details style="margin-bottom:12px;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden"><summary style="cursor:pointer;padding:14px 18px;font-weight:600;font-size:1.05rem;background:#f8fafc;border-bottom:1px solid #e2e8f0;color:#1e293b">**Q: What's the ideal pool size?**</summary><div style="padding:14px 18px;color:#475569;line-height:1.7;background:#fff"><p>A: Start with <code>max: 20</code> and adjust based on <code>max_connections</code> on the database and concurrent request volume.</p>
</div></details><details style="margin-bottom:12px;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden"><summary style="cursor:pointer;padding:14px 18px;font-weight:600;font-size:1.05rem;background:#f8fafc;border-bottom:1px solid #e2e8f0;color:#1e293b">**Q: Can I use a different pool implementation?**</summary><div style="padding:14px 18px;color:#475569;line-height:1.7;background:#fff"><p>A: Knex uses <code>tarn.js</code> (built‑in). No alternative pool providers.</p>
</div></details><details style="margin-bottom:12px;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden"><summary style="cursor:pointer;padding:14px 18px;font-weight:600;font-size:1.05rem;background:#f8fafc;border-bottom:1px solid #e2e8f0;color:#1e293b">**Q: How do I handle a database restart without crashing the app?**</summary><div style="padding:14px 18px;color:#475569;line-height:1.7;background:#fff"><p>A: Set <code>pool.createRetryIntervalMillis</code> and <code>pool.propagateCreateError: false</code>.</p>
</div></details><details style="margin-bottom:12px;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden"><summary style="cursor:pointer;padding:14px 18px;font-weight:600;font-size:1.05rem;background:#f8fafc;border-bottom:1px solid #e2e8f0;color:#1e293b">**Q: Why does my pool have idle connections above `min`?**</summary><div style="padding:14px 18px;color:#475569;line-height:1.7;background:#fff"><p>A: Knex keeps connections alive for <code>idleTimeoutMillis</code> before destroying them.</p>
</div></details>

---

*Connection pooling is covered in the [DodaTech Knex.js Performance course](https://dodatech.com/courses/knex).*

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro