DB-유

Updating Docker Containers on Synology

· synology, docker, maintenance

I found a nextcloud:latest container on my NAS that I'd pulled over a year earlier and never touched. Containers don't update themselves; the image stays the same, vulnerabilities included, until you fetch a newer one and recreate the container. The Synology Docker GUI on DSM 6.2 makes it confusing, because "Download" gets a new image but leaves the running container on the old layers. Here is the update routine, in the GUI and with docker-compose, which is what I use for anything that matters.

One rule before you touch anything: the data must live in bind-mounted folders under /volume1/docker. If it doesn't, fix that first, since recreating a container throws away its internal filesystem. You also need SSH for the compose method.

Know what you are running

sudo docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
sudo docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}}'

Images tagged latest are the ones you cannot reason about; if you can, pin to a version tag like nextcloud:18.0.3-apache and update the tag deliberately.

The GUI way

For a container you created in the GUI:

  1. Docker → Registry → search the image → Download → pick the same tag. This pulls the new layers; the old image is kept.
  2. Docker → Container → select the container → Action → Stop.
  3. Action → Clear (on some builds called Reset). This deletes the container's writable layer but keeps the settings and volume mappings.
  4. Action → Start. The container is recreated from the newly downloaded image with the stored configuration.

Note that Clear also wipes anything that was inside the container and not mounted from the NAS, which is exactly why the bind-mount rule above matters. Afterwards, Docker → Image will show the old image as unused; delete it there to free space.

The docker-compose way

For projects with a docker-compose.yml the whole cycle is three commands:

cd /volume1/docker/nextcloud
sudo docker-compose pull
sudo docker-compose up -d
sudo docker image prune -f

pull fetches new versions of every image referenced in the file; up -d recreates only the containers whose image or configuration changed and leaves the others running; prune removes the now-dangling old images. Before pulling, edit the version tags in the compose file if you pinned them:

services:
  app:
    image: nextcloud:18.0.3-apache
  db:
    image: mariadb:10.4

Check the application's release notes for anything that needs manual action, such as a database migration on the first start.

Apps with their own upgrade step

Some applications update their database schema on start and refuse to run until you confirm it:

sudo docker exec -u www-data nextcloud-app php occ upgrade
sudo docker exec -u www-data nextcloud-app php occ maintenance:mode --off

Gitea, Home Assistant and most others do it automatically; watch the logs after each update:

sudo docker-compose logs --tail 50 -f app

Automating the boring parts

I don't automate the recreate step for anything with a database. I do let a scheduled task pull images and tell me what changed. Task Scheduler → weekly, root:

#!/bin/bash
cd /volume1/docker
for d in */; do
  [ -f "$d/docker-compose.yml" ] || continue
  echo "== $d"
  (cd "$d" && /usr/local/bin/docker-compose pull -q 2>&1)
done
/usr/local/bin/docker images --filter dangling=true -q | wc -l | sed 's/^/dangling images: /'

With "Send run details by email" enabled I get a weekly summary and then run up -d by hand when convenient. For stateless containers I run Watchtower, which polls for new images and recreates containers automatically:

sudo docker run -d --name watchtower --restart unless-stopped \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower --schedule "0 0 4 * * *" --cleanup \
  nginx-static unifi-poller

Listing container names at the end restricts it to those; without names it would update everything, including databases, which I do not want.

Rolling back

Old images are only gone after you prune. If a new version misbehaves, set the previous tag in the compose file and up -d again; the data folder is untouched unless the application migrated its schema, in which case restore the data folder from the snapshot you took before updating. Snapshot Replication on the docker share makes this a one-minute operation.

Did it really update?

sudo docker inspect --format '{{.Image}}' nextcloud-app
sudo docker images --no-trunc --format '{{.ID}} {{.Repository}}:{{.Tag}}' | grep nextcloud

The container's image ID must match the newly pulled image, not the old one. Then open the application and check its About page for the version, and confirm your data is present.

Common snags

  • After Download in the GUI the container still runs the old version: you skipped Clear. Downloading an image never touches an existing container.
  • docker-compose up -d says "ERROR: for app Cannot start service app: driver failed programming external connectivity ... Bind for 0.0.0.0:8080 failed: port is already allocated": another container or a DSM service took the port while the old one was stopped. docker ps -a and netstat -tlnp | grep 8080 will show who.
  • up -d recreates a container you did not change: its environment or labels in the compose file differ from the running one (often because of a value the GUI edited). That is harmless, but it means the GUI and compose disagree; treat compose as the source of truth.
  • "no space left on device" during pull: dangling images and stopped containers have filled the volume. docker system df shows the usage; docker image prune -a removes all unused images.

Fetch, recreate, verify. Once the data is outside the container it's a boring job, which is how I want it.