Docker para Bases de Datos
Hasta ahora corriste PostgreSQL dentro del navegador. Para trabajar en serio necesitás el motor de verdad en tu máquina, sin instalarlo a mano y sin ensuciar tu sistema. Con Docker levantás PostgreSQL y MongoDB con un comando, los borrás sin dejar rastro y los reproducís idénticos en cualquier computadora.
Bienvenido
En las guías anteriores escribiste SQL contra un PostgreSQL que vivía dentro de tu navegador. Sirve para aprender, pero un proyecto real necesita un servidor de base de datos que arranque, guarde datos en disco y acepte conexiones desde tu aplicación. Docker te da eso sin instalar el motor directamente en tu sistema operativo.
Al finalizar esta guía sabrás levantar PostgreSQL y MongoDB con docker run, configurarlos con variables de entorno, hacer que sus datos sobrevivan con volúmenes, conectarte mediante puertos, y describir los dos motores juntos en un archivo docker-compose.yml.
A diferencia de los talleres, acá los comandos los corrés vos, en tu propia terminal. Para eso necesitás Docker instalado: Docker Desktop en Windows o macOS (en Windows usa WSL 2), o Docker Engine en Linux. Comprobalo con docker --version y docker compose version. Los bloques de código de esta guía sí se pueden seleccionar y copiar.
Encontrarás actividades cortas y autocorregibles repartidas por la guía, un armador de comandos interactivo para experimentar sin calificar, y una sección de escenarios de fallas reales donde tenés que decidir qué pasó antes de ver la respuesta.
📦 Aislá
Cada motor corre en su propio contenedor, sin tocar el resto de tu computadora.
💾 Persistí
Los volúmenes deciden si tus datos sobreviven cuando el contenedor desaparece.
🔌 Conectá
Puertos y variables de entorno son la puerta de entrada y la configuración de cada base de datos.
Imágenes y contenedores
Instalar PostgreSQL directamente en tu sistema tiene costos escondidos: una sola versión a la vez, servicios que arrancan solos, y una desinstalación que casi nunca deja todo limpio. Un contenedor evita todo eso.
| Concepto | Qué es |
|---|---|
| Imagen | Una plantilla de solo lectura con el programa y todo lo que necesita (por ejemplo postgres:16). Se descarga una vez desde un registro como Docker Hub. |
| Contenedor | Una instancia en ejecución de una imagen, con su propio sistema de archivos y su propia red. De una misma imagen podés crear varios contenedores. |
| Volumen | Un espacio de almacenamiento que vive fuera del contenedor. Es lo que permite que los datos sobrevivan a su borrado. |
| Etiqueta (tag) | La versión de la imagen después de los dos puntos: postgres:16. Sin etiqueta Docker usa latest, y una actualización silenciosa puede romperte el proyecto. |
Escribí postgres:16 y no solo postgres. Así todo el equipo, y vos dentro de seis meses, levantan exactamente el mismo motor. Un salto de versión mayor puede cambiar el formato de los datos en disco.
Corrés docker run tres veces con la imagen postgres:16, cada vez con un nombre distinto. ¿Qué obtenés?
docker run con PostgreSQL
Este es el comando mínimo para tener un PostgreSQL real escuchando en tu máquina:
docker run -d --name pg-uab \
-e POSTGRES_PASSWORD=cambiame123 \
-p 5432:5432 \
postgres:16
La primera vez, Docker descarga la imagen (puede tardar un par de minutos). Después crea el contenedor y lo deja corriendo. Cada parte del comando cumple un rol:
| Parte | Qué hace |
|---|---|
-d | Modo detached: el contenedor corre en segundo plano y te devuelve la terminal. |
--name pg-uab | Le pone un nombre que vos elegís. Sin esto Docker inventa uno al azar. |
-e VARIABLE=valor | Define una variable de entorno dentro del contenedor: así se configura la imagen. |
-p 5432:5432 | Publica un puerto: conecta el puerto de tu máquina con el del contenedor. |
postgres:16 | La imagen y su versión. Va siempre al final. |
Para comprobar que está vivo y entrar a un psql dentro del contenedor:
docker ps # contenedores en ejecución
docker logs pg-uab # mensajes del motor al arrancar
docker exec -it pg-uab psql -U postgres
Dentro de psql ya podés correr CREATE TABLE, INSERT y todo lo que practicaste en las guías anteriores, pero ahora sobre un PostgreSQL de verdad. Salís con \q.
Clasifica cada necesidad según el tipo de opción de docker run que la resuelve.
Variables de entorno
Las imágenes oficiales no traen un archivo de configuración para editar: se configuran con variables de entorno que definís con -e. Estas son las de PostgreSQL:
| Variable | Efecto |
|---|---|
POSTGRES_PASSWORD | Obligatoria. Contraseña del superusuario. Sin ella el contenedor se detiene al arrancar. |
POSTGRES_USER | Nombre del superusuario. Si no la definís, es postgres. |
POSTGRES_DB | Base de datos que se crea al iniciar. Si no la definís, usa el nombre del usuario. |
docker run -d --name pg-uab \
-e POSTGRES_USER=roy \
-e POSTGRES_PASSWORD=cambiame123 \
-e POSTGRES_DB=tienda \
-p 5432:5432 \
postgres:16
PostgreSQL usa POSTGRES_USER, POSTGRES_PASSWORD y POSTGRES_DB únicamente cuando inicializa un directorio de datos vacío. Si después cambiás la contraseña en el comando pero el volumen ya tiene datos, el motor ignora el valor nuevo y sigue con el anterior. Es la causa de confusión más frecuente con Docker y bases de datos.
Escribirla en el comando la deja en el historial de tu terminal. Escribirla en un docker-compose.yml versionado la publica junto con el código. La práctica correcta es guardarla en un archivo .env que Git ignora (línea .env en .gitignore) y pasarla con --env-file .env o dejar que Compose la lea. Lo vas a ver en la sección de docker-compose.
Sobre la configuración de la imagen de PostgreSQL:
1. Si arrancás el contenedor sin definir la contraseña del superusuario, el resultado es que .
2. Para que la contraseña no quede escrita en el código, lo más adecuado es guardarla en .
Volúmenes: que los datos sobrevivan
Un contenedor es desechable por diseño. Todo lo que escribe dentro de su propio sistema de archivos desaparece con él. Para una base de datos eso es un desastre, salvo que guardes los datos en un volumen.
docker run -d --name pg-uab \
-e POSTGRES_PASSWORD=cambiame123 \
-p 5432:5432 \
-v pgdata:/var/lib/postgresql/data \
postgres:16
-v pgdata:/var/lib/postgresql/data significa: "guardá lo que el contenedor escribe en /var/lib/postgresql/data (el directorio de datos de PostgreSQL) dentro del volumen llamado pgdata". Si borrás el contenedor y creás otro con el mismo -v, la base de datos reaparece intacta.
| Opción | Sintaxis | Cuándo conviene |
|---|---|---|
| Sin volumen | (nada) | Pruebas descartables. Al borrar el contenedor, la base de datos queda huérfana y el próximo arranca vacío. |
| Volumen con nombre | -v pgdata:/ruta | La opción recomendada para bases de datos. Docker lo administra y funciona igual en todos los sistemas. |
| Carpeta local (bind mount) | -v ./datos:/ruta | Cuando querés ver los archivos desde tu máquina, por ejemplo para scripts. En Windows y macOS con Postgres puede dar problemas de permisos. |
La imagen de PostgreSQL crea un volumen anónimo automáticamente aunque no lo pidas. Al borrar el contenedor, esos datos quedan en un volumen con nombre aleatorio al que nadie apunta, y el contenedor nuevo arranca con una base vacía. Por eso conviene siempre nombrar el volumen.
Este ejemplo usa postgres:16. Desde PostgreSQL 18 la imagen oficial cambió el punto de montaje recomendado. Si usás otra versión mayor, revisá la documentación de la imagen en Docker Hub antes de copiar la ruta.
Para administrar volúmenes:
docker volume ls # lista los volúmenes
docker volume inspect pgdata # dónde vive y desde cuándo existe
docker volume rm pgdata # BORRA los datos para siempre
Une cada comando con su efecto sobre los datos guardados en un volumen con nombre.
Creaste una base con -v pgdata:/var/lib/postgresql/data, cargaste datos y ejecutaste docker rm -f pg-uab. ¿Cómo recuperás la base?
Puertos y conexión
Un contenedor tiene su propia red aislada. PostgreSQL escucha en el puerto 5432 adentro, pero tu máquina no lo ve hasta que lo publicás con -p.
-p PUERTO_DE_TU_MAQUINA:PUERTO_DEL_CONTENEDOR
El puerto de la derecha lo fija el motor: 5432 para PostgreSQL, 27017 para MongoDB. El de la izquierda lo elegís vos. Si ya tenés otro PostgreSQL instalado en tu máquina usando el 5432, Docker se queja con port is already allocated. La solución es cambiar solo el lado izquierdo:
docker run -d --name pg-uab \
-e POSTGRES_PASSWORD=cambiame123 \
-p 5433:5432 \
-v pgdata:/var/lib/postgresql/data \
postgres:16
# desde tu máquina ahora te conectás al 5433
psql -h localhost -p 5433 -U postgres
Una aplicación o herramienta (DBeaver, tu backend) usa una cadena de conexión con esos mismos datos:
postgresql://postgres:cambiame123@localhost:5433/postgres
mongodb://admin:cambiame123@localhost:27017/?authSource=admin
-p 5432:5432 escucha en todas las interfaces de tu máquina, y por tanto en tu red local. En una computadora compartida o una red pública conviene limitarlo a tu equipo con -p 127.0.0.1:5432:5432. Y nunca uses una contraseña como postgres o admin en un puerto abierto.
Ejecutaste el contenedor con -p 5433:5432. Desde DBeaver, en tu máquina, ¿a qué puerto te conectás?
MongoDB en un contenedor
MongoDB sigue exactamente el mismo patrón: imagen oficial, variables de entorno para el usuario administrador, un puerto y un volumen. Lo único que cambia son los nombres.
docker run -d --name mongo-uab \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=cambiame123 \
-p 27017:27017 \
-v mongodata:/data/db \
mongo:7
docker exec -it mongo-uab mongosh -u admin -p cambiame123 --authenticationDatabase admin
| PostgreSQL | MongoDB | |
|---|---|---|
| Imagen | postgres:16 | mongo:7 |
| Puerto estándar | 5432 | 27017 |
| Datos dentro del contenedor | /var/lib/postgresql/data | /data/db |
| Contraseña del administrador | POSTGRES_PASSWORD | MONGO_INITDB_ROOT_PASSWORD |
| Cliente en la terminal | psql | mongosh |
Con esto ya tenés el entorno listo para las próximas guías: JSON en PostgreSQL y, después, MongoDB de verdad. Y para el proyecto integrador vas a necesitar los dos motores corriendo a la vez, que es justo lo que resuelve la siguiente sección.
Une cada dato con lo que representa en la configuración de los contenedores.
docker-compose: los dos motores juntos
Dos motores con sus puertos, variables y volúmenes son dos comandos largos que nadie quiere recordar. Docker Compose describe todo en un archivo docker-compose.yml y lo levanta con un solo comando. Además queda dentro del repositorio: quien clone el proyecto tiene el mismo entorno.
services:
postgres:
image: postgres:16
container_name: pg-uab
environment:
POSTGRES_USER: roy
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: tienda
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U roy -d tienda"]
interval: 5s
retries: 5
mongo:
image: mongo:7
container_name: mongo-uab
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD}
ports:
- "27017:27017"
volumes:
- mongodata:/data/db
volumes:
pgdata:
mongodata:
POSTGRES_PASSWORD=cambiame123
MONGO_PASSWORD=otra-clave-distinta-456
Compose lee el .env automáticamente y reemplaza cada ${...}. La estructura es la misma que ya conocés, ahora en YAML: image, environment (los -e), ports (los -p) y volumes (los -v). Los volúmenes con nombre se declaran además al final, en el bloque volumes: de nivel superior.
| Comando | Qué hace |
|---|---|
docker compose up -d | Crea y arranca todos los servicios en segundo plano. |
docker compose ps | Muestra el estado de los servicios del archivo. |
docker compose logs -f postgres | Sigue en vivo los mensajes de un servicio. |
docker compose down | Detiene y elimina los contenedores. Los volúmenes se conservan. |
docker compose down -v | Además elimina los volúmenes: borra todos los datos. |
Compose pone todos los servicios del archivo en una misma red y cada uno se alcanza por su nombre. Cuando agregues tu aplicación como otro servicio, se conectará al host postgres o mongo, no a localhost: dentro de un contenedor, localhost es ese mismo contenedor. Los puertos publicados con ports sirven para las herramientas que corren en tu máquina.
Si montás una carpeta con archivos .sql en /docker-entrypoint-initdb.d, PostgreSQL los ejecuta en orden al inicializar. Es la forma de arrancar con las tablas del esquema ya creadas. Como las variables de entorno, corren solo la primera vez, con el directorio de datos vacío.
Sobre el archivo de Compose de arriba:
1. Para levantar los dos motores juntos, en segundo plano, ejecutás .
2. Para apagar todo pero conservar los datos guardados, ejecutás .
3. Una aplicación que corre como otro servicio de este archivo se conecta a PostgreSQL usando el host .
Armador de comandos
Elegí los motores y las opciones, y mirá cómo cambian al instante el comando docker run y el docker-compose.yml. Los avisos de abajo te dicen qué riesgo introduce cada decisión.
Cuando algo falla
Casi todos los problemas con bases de datos en Docker caen en un puñado de errores. Reconocerlos por su mensaje te ahorra horas.
| Mensaje o síntoma | Causa habitual |
|---|---|
port is already allocated | Otro programa o contenedor ya usa ese puerto de tu máquina. Cambiá el número de la izquierda de -p. |
container name "/pg-uab" is already in use | Ya existe un contenedor con ese nombre, aunque esté detenido. Borralo con docker rm pg-uab o elegí otro nombre. |
Database is uninitialized and superuser password is not specified | Falta POSTGRES_PASSWORD. |
password authentication failed | La contraseña que usás no es la que quedó guardada al inicializar el volumen. Cambiar la variable después no la modifica. |
| La base de datos aparece vacía después de recrear el contenedor | El contenedor nuevo montó otro volumen (o ninguno). Revisá el -v con docker volume ls. |
Tu compañera cambió POSTGRES_PASSWORD en el docker-compose.yml y ejecutó docker compose up -d, pero el volumen pgdata ya tenía datos. Al conectarse con la clave nueva recibe password authentication failed. ¿Por qué?
Al ejecutar docker run ... -p 5432:5432 ... aparece port is already allocated. Tenés un PostgreSQL instalado en tu PC que no querés apagar. ¿Qué hacés?
Un equipo trabaja con Compose y volúmenes con nombre. Predecí qué pasa en cada caso:
1. Ejecutan docker compose down al terminar el día y docker compose up -d a la mañana siguiente: las tablas
.
2. Ejecutan docker compose down -v y luego up -d: la base de datos
.
Actividades de repaso
Un repaso integrador de todo el módulo. Cada actividad se corrige al instante; tu progreso se guarda automáticamente en este navegador.
¿Qué diferencia hay entre una imagen y un contenedor?
Al ejecutar docker rm sobre un contenedor de PostgreSQL cuyo directorio de datos está montado en un volumen con nombre, se borran también los datos del volumen.
¿Qué configura la variable MONGO_INITDB_ROOT_USERNAME?
Tu aplicación corre como un servicio más del mismo docker-compose.yml que PostgreSQL. ¿Qué host usa en su cadena de conexión?
¿Por qué conviene escribir postgres:16 en lugar de solo postgres?
Cheat Sheet
| Comando | Para qué sirve |
|---|---|
docker run -d --name X -e V=v -p H:C -v vol:/ruta imagen:tag | Crea y arranca un contenedor en segundo plano. |
docker ps -a | Lista contenedores, incluidos los detenidos. |
docker logs -f X | Sigue en vivo los mensajes del contenedor. |
docker exec -it X psql -U usuario | Abre un cliente dentro del contenedor. |
docker stop X / docker start X | Detiene o reanuda un contenedor sin perder nada. |
docker rm X | Elimina el contenedor (los volúmenes con nombre quedan). |
docker volume ls / rm V | Lista o elimina volúmenes. rm borra los datos. |
docker compose up -d / down | Levanta o apaga todos los servicios del archivo. |
docker compose down -v | Apaga y borra también los volúmenes: pierde los datos. |
POSTGRES_PASSWORD · MONGO_INITDB_ROOT_PASSWORD | Contraseñas de administrador; solo se leen al inicializar. |
¿Sabías que...?
🐋 El nombre
Docker se lanzó como proyecto de código abierto en 2013. La ballena de su logo carga contenedores sobre el lomo, igual que los barcos de carga que inspiraron el nombre.
⚡ No es una máquina virtual
Un contenedor comparte el núcleo del sistema operativo de tu computadora, por eso arranca en segundos y pesa mucho menos que una máquina virtual completa, que trae su propio sistema operativo.
🧹 Entornos descartables
Como un contenedor se destruye y recrea en segundos, muchos equipos levantan una base de datos limpia para cada ejecución de sus pruebas automáticas y la tiran al terminar.