Por qué `latest` es el verdadero culpable
Cuando una imagen Docker etiquetada como latest se actualiza en el registry, tu docker compose pull la descarga en silencio. Sin advertencia, sin diff. Reinicias el stack y descubres que el nuevo contenedor no puede leer los datos que dejó el anterior.
Esto es exactamente lo que ocurrió con Meilisearch v1.54. Esta versión introdujo un nuevo formato de almacenamiento vectorial por defecto (cambio de arroy a HNSW) que hace el directorio de datos incompatible con versiones anteriores. Al iniciarse, Meilisearch rechaza abrir la base de datos y entra en crash-loop. El único camino de recuperación limpio es crear un dump antes de la actualización — imposible una vez que el contenedor está bloqueado.
El tag latest no resuelve a la misma imagen según el momento del pull. Dos desarrolladores que ejecutan docker compose pull con doce horas de diferencia pueden descargar versiones distintas. En un stack de producción, esta ambigüedad es inaceptable. La solución no es evitar las actualizaciones: es controlar explícitamente qué versión está en ejecución y decidir conscientemente cuándo pasar a la siguiente.
Qué cubre esta guía — y qué no
- Qué cubre esta guía: el protocolo paso a paso para actualizar un stack Docker Compose en un VPS — version pinning, backup de volúmenes, revisión de notas de versión, rollback rápido, healthchecks como red de seguridad.
- Cubierto en otras guías: el checklist de hardening inicial (
docker-compose-production-checklist), la configuración dedepends_onyservice_healthy(docker-compose-depends-on-healthcheck), y la comparativa de herramientas de actualización automática como Watchtower o Diun. - Lo que esta guía no recomienda: Watchtower o cualquier herramienta de auto-pull en producción — ese es precisamente el antipatrón que ilustran los casos de breaking changes.
- Público objetivo: desarrolladores y agencias que gestionan uno o más stacks Docker Compose en producción en un VPS, con acceso root y volúmenes persistentes.
Paso 1 — Fija todas tus imágenes
La primera acción, antes de cualquier actualización, es reemplazar cada image: meili/meilisearch:latest o image: supabase/postgres por una versión explícita.
Dos formas son aceptables:
- Tag de versión: image: getmeili/meilisearch:v1.53.0 — legible, versionable en git, fácil de parchear.
- Digest SHA256: image: getmeili/meilisearch@sha256:abc123… — inmutable, garantiza que descargas exactamente el mismo artefacto en cada redespliegue, incluso si el tag fue sobreescrito.
Para obtener el digest de una imagen ya en ejecución:
docker inspect --format='{{index .RepoDigests 0}}' getmeili/meilisearch:v1.53.0Una vez fijadas tus imágenes, haz commit del docker-compose.yml en git. Cada bump de versión se convierte en un commit, lo que te da un historial claro y un rollback trivial (git revert + docker compose up -d).
Protocolo de actualización — los 5 pasos
Lee las notas de versión antes de hacer pull
Primero, consulta las notas de versión de la nueva versión. Busca las palabras
breaking,migration,incompatible,pg_upgrade,dump. Esto no es opcional: Supabase documentó explícitamente que actualizar de PostgreSQL 15 a 17 requiere unpg_upgrademanual — el contenedor PG 17 se niega a arrancar sobre un volumen PG 15, y el proceso de inicialización no migra los datos automáticamente.Para Langfuse v4 (lanzado el 17 de agosto de 2026), los SDKs Python v2 y anteriores son rechazados en la ingesta por el nuevo stack — un cambio disruptivo que afecta a todos los servicios cliente que rastrean mediante la API antigua.
Tres minutos de lectura te ahorran varias horas de recuperación de datos.
Haz backup de los volúmenes antes del pull
Nunca hagas pull sin tener un backup utilizable. Para volúmenes nombrados, dos enfoques:
Dump aplicativo (recomendado para bases de datos) — el servicio debe estar
healthyantes de hacer el dump:docker compose exec db pg_dump -U postgres -Fc mydb > backup_$(date +%Y%m%d_%H%M%S).dumpSnapshot de volumen en crudo — útil para almacenes binarios (Meilisearch, Redis, MinIO):
docker run --rm \ --volumes-from $(docker compose ps -q meilisearch) \ -v $(pwd)/backups:/backup \ alpine tar czf /backup/meili_$(date +%Y%m%d_%H%M%S).tar.gz /meili_dataVerifica que el backup es legible antes de continuar. Un archivo de dump corrupto descubierto durante la recuperación es el escenario más costoso que existe.
Descarga la nueva imagen y pruébala fuera de producción
Actualiza el tag en tu
docker-compose.yml, luego descarga la imagen sin reiniciar el servicio:docker compose pull meilisearchSi tu entorno lo permite, prueba la nueva imagen sobre un clon del volumen en un entorno ddev o una VM de staging antes de tocar producción. Revisa los logs de arranque en busca de errores de migración:
docker compose up -d meilisearch docker compose logs -f meilisearchEspera a que el healthcheck alcance el estado
healthyantes de validar. Un servicio que arranca pero no estáhealthytodavía no es un servicio listo.Verifica los healthchecks
Un healthcheck bien configurado es tu primera línea de detección. Debe estar presente en cada servicio crítico del stack, en formato Compose v2:
healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:7700/health || exit 1"] interval: 10s timeout: 5s retries: 5 start_period: 30sEl campo
start_periodes crítico para servicios de arranque lento (bases de datos, motores de búsqueda): evita que Docker declare el contenedorunhealthydurante la fase de inicialización y desencadene un reinicio prematuro.Consulta
docker-compose-depends-on-healthcheckpara la configuración completa deservice_healthyen PostgreSQL — el mismo principio aplica a cualquier servicio que necesite un período de calentamiento.Rollback si algo falla
Si la nueva versión no arranca o produce errores, el rollback debe tomar menos de dos minutos. El procedimiento:
1. Vuelve al tag anterior en
docker-compose.yml(ogit revertsi hiciste commit del bump).
2. Reinicia únicamente el servicio afectado, sin recrear los volúmenes:docker compose up -d --no-deps --force-recreate meilisearch3. Revisa los logs inmediatamente:
docker compose logs -f meilisearchEl flag
--no-depses esencial: reinicia el servicio objetivo sin tocar los otros contenedores (base de datos, caché, proxy). Sin él,docker compose up -dpuede recrear todo el stack.⚠️ Si la nueva versión migró el formato de los datos en disco (Meilisearch v1.54, Supabase PG17), revertir la imagen no es suficiente — por eso el backup del volumen es una precondición, no una opción.
Mantén la versión anterior disponible localmente
Antes de descargar la nueva imagen, etiqueta la imagen actualmente en producción con un nombre de retención:
docker tag getmeili/meilisearch:v1.53.0 getmeili/meilisearch:rollbackEsto te permite volver al estado exacto de producción en caso de emergencia, incluso sin acceso al registry o con conexión lenta. En un VPS con ancho de banda limitado, este tag local te ahorra varios minutos de descarga en el peor momento.
Los breaking changes recientes que derribaron instancias
Estos cuatro ejemplos ilustran por qué el protocolo anterior no es teórico.
Meilisearch v1.53 → v1.54 (2026): introducción del almacén vectorial HNSW como formato por defecto. Meilisearch se niega a abrir un índice creado con el antiguo formato arroy. El arranque entra en crash-loop con Your database version is incompatible with your current engine version. La única salida limpia es haber exportado un dump antes de la actualización — importarlo en la nueva versión restaura tus datos.
Supabase Docker PostgreSQL 15 → 17 (migración activada el 17 de junio de 2026): el contenedor supabase/postgres:17 no puede leer un volumen inicializado por PG 15. Supabase documenta explícitamente que el salto requiere un pg_upgrade mediante un script dedicado — el proceso de inicialización del contenedor no lo hace automáticamente. Sin una migración previa, la base de datos no arranca.
Langfuse v3 → v4 (GA el 17 de agosto de 2026): la v4 abandona los endpoints de ingesta batch legacy en favor de OpenTelemetry. Los SDKs Python v2 y anteriores y los SDKs JS/TS v3 y anteriores son rechazados en la ingesta desde el momento en que arranca el stack v4. Si tus servicios cliente no han migrado antes de la actualización del servidor, pierden silenciosamente todos sus trazados.
NocoDB 2026.09.x: la serie 2026.09 reconstruye las imágenes Docker para eliminar dependencias vulnerables. Las instalaciones que usan bind-mounts (./postgres, ./nocodb) en lugar de volúmenes nombrados pueden arrancar sobre una base de datos vacía tras el pull — NocoDB no encuentra sus datos si la ruta de montaje cambió entre versiones. Se recomienda migrar a volúmenes nombrados antes de actualizar.
Estrategias de actualización: comparativa
Desplace la tabla
| Estrategia | Seguridad de datos | Tiempo de preparación | Rollback |
|---|---|---|---|
| `docker compose pull` + `up -d` directo | Sin garantía — breaking changes no detectados | < 1 minuto | Difícil si los datos fueron migrados |
| Bump de tag versionado + backup de volumen | Alta — datos guardados antes de cualquier cambio | 10 a 20 minutos | Trivial: revertir tag + `up -d --no-deps` |
| Test en staging antes de prod | Máxima — breaking changes detectados fuera de prod | Variable según el entorno | No necesario si el test pasó |
| Imagen fijada por digest SHA256 | Alta — inmune al tag-overwrite | Igual que tag versionado | Igual que tag versionado |
Integrar este protocolo en tu flujo de trabajo
Un protocolo que queda en una guía no sirve de nada. Para aplicarlo sistemáticamente, externaliza la versión en un archivo .env versionado en git:
# .env
MEILISEARCH_VERSION=v1.53.0
POSTGRES_VERSION=15.6# docker-compose.yml
services:
meilisearch:
image: getmeili/meilisearch:${MEILISEARCH_VERSION}Actualizar una versión se convierte entonces en un único commit sobre .env — legible en git log, reversible con git revert, y desplegable por CI/CD sin modificar el archivo Compose principal.
Para agencias que gestionan múltiples stacks de clientes, crea un archivo CHANGELOG_INFRA.md por cliente: cada actualización queda registrada con la versión anterior, la fecha, el backup realizado y el resultado. Esto también te protege contractualmente ante un eventual incidente posterior.
Automatizar sin perder el control
Si quieres ser notificado de nuevas versiones sin auto-pull, Diun (Docker Image Update Notifier) monitoriza tu registry y te envía una notificación (Slack, email, webhook) cuando hay una nueva imagen disponible. Tú decides cuándo actualizar.
Esta es la diferencia fundamental con Watchtower: Diun notifica, Watchtower actúa. En un stack de producción con volúmenes persistentes, la notificación es el nivel correcto de automatización — la acción sigue siendo manual y precedida del protocolo anterior.