Docker Compose permite definir varios servicios relacionados en un único archivo. En esta guía montaremos NGINX para servir una página estática, MariaDB para almacenar datos y phpMyAdmin como interfaz de administración. Los servicios comparten una red privada de Compose; solo NGINX y phpMyAdmin publican puertos en el host, y phpMyAdmin queda limitado a la máquina local [1][2].
Nota: este ejemplo sirve para aprender y para un entorno local. Antes de exponerlo en Internet, añade HTTPS, autenticación y controles de acceso adecuados; no publiques phpMyAdmin directamente.
Estructura del proyecto
Crea una carpeta de trabajo con esta estructura:
web-stack/
├── compose.yaml
├── secrets/
│ ├── mariadb_root_password.txt
│ └── mariadb_app_password.txt
└── web/
└── index.html
Escribe una contraseña única en cada archivo de secrets/. No uses los valores de ejemplo de este artículo ni subas estos archivos a un repositorio. Restringe el acceso en el host:
chmod 600 secrets/mariadb_root_password.txt secrets/mariadb_app_password.txt
Prepara una página de prueba en web/index.html:
<!doctype html>
<html lang="es">
<meta charset="utf-8">
<title>Servidor NGINX en Docker</title>
<h1>NGINX está funcionando</h1>
</html>
Definir los servicios en Compose
Crea compose.yaml:
services:
web:
image: nginx:stable-alpine
ports:
- "8080:80"
volumes:
- ./web:/usr/share/nginx/html:ro
restart: unless-stopped
db:
image: mariadb:lts
environment:
MARIADB_DATABASE: aplicacion
MARIADB_USER: appuser
MARIADB_PASSWORD_FILE: /run/secrets/mariadb_app_password
MARIADB_ROOT_PASSWORD_FILE: /run/secrets/mariadb_root_password
secrets:
- mariadb_app_password
- mariadb_root_password
volumes:
- mariadb_data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 10s
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
phpmyadmin:
image: phpmyadmin:5.2
environment:
PMA_HOST: db
ports:
- "127.0.0.1:8081:80"
depends_on:
db:
condition: service_healthy
restart: unless-stopped
secrets:
mariadb_app_password:
file: ./secrets/mariadb_app_password.txt
mariadb_root_password:
file: ./secrets/mariadb_root_password.txt
volumes:
mariadb_data:
Compose crea una red para el proyecto, así que phpMyAdmin puede encontrar MariaDB usando el nombre de servicio db; no hace falta publicar el puerto de la base de datos en el host [1][3]. El volumen con nombre mariadb_data conserva los ficheros de la base aunque se reemplace el contenedor.
Las contraseñas se montan como secretos en los servicios que las necesitan. La imagen oficial de MariaDB admite las variables MARIADB_ROOT_PASSWORD_FILE y MARIADB_PASSWORD_FILE, que leen las claves desde esos archivos [4][5]. Los secretos de Compose basados en archivos se conceden explícitamente a cada servicio y se montan como archivos dentro del contenedor [2].
El puerto 8080 de NGINX se publica en las interfaces del host. El puerto 8081 de phpMyAdmin se enlaza a 127.0.0.1, por lo que solo es accesible desde el propio host. Docker advierte que publicar un puerto sin especificar una dirección normalmente lo hace accesible a través de las interfaces de red del host [6].
Validar y arrancar
Desde web-stack/, valida el YAML y luego inicia los servicios:
docker compose config --quiet
docker compose up -d
docker compose ps
Abre http://localhost:8080 para comprobar NGINX. Para administrar la base desde el mismo host, visita http://localhost:8081 e inicia sesión con el usuario appuser y la contraseña guardada en mariadb_app_password.txt. El usuario root queda reservado para tareas administrativas desde un entorno controlado.
Compose espera a que el healthcheck de MariaDB indique que el servicio está sano antes de crear phpMyAdmin, gracias a depends_on con condition: service_healthy [3]. El healthcheck healthcheck.sh forma parte de la imagen oficial de MariaDB [5].
Operación y persistencia
Consulta el estado y los registros con:
docker compose ps
docker compose logs --follow db
docker compose logs --follow phpmyadmin
Detén los contenedores sin eliminar el volumen de datos:
docker compose down
No añadas --volumes (-v) si quieres conservar la base: esa opción elimina los volúmenes declarados por Compose. La base se guarda en mariadb_data; haz copias de seguridad de forma independiente, pues un volumen no sustituye a un backup [7].
Las variables de inicialización de MariaDB, como el nombre de la base y las credenciales, se aplican al crear una base vacía. Si el volumen ya contiene una base, cambiar esas variables en Compose no modifica automáticamente las cuentas ni los datos existentes [4]. Para actualizar imágenes, revisa las notas de versión y fija una versión concreta adecuada a tu despliegue en lugar de depender de una etiqueta móvil.
En resumen
- Compose define NGINX, MariaDB y phpMyAdmin como servicios separados.
- La base de datos no publica un puerto en el host; phpMyAdmin la alcanza por la red privada de Compose.
- Los archivos de secretos evitan escribir las contraseñas en el YAML, pero también deben protegerse y excluirse del control de versiones.
- El volumen nombrado conserva los datos cuando se eliminan los contenedores, siempre que no se borre explícitamente.
- phpMyAdmin queda disponible solo desde el host local en este ejemplo.
📚 Fuentes consultadas
- Networking in Compose — Docker Docs
- Manage secrets securely in Docker Compose — Docker Docs
- Control startup and shutdown order in Compose — Docker Docs
- MariaDB Server Docker Official Image Environment Variables — MariaDB Documentation
- Using Healthcheck — MariaDB Documentation
- Define services in Docker Compose — Docker Docs
- Volumes — Docker Docs