[{"data":1,"prerenderedAt":166},["ShallowReactive",2],{"seo-verification":3,"blog-docker-compose-update-best-practices-vps-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-docker-compose-update-best-practices-vps-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":27,"featuredImage":29,"bgImage":30,"posterImage":31,"relatedSolution":29,"intro":32,"sections":33,"ctaTitle":109,"ctaBody":110,"ctaButton":111,"ctaUrl":112,"relatedPosts":113},427,"docker-compose-update-best-practices-vps",{"fr":12,"en":10,"ar":13,"es":14},"docker-compose-mise-a-jour-best-practices-vps","أفضل-ممارسات-تحديث-docker-compose-على-vps","docker-compose-actualizacion-buenas-practicas-vps","Updating a Docker Compose stack in production","A complete protocol to update Docker Compose stacks without downtime or data loss: version pinning, volume backup, and two-minute rollback.",9,0,false,"2026-10-09T00:00:00+00:00","2026-10-09T22:36:09+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":25},3,"Deployment","deploiement","bg-success\u002F10 text-success",[28],{"id":23,"name":24,"slug":25,"color":26,"icon":25},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fdocker-compose-mise-a-jour-best-practices-vps-poster.svg","A `docker compose pull` followed by `up -d` has always worked — until it doesn't. The recent breaking changes in Meilisearch v1.54, Supabase PostgreSQL 15→17, Langfuse v3→v4, and NocoDB 2026.09 confirmed it the hard way: stable, months-old production instances brought down by an unprepared update. This guide gives you a repeatable protocol — back up before you pull, check release notes before restarting, and roll back to the previous image in under two minutes if something breaks.",[34,38,46,49,68,72,75,103,106],{"type":35,"title":36,"body":37},"h2","Why `latest` is the real culprit","When a Docker image tagged `latest` is updated in the registry, your `docker compose pull` downloads it silently. No warning, no diff. You restart the stack and discover the new container cannot read the data left by the old one.\n\nThis is exactly what happened with **Meilisearch v1.54**. This version introduced a new default vector storage format (switch from arroy to HNSW) that makes the data directory incompatible with older versions. On startup, Meilisearch refuses to open the database and enters a crash-loop. The only clean recovery path is to create a dump before the update — impossible once the container is stuck.\n\nThe `latest` tag does not resolve to the same image depending on when you pull. Two developers running `docker compose pull` twelve hours apart may pull different versions. On a production stack, this ambiguity is unacceptable. The solution is not to avoid updates: it is to **explicitly control which version is running** and to consciously decide when to move to the next one.",{"type":39,"title":40,"items":41},"ul","What this guide covers — and what it does not",[42,43,44,45],"**What this guide covers**: a step-by-step protocol to update a Docker Compose stack on a VPS — version pinning, volume backup, release notes review, fast rollback, healthchecks as a safety net.","**Covered elsewhere**: the initial hardening checklist (`docker-compose-production-checklist`), configuring `depends_on` and `service_healthy` (`docker-compose-depends-on-healthcheck`), and comparing auto-update tools like Watchtower or Diun.","**What this guide does not recommend**: Watchtower or any auto-pull tool in production — that is precisely the anti-pattern the breaking change cases illustrate.","**Target audience**: developers and agencies managing one or more Docker Compose stacks in production on a VPS, with root access and persistent volumes.",{"type":35,"title":47,"body":48},"Step 1 — Pin all your images","The first action, before any update, is to replace every `image: meili\u002Fmeilisearch:latest` or `image: supabase\u002Fpostgres` with an explicit version.\n\nTwo forms are acceptable:\n\n- **Version tag**: `image: getmeili\u002Fmeilisearch:v1.53.0` — readable, versionable in git, easy to patch.\n- **SHA256 digest**: `image: getmeili\u002Fmeilisearch@sha256:abc123…` — immutable, guarantees you pull exactly the same artifact on every redeploy, even if the tag has been overwritten (which happens on public registries).\n\nTo get the digest of an already-running image:\n\n```bash\ndocker inspect --format='{{index .RepoDigests 0}}' getmeili\u002Fmeilisearch:v1.53.0\n```\n\nOnce your images are pinned, commit `docker-compose.yml` to git. Every version bump becomes a commit, giving you a clear history and a trivial rollback (`git revert` + `docker compose up -d`).",{"type":50,"title":51,"steps":52},"steps","Update protocol — the 5 steps",[53,56,59,62,65],{"title":54,"body":55},"Read the release notes before pulling","First, check the release notes for the new version. Look for the words `breaking`, `migration`, `incompatible`, `pg_upgrade`, `dump`. This is not optional: Supabase explicitly documented that upgrading from PostgreSQL 15 to 17 **requires a manual `pg_upgrade`** — the PG 17 container refuses to start on a PG 15 volume, and the initialization process does not automatically migrate data.\n\nFor Langfuse v4 (released August 17, 2026), Python SDK v2 and older are **rejected at ingestion** by the new stack — a breaking change that affects all client services tracing via the old API.\n\nThree minutes of reading saves several hours of data recovery.",{"title":57,"body":58},"Back up volumes before pulling","Never pull before having a usable backup. For named volumes, two approaches:\n\n**Application dump (recommended for databases)** — the service must be `healthy` before dumping:\n\n```bash\ndocker compose exec db pg_dump -U postgres -Fc mydb > backup_$(date +%Y%m%d_%H%M%S).dump\n```\n\n**Raw volume snapshot** — useful for binary stores (Meilisearch, Redis, MinIO):\n\n```bash\ndocker run --rm \\\n  --volumes-from $(docker compose ps -q meilisearch) \\\n  -v $(pwd)\u002Fbackups:\u002Fbackup \\\n  alpine tar czf \u002Fbackup\u002Fmeili_$(date +%Y%m%d_%H%M%S).tar.gz \u002Fmeili_data\n```\n\nVerify the backup is readable before proceeding. A corrupt dump file discovered during recovery is the most expensive scenario there is.",{"title":60,"body":61},"Pull the new image and test offline","Update the tag in your `docker-compose.yml`, then pull the image without restarting the service:\n\n```bash\ndocker compose pull meilisearch\n```\n\nIf your environment allows it, test the new image on a volume clone in a ddev environment or a staging VM before touching production. Check the startup logs for any migration errors:\n\n```bash\ndocker compose up -d meilisearch\ndocker compose logs -f meilisearch\n```\n\nWait for the healthcheck to reach `healthy` before validating. A service that starts but is not yet `healthy` is not a ready service.",{"title":63,"body":64},"Check healthchecks","A well-configured healthcheck is your first detection line. It must be present on every critical service in the stack, in Compose v2 format:\n\n```bash\nhealthcheck:\n  test: [\"CMD-SHELL\", \"curl -sf http:\u002F\u002Flocalhost:7700\u002Fhealth || exit 1\"]\n  interval: 10s\n  timeout: 5s\n  retries: 5\n  start_period: 30s\n```\n\nThe `start_period` field is critical for slow-starting services (databases, search engines): it prevents Docker from declaring the container `unhealthy` during the initialization phase and triggering a premature restart.\n\nSee `docker-compose-depends-on-healthcheck` for the full `service_healthy` configuration on PostgreSQL — the same principle applies to any service that needs a warm-up period.",{"title":66,"body":67},"Roll back if something breaks","If the new version fails to start or produces errors, rollback must take under two minutes. The procedure:\n\n1. Revert to the previous tag in `docker-compose.yml` (or `git revert` if you committed the bump).\n2. Restart only the affected service, without recreating volumes:\n\n```bash\ndocker compose up -d --no-deps --force-recreate meilisearch\n```\n\n3. Check the logs immediately:\n\n```bash\ndocker compose logs -f meilisearch\n```\n\nThe `--no-deps` flag is essential: it restarts the target service without touching other containers (database, cache, proxy). Without it, `docker compose up -d` may recreate the entire stack.\n\n⚠️ If the new version migrated the on-disk data format (Meilisearch v1.54, Supabase PG17), rolling back the image is not enough — which is why the volume backup is a precondition, not an option.",{"type":69,"title":70,"body":71},"tip","Keep the previous version available locally","Before pulling the new image, tag the currently-running image under a retention name:\n\n```bash\ndocker tag getmeili\u002Fmeilisearch:v1.53.0 getmeili\u002Fmeilisearch:rollback\n```\n\nThis lets you return to the exact production state in an emergency, even if you no longer have registry access or the connection is slow. On a VPS with limited bandwidth, this local tag saves several minutes of download time at the worst moment.",{"type":35,"title":73,"body":74},"Recent breaking changes that took down instances","These four examples illustrate why the protocol above is not theoretical.\n\n**Meilisearch v1.53 → v1.54** (2026): introduction of the HNSW vector store as the default format. Meilisearch refuses to open an index created with the old arroy format. Startup enters a crash-loop with `Your database version is incompatible with your current engine version`. The only clean way out is to have exported a dump before the update — importing it into the new version restores your data.\n\n**Supabase Docker PostgreSQL 15 → 17** (migration activated June 17, 2026): the `supabase\u002Fpostgres:17` container cannot read a volume initialized by PG 15. Supabase explicitly documents that the jump requires a `pg_upgrade` via a dedicated script — the container initialization process does not do it automatically. Without a prior migration, the database does not start.\n\n**Langfuse v3 → v4** (GA August 17, 2026): v4 drops the legacy batch ingestion endpoints in favor of OpenTelemetry. Python SDK v2 and older and JS\u002FTS SDK v3 and older are **rejected at ingestion** from the moment the v4 stack starts. If your client services have not migrated before the server update, they silently lose all their traces.\n\n**NocoDB 2026.09.x**: the 2026.09 series rebuilds Docker images to remove vulnerable dependencies. Installations using bind-mounts (`.\u002Fpostgres`, `.\u002Fnocodb`) instead of named volumes may start on an empty database after the pull — NocoDB cannot find its data if the mount path changed between versions. Migrate to named volumes before updating.",{"type":76,"title":77,"headers":78,"rows":83},"comparison","Update strategies: comparison",[79,80,81,82],"Strategy","Data safety","Prep time","Rollback",[84,89,94,99],[85,86,87,88],"`docker compose pull` + `up -d` direct","No guarantee — breaking changes undetected","\u003C 1 minute","Difficult if data was migrated",[90,91,92,93],"Versioned tag bump + volume backup","High — data saved before any change","10 to 20 minutes","Trivial: revert tag + `up -d --no-deps`",[95,96,97,98],"Staging test before prod","Maximum — breaking changes caught outside prod","Varies by environment","Not needed if test passed",[100,101,102,102],"Image pinned by SHA256 digest","High — immune to tag-overwrite","Same as versioned tag",{"type":35,"title":104,"body":105},"Integrating this protocol into your workflow","A protocol that stays in a guide is useless. To apply it systematically, externalize the version into a versioned `.env` file committed to git:\n\n```bash\n# .env\nMEILISEARCH_VERSION=v1.53.0\nPOSTGRES_VERSION=15.6\n```\n\n```bash\n# docker-compose.yml\nservices:\n  meilisearch:\n    image: getmeili\u002Fmeilisearch:${MEILISEARCH_VERSION}\n```\n\nUpdating a version then becomes a single commit on `.env` — readable in `git log`, reversible with `git revert`, and deployable by CI\u002FCD without modifying the main Compose file.\n\nFor agencies managing multiple client stacks, create an `INFRA_CHANGELOG.md` per client: every update is traced with the previous version, date, backup taken, and outcome. This also protects you contractually in the event of a subsequent incident.",{"type":69,"title":107,"body":108},"Automate without losing control","If you want to be notified of new versions without auto-pulling, **Diun** (Docker Image Update Notifier) monitors your registry and sends you a notification (Slack, email, webhook) when a new image is available. You remain in control of when to update.\n\nThis is the fundamental difference from Watchtower: Diun notifies, Watchtower acts. On a production stack with persistent volumes, notification is the right level of automation — the action stays manual and preceded by the protocol above.","A VPS with root access to apply this protocol","Full dumps, snapshots before updates, rollback to a previous image: this protocol requires root access and controllable local storage. Shared hosting does not give you this level of control over Docker volumes.","See Cloud VPS plans","\u002Fvps-cloud",[114,130,146],{"id":115,"slug":116,"slugs":117,"title":121,"excerpt":122,"readTime":123,"views":18,"isPinned":19,"publishedAt":124,"updatedAt":125,"category":126,"categories":127,"featuredImage":29,"bgImage":30,"posterImage":129,"relatedSolution":29},229,"docker-compose-in-production-10-point-checklist",{"fr":118,"en":116,"ar":119,"es":120},"docker-compose-production-checklist","docker-compose-في-الإنتاج-قائمة-التحقق-من-10-نقاط","checklist-docker-compose-en-produccion","Docker Compose in Production: 10-Point Checklist","Docker Compose production checklist: 10 essential settings, health checks, secrets without downtime, rollback strategy, common error troubleshooting, CVE-2026-17106.",12,"2026-08-06T00:00:00+00:00","2026-09-29T14:40:45+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":25},[128],{"id":23,"name":24,"slug":25,"color":26,"icon":25},"\u002Fblog\u002Fcovers\u002Fdocker-compose-production-checklist-poster.svg",{"id":131,"slug":132,"slugs":133,"title":137,"excerpt":138,"readTime":139,"views":18,"isPinned":19,"publishedAt":140,"updatedAt":141,"category":142,"categories":143,"featuredImage":29,"bgImage":30,"posterImage":145,"relatedSolution":29},282,"depends-on-is-not-enough-postgresql-healthcheck-in-compose",{"fr":134,"en":132,"ar":135,"es":136},"docker-compose-depends-on-healthcheck","depends-on-لا-يكفي-healthcheck-لـ-postgresql-في-compose","docker-compose-healthcheck-postgresql","depends_on is not enough: PostgreSQL healthcheck in Compose","Why `depends_on` alone does not guarantee PostgreSQL is ready, and how to configure a reliable healthcheck with `service_healthy` to avoid race conditions.",8,"2026-08-19T00:00:00+00:00","2026-09-07T11:26:10+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":25},[144],{"id":23,"name":24,"slug":25,"color":26,"icon":25},"\u002Fblog\u002Fcovers\u002Fdocker-compose-depends-on-healthcheck-poster.svg",{"id":147,"slug":148,"slugs":149,"title":153,"excerpt":154,"readTime":155,"views":18,"isPinned":19,"publishedAt":156,"updatedAt":157,"category":158,"categories":163,"featuredImage":29,"bgImage":30,"posterImage":165,"relatedSolution":29},382,"vps-automatic-security-updates-debian-ubuntu",{"fr":150,"en":148,"ar":151,"es":152},"securite-vps-mises-a-jour-automatiques-debian-ubuntu","تحديثات-أمان-تلقائية-vps-debian-ubuntu","actualizaciones-seguridad-vps-debian-ubuntu","Automatic Security Updates on Debian\u002FUbuntu VPS","Configure unattended-upgrades on your Debian\u002FUbuntu VPS to automate security patches and reduce attack surface across your client fleet.",10,"2026-09-26T00:00:00+00:00","2026-09-29T14:40:42+00:00",{"id":139,"name":159,"slug":160,"color":161,"icon":162},"Security & Monitoring","securite-monitoring","bg-rose-500\u002F10 text-rose-400","security",[164],{"id":139,"name":159,"slug":160,"color":161,"icon":162},"\u002Fblog\u002Fcovers\u002Fsecurite-vps-mises-a-jour-automatiques-debian-ubuntu-poster.svg",1791585712581]