databases/Artigo

    PostgreSQL em produção com Docker

    Rodar PostgreSQL em produção com Docker exige mais que um docker run com a imagem oficial. Você precisa garantir persistência de dados, healthcheck para dependências de startup, ajuste de memória para a VPS, logs estruturados e nunca expor o banco diretamente na internet. Este artigo cobre a configuração completa.

    docker-compose.yml para produção

    A configuração mínima recomendada para PostgreSQL em produção inclui volume nomeado, healthcheck, limites de recurso e reinício automático:

    yaml
    services:
      postgres:
        image: postgres:16-alpine
        restart: always
        environment:
          POSTGRES_DB: ${POSTGRES_DB}
          POSTGRES_USER: ${POSTGRES_USER}
          POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
          PGDATA: /var/lib/postgresql/data/pgdata
        volumes:
          - pg_data:/var/lib/postgresql/data
          - ./postgresql.conf:/etc/postgresql/postgresql.conf:ro
        command: postgres -c config_file=/etc/postgresql/postgresql.conf
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
          interval: 10s
          timeout: 5s
          retries: 5
          start_period: 30s
        deploy:
          resources:
            limits:
              memory: 1G
            reservations:
              memory: 256M
        networks:
          - db_network
    
    volumes:
      pg_data:
    
    networks:
      db_network:
        driver: bridge
    Dica
    PGDATA: /var/lib/postgresql/data/pgdata adiciona um subdiretório dentro do volume — evita conflito com arquivos de inicialização que o PostgreSQL coloca na raiz do diretório de dados.

    Tuning de memória (postgresql.conf)

    O PostgreSQL usa os padrões conservadores da imagem oficial, adequados para ambientes de desenvolvimento. Para produção, ajuste conforme a RAM da VPS:

    ini
    # postgresql.conf para VPS de 2 GB RAM dedicados ao banco
    # Regras gerais:
    #   shared_buffers = 25% da RAM
    #   effective_cache_size = 75% da RAM
    #   work_mem = RAM / (max_connections * 2)
    
    # Para VPS 2 GB:
    shared_buffers = 512MB
    effective_cache_size = 1536MB
    maintenance_work_mem = 128MB
    work_mem = 10MB
    max_connections = 100
    wal_buffers = 16MB
    
    # Checkpoints (reduz I/O de pico)
    checkpoint_completion_target = 0.9
    checkpoint_timeout = 10min
    
    # Paralelismo (para VPS com 2+ vCPUs)
    max_parallel_workers_per_gather = 2
    max_parallel_workers = 4
    
    # Logs
    log_min_duration_statement = 500
    log_checkpoints = on
    log_connections = off
    log_lock_waits = on
    Dica
    log_min_duration_statement = 500 registra queries que demoram mais de 500 ms — essencial para identificar gargalos sem poluir os logs com queries rápidas.

    Segurança: nunca expor o banco na internet

    O PostgreSQL deve ser acessível apenas dentro da rede Docker (pelo nome do serviço) ou via SSH tunnel. Nunca mapeie a porta 5432 em produção:

    yaml
    # CORRETO: sem mapeamento de porta (banco inacessível externamente)
    services:
      postgres:
        image: postgres:16-alpine
        # SEM "ports:" — acesso apenas pela rede Docker interna
        networks:
          - db_network
    
      app:
        environment:
          DATABASE_URL: postgres://user:pass@postgres:5432/db
        networks:
          - db_network
    
    # ERRADO para produção (expõe o banco na internet):
    # ports:
    #   - "5432:5432"
    
    # Para acesso externo seguro, use SSH tunnel:
    ssh -L 5432:localhost:5432 root@IP_DA_VPS
    # Depois conecte em localhost:5432 no seu DBeaver/pgAdmin local
    Atenção
    Bots varrem a internet continuamente em busca de portas PostgreSQL abertas. Um banco exposto com senha fraca é comprometido em minutos. Use SSH tunnel ou VPN para acesso remoto.

    Verificar integridade e manutenção

    Manutenção regular previne degradação de performance e corrupção silenciosa:

    bash
    # VACUUM ANALYZE — limpar tuplas mortas e atualizar estatísticas
    docker compose exec postgres psql -U postgres -d meu_banco \
      -c "VACUUM ANALYZE;"
    
    # VACUUM FULL — reescreve tabelas (requer lock, use em manutenção)
    docker compose exec postgres psql -U postgres -d meu_banco \
      -c "VACUUM FULL ANALYZE;"
    
    # Ver tabelas com mais tuplas mortas (candidatas a VACUUM)
    docker compose exec postgres psql -U postgres -d meu_banco -c "
    SELECT schemaname, tablename, n_dead_tup, n_live_tup,
           round(n_dead_tup::numeric/NULLIF(n_live_tup,0)*100, 2) AS dead_pct
    FROM pg_stat_user_tables
    ORDER BY n_dead_tup DESC
    LIMIT 10;"
    
    # Verificar tamanho dos bancos
    docker compose exec postgres psql -U postgres \
      -c "SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) FROM pg_database ORDER BY pg_database_size(pg_database.datname) DESC;"
    Dica
    O autovacuum do PostgreSQL já executa VACUUM automaticamente, mas em bancos com alta taxa de writes pode ser necessário ajustar autovacuum_vacuum_scale_factor e autovacuum_analyze_scale_factor.

    Atualizar o PostgreSQL com zero downtime

    Para atualizar a versão minor (16.1 → 16.5), basta puxar a nova imagem. Para major (15 → 16), é necessário pg_upgrade:

    bash
    # Atualização minor (patch) — sem perda de dados
    docker compose pull postgres
    docker compose up -d postgres
    
    # Verificar nova versão
    docker compose exec postgres psql -U postgres -c "SELECT version();"
    
    # Atualização major (ex: PG 15 → 16):
    # 1. Fazer backup completo com pg_dumpall
    docker compose exec postgres pg_dumpall -U postgres > backup-before-upgrade.sql
    
    # 2. Parar o banco e remover o volume antigo
    docker compose down
    docker volume rm nome_do_projeto_pg_data
    
    # 3. Mudar a tag da imagem no docker-compose.yml para postgres:16-alpine
    # 4. Subir e restaurar
    docker compose up -d postgres
    cat backup-before-upgrade.sql | docker compose exec -T postgres psql -U postgres

    $ runstack deploy --plan starter

    Não quer configurar manualmente?

    Não quer configurar manualmente? Implante o PostgreSQL em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.

    Perguntas frequentes