# Configuración avanzada e inicialización


Con el almacenamiento persistente configurado en la sección anterior, estás listo para personalizar PostgreSQL para su uso en el mundo real. Esta guía cubre técnicas de configuración avanzada para ejecutar PostgreSQL en contenedores de Docker, incluyendo la inicialización automatizada de bases de datos, el ajuste de rendimiento y la configuración de la zona horaria.

## Descripción general

Aunque los contenedores de PostgreSQL se pueden iniciar rápidamente con la configuración predeterminada, los entornos de producción requieren configuraciones personalizadas. Esta guía explica cómo:

- Automatizar la creación de bases de datos, esquemas y usuarios durante el inicio del contenedor
- Ajustar los parámetros de rendimiento de PostgreSQL para cargas de trabajo contenedorizadas
- Configurar la zona horaria y el idioma (locale)

## Scripts de inicialización

La imagen oficial de Docker para PostgreSQL admite la ejecución automática de scripts de inicialización cuando el contenedor se inicia por primera vez. Cualquier archivo colocado en el directorio `/docker-entrypoint-initdb.d/` se ejecuta en orden alfabético.

### Cómo funciona la inicialización

Cuando el contenedor se inicia, verifica si el directorio de datos de PostgreSQL está vacío. Si el directorio ya contiene datos, PostgreSQL se inicia inmediatamente sin realizar ninguna inicialización. Si el directorio está vacío, el contenedor ejecuta `initdb` para crear un nuevo clúster de base de datos, luego ejecuta todos los scripts en `/docker-entrypoint-initdb.d/` en orden alfabético antes de iniciar PostgreSQL.

### Formatos de archivo compatibles

| Formato | Descripción |
|--------|-------------|
| `.sql` | Comandos SQL ejecutados directamente |
| `.sql.gz` | Archivos SQL comprimidos con Gzip |
| `.sh` | Scripts de shell ejecutados con bash |

> [!IMPORTANT]
>
> Los scripts de inicialización solo se ejecutan cuando el directorio de datos de PostgreSQL (`/var/lib/postgresql/data`) está vacío. Si montas un volumen que contiene datos existentes, se omitirá la inicialización. Este comportamiento evita que se sobrescriban las bases de datos existentes.

## Montar scripts de inicialización

Usa Docker Compose para montar scripts de inicialización en el contenedor. Primero, crea un directorio de proyecto:

```console
$ mkdir -p postgres-project/init-db
$ cd postgres-project
```

Crea un archivo `compose.yaml`:

```yaml
services:
  db:
    image: postgres:18
    volumes:
      - ./init-db:/docker-entrypoint-initdb.d
      - postgres_data:/var/lib/postgresql
    environment:
      POSTGRES_PASSWORD: mysecretpassword

volumes:
  postgres_data:
```

Todos los scripts en el directorio `./init-db` se ejecutan cuando el contenedor se inicia por primera vez. Esto es ideal para inicializar bases de datos.

## Ejemplo de script de inicialización

Crea un archivo llamado `init.sql` en tu directorio `init-db`:

```sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    name VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

Este script se ejecuta automáticamente cuando el contenedor se inicia por primera vez, creando tu esquema de base de datos inicial.

> [!NOTE]
>
> Asegúrate de que los scripts de inicialización tengan los permisos de lectura adecuados. Si te encuentras con errores de "Permiso denegado", ejecuta `chmod 644 init-db/*.sql` para que los archivos sean legibles por el contenedor.

## Ajuste de rendimiento

La configuración predeterminada de PostgreSQL es conservadora para funcionar en sistemas con recursos limitados. Para cargas de trabajo de producción, debes ajustar estos parámetros según los recursos asignados a tu contenedor.

### Método 1: Archivo de configuración personalizado

Para obtener un control total, monta un archivo `postgresql.conf` personalizado. Primero, extrae la configuración predeterminada:

```console
$ docker run -i --rm postgres:18 cat /usr/share/postgresql/postgresql.conf.sample > my-postgres.conf
```

Edita `my-postgres.conf` con la configuración deseada, luego móntalo en tu archivo de Compose:

```yaml
services:
  db:
    image: postgres:18
    volumes:
      - ./my-postgres.conf:/etc/postgresql/postgresql.conf
      - ./init-db:/docker-entrypoint-initdb.d
      - postgres_data:/var/lib/postgresql
    command: postgres -c config_file=/etc/postgresql/postgresql.conf
    environment:
      POSTGRES_PASSWORD: mysecretpassword

volumes:
  postgres_data:
```

## Parámetros clave de configuración

Las siguientes tablas enumeran parámetros importantes de `postgresql.conf` para despliegues contenedorizados de PostgreSQL.

### Configuración de conexión

| Parámetro | Descripción | Valor predeterminado |
|-----------|-------------|---------|
| `listen_addresses` | Direcciones IP en las que escuchar | `localhost` |
| `port` | Número de puerto TCP | `5432` |
| `max_connections` | Máximo de conexiones concurrentes | `100` |

### Configuración de memoria

| Parámetro | Descripción | Valor inicial recomendado |
|-----------|-------------|---------------------------|
| `shared_buffers` | Memoria compartida para caché | 25% de la memoria del contenedor |
| `work_mem` | Memoria por operación de consulta | 4MB - 64MB |
| `maintenance_work_mem` | Memoria para VACUUM, CREATE INDEX | 64MB - 256MB |
| `effective_cache_size` | Estimación del tamaño de caché del planificador | 50-75% de la memoria del contenedor |

#### Límites de memoria de Docker

Al ajustar los parámetros de memoria, establece límites de memoria explícitos en tu contenedor utilizando `deploy.resources.limits.memory` en Compose o `--memory` con `docker run`. Sin límites, PostgreSQL ve la RAM total del host y podría asignar más de lo previsto. Por ejemplo, si tu contenedor debe usar un máximo de 4 GB, establece `shared_buffers` en aproximadamente 1 GB (25%).

### Configuración de E/S

| Parámetro | Descripción | Valor inicial recomendado |
|-----------|-------------|---------------------------|
| `effective_io_concurrency` | Operaciones de E/S de disco concurrentes | `200` para SSD, `2` para HDD |

### Configuración de límites de tiempo (timeout)

| Parámetro | Descripción | Valor predeterminado |
|-----------|-------------|---------|
| `statement_timeout` | Tiempo máximo para cualquier sentencia | `0` (desactivado) |
| `lock_timeout` | Tiempo máximo de espera para un bloqueo | `0` (desactivado) |
| `deadlock_timeout` | Tiempo antes de verificar un interbloqueo (deadlock) | `1s` |
| `transaction_timeout` | Tiempo máximo para una transacción | `0` (desactivado) |

> [!NOTE]
>
> Establecer `shared_buffers` demasiado alto en un contenedor puede exceder los límites de memoria compartida del kernel. No uses más del 25-30% del límite de memoria del contenedor.

## Configuración de zona horaria e idioma (locale)

Una localización adecuada garantiza que las marcas de tiempo y el ordenamiento se comporten correctamente para los usuarios de tu aplicación.

```yaml
services:
  db:
    image: postgres:18
    volumes:
      - postgres_data:/var/lib/postgresql
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    environment:
      POSTGRES_PASSWORD: mysecretpassword
      TZ: America/New_York

volumes:
  postgres_data:
```

Alternativamente, establece la zona horaria usando un parámetro de línea de comandos de PostgreSQL:

```yaml
services:
  db:
    image: postgres:18
    command: ["postgres", "-c", "timezone=America/New_York"]
    environment:
      POSTGRES_PASSWORD: mysecretpassword
```

### Configurar el idioma (locale)

Especifica la configuración de idioma (locale) durante la inicialización de la base de datos usando la variable de entorno `POSTGRES_INITDB_ARGS`:

```yaml
services:
  db:
    image: postgres:18
    volumes:
      - postgres_data:/var/lib/postgresql
    environment:
      POSTGRES_PASSWORD: mysecretpassword
      POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=en_US.UTF-8 --lc-ctype=en_US.UTF-8"

volumes:
  postgres_data:
```

Esto afecta a la colación (ordenamiento) y al comportamiento de procesamiento de caracteres. Cambiar esta variable después de la creación de la base de datos no tiene ningún efecto: solo se aplica durante la primera ejecución cuando se inicializa el directorio de datos.

## Conectarse a la base de datos

Puedes interactuar con PostgreSQL ejecutándose en un contenedor incluso sin tener `psql` instalado en tu máquina host.

### Shell interactivo

Abre una sesión de `psql` dentro del contenedor:

```console
$ docker exec -it postgres-container psql -U postgres
```

Conéctate a una base de datos específica:

```console
$ docker exec -it postgres-container psql -U postgres -d mydb
```
