Watchlet keeps selected Docker Compose services up to date without handing a broad updater control over every container on the host.
If you run a homelab or small server with Compose stacks, you probably have a few services that should track their published image tags and a few services that should not be touched automatically. Watchlet gives you the boring middle path: label the services you want managed, point Watchlet at your Compose files, and let it pull, recreate, clean up, and log exactly what happened.
Watchlet reads one or more explicitly configured Docker Compose files and processes each file as its own Compose project.
For services labeled watchlet.enable=true, Watchlet:
- resolves the service image through Docker Compose
- records the current local image identity
- runs
docker compose pullfor that service - records the image identity again
- recreates the service when the identity changed
- removes only the old image IDs from services it successfully updated
Watchlet is intentionally narrow:
- Docker Compose files only.
- Explicit Compose paths only; no project discovery.
- Explicit opt-in only via
watchlet.enable=true. - Registry-backed
image:services are update candidates. - Services with
build:are skipped and logged, even if they also defineimage:. - Cleanup is targeted; Watchlet does not run broad
docker image prune. - Logs are plain key-value lines for easy Docker log inspection.
Run Watchlet as a small sidecar service with access to Docker and read-only access to the Compose projects you want it to manage:
services:
watchlet:
image: ghcr.io/mmarfil/watchlet:latest
container_name: watchlet
restart: unless-stopped
volumes:
# Required: lets Watchlet call Docker and Docker Compose on the host.
- /var/run/docker.sock:/var/run/docker.sock
# Mount each Compose project directory read-only.
# Left side: path on your host.
# Right side: path inside the Watchlet container.
- /Volumes/Docker/Media:/media:ro
- /Volumes/Docker/System:/system:ro
environment:
# Container-side paths to the Compose files mounted above.
# Separate multiple Compose files with commas.
WATCHLET_COMPOSE: /media/docker-compose.yml,/system/docker-compose.yml
WATCHLET_INTERVAL: 24h
labels:
# Optional: lets Watchlet update its own image too.
- watchlet.enable=trueThe published image includes the watchlet binary plus Docker CLI and the Docker Compose plugin, because Watchlet performs updates by calling docker compose against the mounted Docker socket.
The watchlet.enable=true label on the Watchlet service lets Watchlet update itself. Watchlet handles its own service last, after the rest of the pass is finished, because recreating itself restarts the updater. If you prefer to update Watchlet manually, omit that label and run docker compose pull watchlet && docker compose up -d watchlet when you want a new version.
Then opt in individual services inside those Compose files:
services:
app:
image: ghcr.io/example/app:latest
labels:
watchlet.enable: "true"List-style labels also work:
services:
app:
image: ghcr.io/example/app:latest
labels:
- watchlet.enable=trueLocal builds are not supported. Services that define build: are skipped and reported, even when they also define image:.
Watchlet supports environment variables for container use and CLI flags for local/manual runs. CLI flags override environment variables.
| Setting | CLI | Environment | Default |
|---|---|---|---|
| Compose files | --compose PATH repeated |
WATCHLET_COMPOSE=/path/a.yml,/path/b.yml |
required |
| Interval | --interval DURATION |
WATCHLET_INTERVAL=24h |
24h |
| One-shot mode | --once |
none | interval mode |
| Force recreate | --force with --once |
none | disabled |
Durations use Go duration syntax: 24h, 1h, 30m, and so on.
WATCHLET_COMPOSE is a comma-separated list. --compose may be repeated:
watchlet --once \
--compose /media/docker-compose.yml \
--compose /system/docker-compose.ymlRun one update pass and exit:
watchlet --once --compose /media/docker-compose.ymlRun one forced pass. Force mode still respects labels and skips build: services, but recreates selected image: services after pulling even when the image identity is unchanged:
watchlet --once --force --compose /media/docker-compose.ymlRun continuously on the default 24-hour interval:
watchlet --compose /media/docker-compose.ymlRun continuously with environment configuration:
WATCHLET_COMPOSE=/media/docker-compose.yml,/system/docker-compose.yml \
WATCHLET_INTERVAL=24h \
watchletWatchlet passes each configured Compose file to Docker Compose with that file’s directory as the project directory. For example, /media/docker-compose.yml runs with /media as the Compose project directory. This keeps stack-local .env files and Compose interpolation aligned with normal Compose behavior.
For image identity checks, Watchlet resolves the effective image reference with Docker Compose config output before running docker image inspect. It does not parse docker compose pull prose to decide whether an update occurred.
If one Compose file or service fails, Watchlet logs the failure, continues with the remaining configured Compose files, and reports a failed final summary for that pass.
Watchlet emits key-value logs such as:
level=INFO msg=watchlet action=service-selected compose=/media/docker-compose.yml service=app image=ghcr.io/example/app:latest
level=INFO msg=watchlet action=action-result operation=pull compose=/media/docker-compose.yml service=app status=ok
level=INFO msg=watchlet action=cleanup compose=/media/docker-compose.yml service=app image_id=sha256:old status=ok
Read logs with your normal container log tooling, for example docker logs watchlet or docker compose logs watchlet. Command failures include the failing layer, compose path, service when applicable, and Docker/Compose diagnostics when available, including captured stdout/stderr from Docker or Compose.
Watchlet is open source so you can read it, trust it, copy it, and run it without mystery.
It is not set up as a contribution-driven project. That is on purpose, and not meant to be unfriendly. The whole point is to keep Watchlet dumb, small, and easy to reason about. More features, more knobs, and more edge cases would quickly turn it into the kind of tool this project is trying not to be.
If Watchlet is almost what you want but not quite, please fork it and make it your own. Add notifications, dashboards, policies, plugins, a mascot, whatever makes your setup happy. This version is just going to stay boring.
Watchlet is not an orchestrator, dashboard, notification system, registry client, or local build system. It is a small updater for explicitly selected Docker Compose image: services.