# Redes y conectividad


Esta guía cubre dos formas comunes de conectarse a PostgreSQL ejecutándose en Docker:

- **De contenedor a contenedor**: Conéctate desde el contenedor de tu aplicación a PostgreSQL a través de una red privada de Docker. No es necesario exponer ningún puerto al host.
- **De host a contenedor**: Conéctate desde tu computadora portátil o máquina de desarrollo usando `localhost` y un puerto publicado.

**Requisito previo**: Esta guía asume que tienes PostgreSQL ejecutándose con almacenamiento persistente. Si no es así, sigue primero la guía [Configuración inmediata y persistencia de datos](/guides/postgresql/immediate-setup-and-data-persistence/).

## Acceso de red interna (de contenedor a contenedor)

Cuando tu aplicación se ejecuta en otro contenedor, conectarse a PostgreSQL a través de una red puente (bridge network) definida por el usuario es el enfoque recomendado. Esta configuración proporciona resolución DNS automática, por lo que tu aplicación puede conectarse a PostgreSQL utilizando el nombre del contenedor como nombre de host (hostname), sin necesidad de rastrear direcciones IP.

> **¿Por qué no usar la red puente predeterminada?** Aunque los contenedores en la red puente predeterminada pueden comunicarse, solo pueden hacerlo por dirección IP. Dado que las direcciones IP de los contenedores cambian cuando estos se reinician, esto requeriría actualizar las cadenas de conexión de PostgreSQL cada vez. Las redes puente definidas por el usuario resuelven esto al proporcionar resolución DNS automática, lo que garantiza que tus cadenas de conexión de PostgreSQL se mantengan estables incluso si los contenedores se reinician y reciben nuevas direcciones IP.

Aquí tienes una comparación rápida:

> [!NOTE]
>
> Los siguientes ejemplos muestran la diferencia en el enfoque. Para probar esto en la práctica, sigue primero los pasos de esta guía para configurar contenedores en las redes adecuadas.

Con la red puente predeterminada, primero tendrías que buscar la dirección IP:
```bash
# Obtener la dirección IP del contenedor (cambia al reiniciar)
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgres-dev
# Salida: 172.17.0.2

# Luego conectarse usando esa dirección IP desde otro contenedor
# (No se necesita la bandera --network: los contenedores usan la red puente por defecto)
docker run --rm -it \
  -e PGPASSWORD=mysecretpassword \
  postgres:18 \
  psql -h 172.17.0.2 -U postgres
```

Con una red definida por el usuario, simplemente usas el nombre del contenedor:
```bash
# El nombre del contenedor funciona directamente, no se requiere buscar la IP
docker run --rm -it \
  --network my-app-net \
  -e PGPASSWORD=mysecretpassword \
  postgres:18 \
  psql -h postgres-dev -U postgres
```

### Paso 1: Crear una red definida por el usuario

```bash
docker network create my-app-net

# Salida de ejemplo
ab7f984be43a0ca15534a9ee568716ddbe869a5875077fad3ef3192e3af7d288

docker network ls
# Salida
ab7f984be43a   my-app-net    bridge    local
```

### Paso 2: Ejecutar PostgreSQL en esa red (sin publicación de puertos)

Ten en cuenta que no hay `-p 5432:5432` aquí. Esto mantiene a PostgreSQL interno a Docker y no accesible desde la máquina host, lo cual es más seguro para entornos de producción.

```bash
docker run -d --name postgres-dev \
  --network my-app-net \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -v postgres_data:/var/lib/postgresql \
  postgres:18

  # Salida
CONTAINER ID  IMAGE        COMMAND                 CREATED         STATUS        PORTS     NAMES
6d351ed89efc  postgres:18  "docker-entrypoint.s…"  9 seconds ago   Up 8 seconds  5432/tcp  postgres-dev
```

### Paso 3: Conectarse desde otro contenedor usando el nombre del contenedor Postgres

Puedes probar la conectividad con un contenedor cliente `psql` temporal:

```bash
docker run --rm -it \
  --network my-app-net \
  -e PGPASSWORD=mysecretpassword \
  postgres:18 \
  psql -h postgres-dev -U postgres
```

**Punto clave**: `-h postgres-dev` funciona porque el DNS de Docker resuelve el nombre del contenedor en una red definida por el usuario. El nombre del contenedor actúa como el nombre de host (hostname).

### Ejemplos de cadenas de conexión

Al conectarte desde el contenedor de tu aplicación, utiliza estas cadenas de conexión de PostgreSQL:

- **Formato URI de PostgreSQL**:
  Este es el formato estándar de URI de conexión de PostgreSQL que combina todos los parámetros de conexión en una sola cadena, ampliamente compatible con clientes y bibliotecas de PostgreSQL.

  ```bash
  postgresql://postgres:mysecretpassword@postgres-dev:5432/postgres
  ```

  Este comando demuestra cómo pasar una cadena de conexión URI de PostgreSQL como una variable de entorno a un contenedor, que luego tu aplicación puede leer para conectarse a la base de datos.

  Ejemplo de uso en un comando docker run:
  ```bash
  docker run --rm -it \
    --network my-app-net \
    -e DATABASE_URL="postgresql://postgres:mysecretpassword@postgres-dev:5432/postgres" \
    alpine:latest \
    sh -c 'echo "DATABASE_URL is set to: $DATABASE_URL"'
  ```


- **Parámetros de conexión de PostgreSQL**:
  Este formato utiliza pares clave-valor separados por espacios, que muchas bibliotecas de clientes de PostgreSQL aceptan como alternativa al formato URI.
  ```bash
  host=postgres-dev
  port=5432
  user=postgres
  password=mysecretpassword
  dbname=postgres
  ```

  Ejemplo de uso en código de aplicación (Python con psycopg2):
  ```python
  conn = psycopg2.connect(
      host="postgres-dev",
      port=5432,
      user="postgres",
      password="mysecretpassword",
      dbname="postgres"
  )
  ```

- **Conectarse a una base de datos específica**:
  Reemplaza el nombre de la base de datos en la cadena de conexión para conectarte a una base de datos específica en lugar de la base de datos predeterminada `postgres`.
  Si creaste una base de datos personalizada (por ejemplo, `testdb`), usa:
  ```bash
  postgresql://postgres:mysecretpassword@postgres-dev:5432/testdb
  ```

  Ejemplo con SSL desactivado (común en redes Docker):
  Agrega `?sslmode=disable` a la cadena de conexión cuando te conectes dentro de una red privada de Docker donde no se requiere cifrado SSL.
  ```bash
  postgresql://postgres:mysecretpassword@postgres-dev:5432/testdb?sslmode=disable
  ```

> [!NOTE]
>
> En estos ejemplos se utiliza el puerto predeterminado `5432`. Si te estás conectando a una instancia de PostgreSQL diferente o has cambiado el puerto, update la cadena de conexión en consecuencia. El nombre del contenedor (`postgres-dev`) es resuelto por el DNS de Docker a la dirección IP del contenedor en la red.


## Conectarse desde el Host (acceso externo)

Para conectarte a PostgreSQL desde tu máquina host utilizando herramientas como `psql`, `pgAdmin`, `DBeaver` o scripts de administración de bases de datos, necesitas publicar el puerto de PostgreSQL (`5432`) en el host. Esto permite que las herramientas externas accedan al contenedor de PostgreSQL.

### Exponer Postgres solo a localhost (recomendado para desarrollo)

Esto se asocia a `127.0.0.1` para que solo sea accesible desde tu máquina local, no desde otros dispositivos en tu red. Esta es la opción más segura para el desarrollo.

```bash
docker run -d --name postgres-dev \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -p 127.0.0.1:5432:5432 \
  -v postgres_data:/var/lib/postgresql \
  postgres:18
```

Ahora conéctate desde tu host:

- **Host**: `localhost` o `127.0.0.1`
- **Puerto**: `5432`

Si tienes `psql` instalado en tu host:
```bash
psql -h localhost -p 5432 -U postgres
```

Se te pedirá la contraseña. Alternativamente, puedes usar la variable de entorno `PGPASSWORD`:
```bash
PGPASSWORD=mysecretpassword psql -h localhost -p 5432 -U postgres
```

### Conectarse con herramientas GUI de PostgreSQL

Las herramientas GUI populares de PostgreSQL pueden conectarse utilizando estos detalles de conexión comunes: Host: `localhost`, Puerto: `5432`, Usuario: `postgres`, Base de datos: `postgres` (o el nombre de tu base de datos).

- **pgAdmin**: Una plataforma de desarrollo y administración de PostgreSQL basada en la web
- **DBeaver**: Una herramienta de base de datos universal que admite PostgreSQL y muchas otras bases de datos. Selecciona PostgreSQL como el tipo de conexión
- **TablePlus**: Una herramienta de administración de bases de datos moderna y nativa para macOS y Windows con una interfaz limpia

Todas las herramientas te pedirán la contraseña que estableciste con `POSTGRES_PASSWORD`.

### Exponer Postgres a todas las interfaces de red (usar con precaución)

Para permitir conexiones desde otros dispositivos en tu red, utiliza `-p 5432:5432` en lugar de `-p 127.0.0.1:5432:5432`. Esto asocia PostgreSQL a todas las interfaces de red de tu host, haciéndolo accesible desde cualquier dispositivo que pueda llegar a tu host, no solo desde localhost.

```bash
docker run -d --name postgres-dev \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -p 5432:5432 \
  -v postgres_data:/var/lib/postgresql \
  postgres:18
```

> [!WARNING]
>
> Exponer PostgreSQL a todas las interfaces de red (`0.0.0.0:5432`) lo hace accesible desde cualquier dispositivo que pueda llegar a tu host. Utiliza esto únicamente en entornos de red de confianza o detrás de un firewall. Para producción, considera usar un proxy inverso o una VPN en su lugar.

### Consideraciones de seguridad de PostgreSQL para el acceso externo

Al exponer PostgreSQL al acceso externo, sigue estas prácticas de seguridad específicas de PostgreSQL:

- **Evita usar el superusuario `postgres`**: El usuario `postgres` predeterminado tiene todos los privilegios de la base de datos. Crea usuarios dedicados solo con los permisos que tu aplicación necesita.
- **Usa contraseñas seguras**: Las contraseñas de PostgreSQL deben ser complejas. Considera usar variables de entorno o gestión de secretos en lugar de escribir contraseñas en código duro (hardcoding).
- **Limita la exposición de red**: Asociar a `127.0.0.1` (solo localhost) es más seguro que exponer a todas las interfaces (`0.0.0.0`).
- **Considera SSL/TLS**: Para producción, configura PostgreSQL para requerir conexiones SSL. La guía [Configuración avanzada e inicialización](/guides/postgresql/advanced-configuration-and-initialization/) muestra cómo configurar los ajustes de PostgreSQL.
- **Crea usuarios específicos de la aplicación**: Usa scripts de inicialización para crear usuarios con privilegios limitados. Por ejemplo, un usuario de solo lectura para informes o un usuario que solo pueda acceder a bases de datos específicas.

La guía [Configuración avanzada e inicialización](/guides/postgresql/advanced-configuration-and-initialization/) muestra cómo usar scripts de inicialización para crear usuarios y roles automáticamente.

## Uso de Docker Compose para redes

Docker Compose crea automáticamente una red para tus servicios, simplificando la configuración de red. Aquí tienes un ejemplo que combina el acceso interno y externo:

```yaml
services:
  db:
    image: postgres:18
    container_name: postgres-dev
    environment:
      POSTGRES_PASSWORD: mysecretpassword
    volumes:
      - postgres_data:/var/lib/postgresql
    ports:
      - "127.0.0.1:5432:5432"  # Exponer solo a localhost
    networks:
      - app-network

  app:
    build: ./my-app
    environment:
      DATABASE_URL: postgresql://postgres:mysecretpassword@db:5432/mydb
    networks:
      - app-network
    depends_on:
      - db

volumes:
  postgres_data:

networks:
  app-network:
    driver: bridge
```

En esta configuración centrada en PostgreSQL:
- El servicio `app` se conecta a PostgreSQL utilizando el nombre del servicio (`db`) como el nombre de host en la cadena de conexión
- PostgreSQL es accesible desde tu host en `localhost:5432` para herramientas externas
- Ambos servicios están aislados en una red personalizada, lo que proporciona seguridad a nivel de red
- La directiva `depends_on` garantiza que PostgreSQL se inicie antes que tu aplicación

Detalles de conexión de PostgreSQL para el servicio app:
- Nombre de host: `db` (resuelto por el DNS de Docker)
- Puerto: `5432` (puerto predeterminado de PostgreSQL)
- Base de datos: `mydb` (como se especifica en la cadena de conexión)
- Usuario: `postgres` (o un usuario personalizado que hayas creado)

> [!NOTE]
>
> Docker Compose crea automáticamente una red para tu proyecto. Los servicios pueden comunicarse entre sí por nombre de servicio sin una configuración de red explícita, pero definir una red personalizada te da más control. Para PostgreSQL, esto significa que tu aplicación siempre puede conectarse usando el nombre del servicio, independientemente de los reinicios de contenedores o los cambios de IP.

## Resolución de problemas

Esta sección cubre problemas comunes de conexión de PostgreSQL y sus soluciones al trabajar con redes Docker.

### "Could not translate host name postgres-dev"

- Ambos contenedores deben estar en la misma red de Docker (`my-app-net`).
- Verifica que la red exista: `docker network ls`
- Verifica en qué red está un contenedor: `docker inspect postgres-dev | grep NetworkMode`
- Asegúrate de estar usando una red definida por el usuario, no la red puente predeterminada

### "Connection refused" o "could not connect to server"

- **PostgreSQL aún podría estar inicializándose**: PostgreSQL tarda unos segundos en iniciarse e inicializar el clúster de la base de datos. Espera entre 5 y 10 segundos después del inicio del contenedor y vuelve a intentarlo.
- **Verifica si el contenedor de PostgreSQL se está ejecutando**:

  ```bash
  docker ps --filter name=postgres-dev
  ```

- **Revisa los registros de PostgreSQL en busca de errores de inicialización o conexión**:

  ```bash
  docker logs postgres-dev
  ```

  Busca mensajes como "database system is ready to accept connections" para confirmar que PostgreSQL se ha iniciado por completo.

- **Verifica que el mapeo de puertos sea correcto**:

  ```bash
  docker port postgres-dev
  ```

  Esto debería mostrar `5432/tcp -> 127.0.0.1:5432` (o `0.0.0.0:5432` si está asociado a todas las interfaces).

- **Prueba la conectividad de PostgreSQL desde el interior del contenedor**:

  ```bash
  docker exec -it postgres-dev psql -U postgres -c "SELECT version();"
  ```

  Si esto funciona pero las conexiones externas fallan, el problema es con la publicación de puertos, no con PostgreSQL en sí.

### "Password authentication failed" o "FATAL: password authentication failed for user"

- **Confirma la contraseña**: Verifica que estés usando la misma contraseña establecida en `POSTGRES_PASSWORD` cuando iniciaste el contenedor.
- **Volumen existente con credenciales antiguas**: Si reutilizaste un volumen existente, la contraseña de la inicialización original sigue vigente. La variable de entorno `POSTGRES_PASSWORD` solo establece la contraseña durante la primera inicialización de la base de datos. Para restablecerla:
  - Elimina el volumen: `docker volume rm postgres_data`
  - O conéctate con la contraseña antigua
  - O cambia la contraseña después de conectarte: `ALTER USER postgres WITH PASSWORD 'newpassword';`
- **Intenta conectarte con solicitud de contraseña**: `psql -h localhost -U postgres -W` (la bandera `-W` fuerza una solicitud de contraseña)
- **Usa la variable de entorno PGPASSWORD**: `PGPASSWORD=mysecretpassword psql -h localhost -U postgres`
- **Verifica la configuración de autenticación de PostgreSQL**: Si has personalizado `pg_hba.conf`, comprueba que el método de autenticación permita la autenticación por contraseña

### "Network not found"

- Asegúrate de que la red exista antes de iniciar los contenedores: `docker network create my-app-net`
- Si usas Docker Compose, the red se crea automáticamente al ejecutar `docker compose up`

