---
title: "pgxpool em Go: PostgreSQL em Produção"
url: "https://golang.com.br/blog/pgxpool-go-postgresql-producao/"
markdown_url: "https://golang.com.br/blog/pgxpool-go-postgresql-producao.MD"
description: "Aprenda a configurar pgxpool em Go para PostgreSQL: tamanho do pool, timeouts, health check, queries com contexto, métricas e cuidados de produção."
date: "2026-06-01"
author: "Golang Brasil"
---

# pgxpool em Go: PostgreSQL em Produção

Aprenda a configurar pgxpool em Go para PostgreSQL: tamanho do pool, timeouts, health check, queries com contexto, métricas e cuidados de produção.


`pgxpool` é a forma mais comum de usar PostgreSQL com Go quando a aplicação precisa passar do exemplo local para produção. O driver `pgx` já conversa muito bem com PostgreSQL, mas abrir uma conexão nova a cada requisição é caro, lento e frágil. Um pool mantém conexões prontas, limita concorrência contra o banco e dá um ponto único para configurar timeouts, health checks e observabilidade.

Esse detalhe costuma ser subestimado. Uma API pequena funciona no notebook com `pgx.Connect`, mas em produção recebe tráfego concorrente, deploys paralelos, jobs em background, timeouts de rede, conexões ociosas derrubadas por proxy e queries lentas que prendem recursos. Sem pool bem configurado, o sintoma aparece como latência irregular, `too many connections`, requisições penduradas ou banco saturado sem motivo claro.

Este guia mostra como configurar `pgxpool` em Go com limites explícitos, `context.Context`, health check, encerramento limpo e métricas básicas. Ele complementa [Go com PostgreSQL](/aprenda/golang-postgresql/), [sqlc em Go](/blog/sqlc-go-postgresql-typesafe/), [migrations em Go](/blog/migrations-go-banco-dados-producao/) e [context, timeout e cancelamento em Go](/blog/context-timeout-cancelamento-go/).

## Quando usar pgxpool

Use `pgxpool` quando sua aplicação mantém um processo vivo que faz várias queries ao longo do tempo: API HTTP, worker, consumidor de fila, scheduler, serviço gRPC ou backend B2B. O pool evita o custo de handshake por operação e controla quantas queries podem disputar conexões ao mesmo tempo.

Para scripts curtos, CLIs administrativas ou migrations pontuais, uma conexão direta pode ser suficiente. Mesmo assim, em aplicações longas, prefira criar um pool no bootstrap e passá-lo para a camada de repositório. O pool deve ser um recurso de aplicação, não algo criado dentro de cada handler.

## Instalação

Instale o módulo do `pgx`:

```bash
go get github.com/jackc/pgx/v5/pgxpool
```

Em produção, a string de conexão deve vir de variável de ambiente ou gerenciador de segredos. Nunca coloque usuário e senha fixos no código:

```bash
export DATABASE_URL="postgres://app:senha@localhost:5432/app?sslmode=disable"
```

## Configuração mínima com limites explícitos

O ponto mais importante é não depender só dos defaults. Comece com um `ParseConfig`, ajuste limites e valide o pool com `Ping` no bootstrap:

```go
package database

import (
    "context"
    "fmt"
    "time"

    "github.com/jackc/pgx/v5/pgxpool"
)

func Open(ctx context.Context, databaseURL string) (*pgxpool.Pool, error) {
    cfg, err := pgxpool.ParseConfig(databaseURL)
    if err != nil {
        return nil, fmt.Errorf("parse database url: %w", err)
    }

    cfg.MaxConns = 10
    cfg.MinConns = 2
    cfg.MaxConnLifetime = 30 * time.Minute
    cfg.MaxConnIdleTime = 5 * time.Minute
    cfg.HealthCheckPeriod = 1 * time.Minute

    pool, err := pgxpool.NewWithConfig(ctx, cfg)
    if err != nil {
        return nil, fmt.Errorf("create pgx pool: %w", err)
    }

    pingCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()

    if err := pool.Ping(pingCtx); err != nil {
        pool.Close()
        return nil, fmt.Errorf("ping postgres: %w", err)
    }

    return pool, nil
}
```

`MaxConns` deve ser escolhido com calma. Se você roda 6 réplicas da API e cada uma abre até 20 conexões, o banco pode receber 120 conexões só desse serviço. Some workers, ferramentas administrativas, replicas de leitura e conexões do próprio banco antes de aumentar esse número.

Uma regra prática inicial é começar pequeno: 5 a 20 conexões por processo para APIs comuns, medir espera por conexão e ajustar. Pool grande demais não torna queries lentas mais rápidas; só permite que mais queries lentas ataquem o banco ao mesmo tempo.

## Usando contexto em todas as queries

Toda query deve receber contexto com timeout ou deadline herdado da requisição. Isso evita que uma chamada HTTP cancelada continue segurando conexão no banco:

```go
type UserRepository struct {
    db *pgxpool.Pool
}

func (r UserRepository) FindByEmail(ctx context.Context, email string) (User, error) {
    ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
    defer cancel()

    var user User
    err := r.db.QueryRow(ctx, `
        SELECT id, email, name, created_at
          FROM users
         WHERE email = $1
    `, email).Scan(&user.ID, &user.Email, &user.Name, &user.CreatedAt)
    if err != nil {
        return User{}, fmt.Errorf("find user by email: %w", err)
    }

    return user, nil
}
```

Evite `context.Background()` dentro de repositórios. O handler, worker ou job que chamou a função deve controlar cancelamento. O repositório pode aplicar um timeout máximo local, mas não deve apagar o contexto de origem.

## Encerramento limpo no deploy

Quando a aplicação recebe `SIGTERM`, pare de aceitar novas requisições, deixe as requisições em andamento terminarem e feche o pool. Em servidores HTTP, combine `http.Server.Shutdown` com `pool.Close()`:

```go
func main() {
    ctx := context.Background()
    pool, err := database.Open(ctx, os.Getenv("DATABASE_URL"))
    if err != nil {
        log.Fatal(err)
    }
    defer pool.Close()

    // inicialize handlers, server e signal handling aqui
}
```

`pool.Close()` bloqueia até liberar recursos do pool. Por isso, ele deve acontecer no shutdown da aplicação, não no fim de cada requisição. Fechar o pool em handler é um bug: a primeira requisição pode quebrar as próximas.

## Métricas que você deve observar

`pgxpool` expõe estatísticas úteis pelo método `Stat()`. Mesmo sem Prometheus no primeiro dia, logar esses valores durante incidentes ajuda muito:

```go
stats := pool.Stat()
log.Printf(
    "pgxpool total=%d acquired=%d idle=%d acquire_count=%d acquire_duration=%s",
    stats.TotalConns(),
    stats.AcquiredConns(),
    stats.IdleConns(),
    stats.AcquireCount(),
    stats.AcquireDuration(),
)
```

Os sinais mais importantes são:

- `AcquiredConns` perto de `MaxConns` por muito tempo: o serviço está usando todo o pool.
- Tempo de aquisição crescendo: goroutines estão esperando conexão livre.
- Muitas conexões ociosas: talvez o pool esteja maior do que precisa.
- Erros de timeout em queries simples: pode haver lock, índice ausente ou saturação no banco.

Essas métricas devem ser lidas junto com latência de endpoints, CPU do banco, locks, `pg_stat_activity` e logs de query lenta. Pool tuning sem olhar para o banco vira chute.

## Cuidados comuns em produção

O primeiro cuidado é não compartilhar a mesma conta e limite de conexão entre todos os serviços sem planejamento. APIs, workers e jobs têm perfis diferentes. Worker de fila pode fazer poucas queries longas; API pode fazer muitas queries curtas. Separar configuração por processo facilita tuning.

O segundo é usar timeouts diferentes para operações diferentes. Buscar um usuário por ID não deveria ter o mesmo orçamento de tempo de um relatório pesado. Contextos curtos protegem a API, mas timeouts agressivos demais podem derrubar operações legítimas.

O terceiro é não tratar pool como fila infinita. Se todos os handlers bloqueiam esperando conexão, sua aplicação continua aceitando trabalho que não consegue cumprir. Em endpoints críticos, considere timeout curto para aquisição e resposta clara quando o banco está saturado.

Por fim, lembre que `pgxpool` resolve gerenciamento de conexões, não modelagem de dados. Índices ruins, transações longas, N+1 queries e locks continuam derrubando performance. O pool é a fundação; boas queries e migrations seguras continuam obrigatórias.

## Checklist rápido

Antes de colocar Go com PostgreSQL em produção, confira:

- O pool é criado uma vez no bootstrap da aplicação.
- `MaxConns`, `MinConns`, lifetime e idle time são explícitos.
- O total de conexões considera todas as réplicas e workers.
- Todas as queries recebem `context.Context` com timeout.
- `Ping` roda no startup para falhar cedo quando o banco está inacessível.
- `pool.Close()` roda no shutdown, não por requisição.
- Métricas de pool e query lenta estão acessíveis durante incidentes.

Com isso, `pgxpool` deixa de ser só uma linha de setup e vira parte real da confiabilidade do backend. Go e PostgreSQL combinam muito bem, mas produção exige limites claros: conexões são recurso finito, e um pool bem configurado é o que impede sua API de descobrir isso tarde demais.
