Image Cooldown
The image cooldown feature enforces a configurable minimum image age before Watchtower will apply an update to a container. By requiring that an image has existed in the registry for a certain period of time, the cooldown provides a defense against supply-chain attacks, where a compromised image is published and immediately pulled by automated update tools. In addition, this helps to reduce the chance of receiving potentially buggy updates prior to the developer releasing an emergency patch.
Overview
When a cooldown is configured, Watchtower checks the creation timestamp of a new image before proceeding with a container update. If the image was published more recently than the configured cooldown period, the update is deferred until the image matures.
This check itself is lightweight, as Watchtower fetches only the image manifest and a small config blob from the registry (typically under 15 KB combined), without downloading any image layers. This also helps to avoid downloading a potentially malicious image.
When Watchtower cannot determine the image creation time, the update is always deferred.
This is a deliberate security decision. It is safer to miss an update than to pull an unverified image. A compromised image published moments ago would not yet have a reliable age signal, and treating the absence of data as “safe to update” would defeat the purpose of the cooldown feature.
**The cooldown delay applies to all updates, including security patches. **
A critical CVE fix published less than the cooldown duration ago will not be applied until the cooldown expires. Balance your cooldown duration against your risk tolerance and patch SLAs.
Configuration
Global Configuration
Set the cooldown delay for all monitored containers using the cooldown-delay configuration option.
services:
watchtower:
image: nickfedor/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_COOLDOWN_DELAY=24hPer-Container Label
Individual containers can override the global cooldown using the com.centurylinklabs.watchtower.cooldown-delay label.
When set, the label value takes precedence over the global configuration.
services:
nginx:
image: nginx
labels:
com.centurylinklabs.watchtower.cooldown-delay: "48h"
...Setting the label to "0" disables cooldown for that container, even when a global cooldown is configured.
LABEL com.centurylinklabs.watchtower.cooldown-delay="0"No-Pull Precedence
When the no-pull configuration option is enabled globally (or the com.centurylinklabs.watchtower.no-pull label is set on a container), Watchtower does not fetch new images from the registry.
Because no new images are pulled, the cooldown check is skipped entirely.
This takes precedence over any cooldown configuration — including per-container labels.
If you rely on cooldown as a supply-chain defense, ensure that no-pull mode is not inadvertently enabled.
Duration Format
Cooldown values are specified as a duration string. The following units are supported:
| Unit | Description | Example |
|---|---|---|
s |
Seconds | 90s |
m |
Minutes | 30m |
h |
Hours | 24h |
d |
Days | 3d |
w |
Weeks | 1w |
M |
Months | 1M |
Units can be combined:
24h— 24 hours3d12h— 3 days and 12 hours1w— 7 days1M— 30 days (approximate)
Examples
services:
watchtower:
image: nickfedor/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_COOLDOWN_DELAY=24h
# Inherits the global 24h cooldown
web:
image: nginx:latest
ports:
- "80:80"
# Override: requires image to be 48 hours old
api:
image: myorg/api:latest
ports:
- "8080:8080"
labels:
- "com.centurylinklabs.watchtower.cooldown-delay=48h"
# Override: cooldown disabled — update immediately
redis:
image: redis:latest
labels:
- "com.centurylinklabs.watchtower.cooldown-delay=0"How It Works
Image Age Determination
When Watchtower detects that a container’s running image differs from the latest available image, it retrieves the image creation timestamp from the registry before deciding whether to update. The process follows the OCI Distribution Spec API:
flowchart TD
A[Detect stale image] --> B[Fetch image manifest from registry]
B --> C{Multi-platform index?}
C -->|Yes| D[Select platform-specific manifest]
C -->|No| E[Extract config digest]
D --> E
E --> F[Fetch config blob from registry]
F --> G[Parse JSON — extract 'created' field]
G --> H[Calculate image age]
classDef step fill:#003343,stroke:#000,stroke-width:2px;
classDef decision fill:#003343,stroke:#000,stroke-width:2px;
class A,B,D,E,F,G,H step
class C decision
- Manifest fetch: Watchtower sends a
GETrequest to/v2/<image>/manifests/<tag>withAcceptheaders covering both OCI and Docker manifest formats. - Platform selection: For multi-platform images (manifest lists or OCI indexes), Watchtower selects the manifest matching the runtime OS and architecture. Cross-platform monitoring can be configured via
WATCHTOWER_COOLDOWN_PLATFORM_OSandWATCHTOWER_COOLDOWN_PLATFORM_ARCH. For ARM images with multiple variants (e.g.,v7,v8), specifyWATCHTOWER_COOLDOWN_PLATFORM_VARIANTto disambiguate. - Config blob fetch: The manifest’s config digest is used to fetch
/v2/<image>/blobs/<digest>. This is a small JSON document (typically <15 KB). - Timestamp extraction: The
createdfield from the config JSON is parsed as the image creation time.
No image layers are downloaded during this process.
Supported registries:
- Docker Hub (
docker.io) - GitHub Container Registry (
ghcr.io) - LinuxServer (
lscr.io— internally mapped toghcr.io) - Private registries implementing the OCI Distribution Spec
Update Decision
After determining the image age, Watchtower evaluates four outcomes:
| Outcome | Condition | Behavior |
|---|---|---|
| Proceeding | Image age > cooldown duration | The update proceeds normally |
| Proceeding | Image age is negative (clock skew) | The update proceeds with a warning to avoid indefinite deferral |
| Deferring | Image age ≤ cooldown duration | The update is skipped; the container remains on its current image |
| Deferring | Image age unavailable | The update is skipped for safety |
flowchart TD
A[Stale image detected] --> B{Cooldown configured?}
B -->|No| F[Proceed with update]
B -->|Yes| C[Fetch image creation time]
C --> D{Image age available?}
D -->|No| E[Defer update for safety]
D -->|Yes| G{Age strictly exceeds cooldown?}
G -->|Yes| F
G -->|No| I{Age is negative?}
I -->|Yes| F
I -->|No| H[Defer update — within cooldown]
classDef step fill:#003343,stroke:#000,stroke-width:2px;
classDef decision fill:#003343,stroke:#000,stroke-width:2px;
classDef defer fill:#8B0000,stroke:#000,stroke-width:2px;
class A,C,F step
class B,D,G,I decision
class E,H defer
Limitations
The cooldown feature relies on the created field from the image config blob. Several factors can affect the accuracy or availability of this data:
- Build timestamp, not push timestamp: The
createdfield records when the image was built, not when it was pushed to the registry. An image built days ago but only just tagged and pushed will appear old, potentially bypassing the intended cooldown window. - Manipulated timestamps: A compromised image could include a fabricated creation timestamp, making a freshly published malicious image appear mature. Cooldown is a defense-in-depth measure, not a guarantee of image integrity.
- Clock skew: If the Watchtower host and the registry have significantly different system clocks, age calculations may be inaccurate. NTP synchronization on all involved hosts is recommended to minimize this risk. When the image creation time is in the future (negative age), Watchtower logs a warning and proceeds with the update to avoid indefinite deferral.
- Missing
createdfield: Some registries or image build tools may not populate thecreatedfield. When the field is absent, Watchtower cannot determine the image age and defers the update as a safety measure (see the warning above).
Registry Usage Impact
The cooldown check issues GET requests to the registry for the image manifest and config blob.
These requests may count against container registry pull rate limits, which is most relevant for Docker Hub users.
Docker Hub
Official Documentation: https://docs.docker.com/docker-hub/usage/pulls/
Docker Hub enforces pull rate limits per six-hour window:
| Account Type | Pull Limit |
|---|---|
| Unauthenticated | 100 pulls per IP (IPv4) or /64 subnet (IPv6) |
| Personal (free, authenticated) | 200 pulls |
| Pro / Team / Business | Unlimited |
Not all registry requests count as pulls.
Verified behavior (tested against nickfedor/watchtower on Docker Hub):
| Request Type | Counts as Pull? |
|---|---|
HEAD manifest |
No |
GET manifest |
Yes |
GET config blob (307 → CDN) |
No |
| Auth token request | No |
The existing digest comparison uses a HEAD request, which does not consume a pull.
The cooldown age check uses GET requests for manifests, which do consume pulls, but the config blob fetch is served via a CDN redirect (307) and does not count against the limit.
Multi-Platform Images
The cooldown check consumes two pulls per stale container:
- One
GETfor the image index manifest - One
GETfor the platform-specific manifest
The subsequent config blob fetch does not count.
Single-Platform Images
The cooldown check consumes one pull per stale container:
- One
GETfor the image manifest
The subsequent config blob fetch does not count.
Practical Impact
Users running Watchtower against Docker Hub with many monitored containers or short polling intervals should be aware that each stale image detected during a cooldown window consumes one or two pulls. Authenticating with a Docker Hub account raises the limit from 100 to 200 pulls, and paid tiers have unlimited pulls.
GitHub Container Registry (ghcr.io)
GHCR.io does not publish explicit pull rate limits and currently provides free storage and bandwidth for container images. Observed limits are in the range of tens of thousands of requests per minute, which is well beyond what Watchtower’s cooldown feature would generate under any realistic deployment.
Per-Registry Impact Summary
| Registry | Pull Limit | Cooldown Impact |
|---|---|---|
| Docker Hub (unauthenticated) | 100 / 6 hours | Moderate — may exceed limit with many containers on short intervals |
| Docker Hub (authenticated free) | 200 / 6 hours | Low — sufficient for most deployments |
| Docker Hub (paid) | Unlimited | None |
| GHCR.io | ~44,000 / minute (observed) | None |
Monitor-Only Containers
Cooldown is not evaluated for containers running in monitor-only mode.
Since monitor-only containers are never updated, the cooldown check is skipped to avoid unnecessary registry API calls.
Rolling Restarts
Cooldown is evaluated per-container independently. In rolling restart scenarios, each container’s image age is checked separately. This means containers may update at different times even when using the same image, depending on when each container’s cooldown window expires.
Notifications
Watchtower reports the cooldown status in its notifications. The output varies depending on the outcome:
nickfedor/watchtower:latest created more than 24 hours ago - eligible for update