Git Monitoring¶
Watchtower can treat a hosted Git branch or tag as the signal that a container image is stale. When that ref advances, Watchtower asks the Docker host to rebuild the image from a Compose build context, then uses the new image in the existing container update process.
Configuration options for this feature are listed in Configuration → Git Monitoring.
Two choices decide the rest of the setup:
- Associate the container and turn the watcher on. A repository label by itself does not enable updates.
- Choose a build context. A Git URL context lets the Docker daemon clone and build. A local path context checks out a Compose project mounted into Watchtower.
Follow Walkthrough for the two setups most people use. If an update does not happen, use Common mistakes before changing flags.
Overview¶
What Git Monitoring Does¶
Git monitoring is an alternate staleness signal. It does not replace registry digest checks for containers that are not associated with a repository. The Watchtower container itself always stays on the registry self-update path.
| Stage | What Watchtower does |
|---|---|
| Git monitoring | Talks to the hosted remote and compares the configured branch or tag to the known running revision |
| Image build | Asks the Docker host, or Compose, to rebuild from the chosen build context |
| Container update | Recreates the container from inspect config, or applies Compose for a local path context |
When a Container Uses the Git Path¶
Both of the following must be true:
- The container is associated with a repository, using
com.centurylinklabs.watchtower.git-repoor the Git Image configuration option. - The Git watcher is on, using the Git Enable configuration option or
com.centurylinklabs.watchtower.git-watch.
com.centurylinklabs.watchtower.git-repo does not replace the Label Enable configuration option.
What Git Monitoring Does Not Do¶
| It does not | Use this instead |
|---|---|
| Watch a local working tree for file saves | Compose Watch |
| Rebuild because a stamp label is missing | Wait until the hosted ref advances |
| Invent Compose or run configuration | Put that configuration on the container or in the Compose project |
Treat org.opencontainers.image.source as an association |
Set com.centurylinklabs.watchtower.git-repo or Git Image |
See Local filesystem monitoring.
Walkthrough¶
Most setups are one of these two. Follow the one that matches how the image is built.
- One application, with the Dockerfile in a hosted repository: A single service built from Git.
- Several services built from a Compose project on disk, plus containers that should stay on the registry: A Compose project on disk.
Starting Watchtower does not rebuild the application. An update session does, and only after the hosted ref moves past the revision Watchtower already knows.
You need the Docker socket mounted into Watchtower. A private repository needs a read token in a Docker Secret. Do not put the token in the compose file, and do not put the raw token in the process environment.
A single service built from Git¶
The Docker daemon clones the repository when main moves, builds the image, and Watchtower recreates the container. This is a Git URL context.
-
Confirm the repository has a
Dockerfileat its root. If the Dockerfile is somewhere else, and itsCOPYinstructions only use files from inside its own directory, setcom.centurylinklabs.watchtower.git-dockerfileto that path. Leave Git Context unset whenCOPYneeds files outside that directory. See Nested Dockerfiles. -
Use an
https://clone URL. A private repository cannot usegit@host:pathfor this build. The daemon does not receive Watchtower's SSH key. A public repository can skip the token in the next step. -
For a private repository, create a read token and store it in a file. The scope depends on the server.
Server Clone URL Token scope GitHub https://github.com/org/app.gitRepository contents: Read GitLab https://gitlab.com/group/app.gitread_repository. Nested groups belong in the URLCodeberg https://codeberg.org/org/app.gitRepository read Gitea or Forgejo you host https://git.example.com/org/app.gitRepository read -
Associate the container and turn the watcher on.
git-reponames the repository. It does not enable updates.WATCHTOWER_GIT_ENABLEturns the watcher on for every associated container.github.com,gitlab.com, andcodeberg.orgdo not needcom.centurylinklabs.watchtower.git-host.docker-compose.ymlservices: watchtower: image: nickfedor/watchtower volumes: - /var/run/docker.sock:/var/run/docker.sock environment: WATCHTOWER_GIT_ENABLE: "true" restart: unless-stopped app: image: myapp:latest labels: com.centurylinklabs.watchtower.git-repo: "https://github.com/org/app.git" com.centurylinklabs.watchtower.git-ref: "main"docker-compose.ymlsecrets: git_auth_token: file: ./secrets/git_auth_token.txt services: watchtower: image: nickfedor/watchtower volumes: - /var/run/docker.sock:/var/run/docker.sock secrets: - git_auth_token environment: WATCHTOWER_GIT_ENABLE: "true" WATCHTOWER_GIT_AUTH_TOKEN: /run/secrets/git_auth_token restart: unless-stopped app: image: myapp:latest labels: com.centurylinklabs.watchtower.git-repo: "https://github.com/org/app.git" com.centurylinklabs.watchtower.git-ref: "main"Start app and Watchtowerdocker run -d \ --name app \ --label com.centurylinklabs.watchtower.git-repo="https://github.com/org/app.git" \ --label com.centurylinklabs.watchtower.git-ref="main" \ myapp:latest docker run -d \ --name watchtower \ -v /var/run/docker.sock:/var/run/docker.sock \ --restart unless-stopped \ -e WATCHTOWER_GIT_ENABLE=true \ nickfedor/watchtowerStart app and Watchtowerdocker run -d \ --name app \ --label com.centurylinklabs.watchtower.git-repo="https://github.com/org/app.git" \ --label com.centurylinklabs.watchtower.git-ref="main" \ myapp:latest docker run -d \ --name watchtower \ -v /var/run/docker.sock:/var/run/docker.sock \ -v "$(pwd)/secrets/git_auth_token.txt:/run/secrets/git_auth_token:ro" \ --restart unless-stopped \ -e WATCHTOWER_GIT_ENABLE=true \ -e WATCHTOWER_GIT_AUTH_TOKEN=/run/secrets/git_auth_token \ nickfedor/watchtowerReplace
https://github.com/org/app.gitwith the clone URL from the table. A self-hosted API on another port or path also needs Hosts.[email protected]:org/app.gitmay usehttps://git.example.com:3000. The hostname must match. A different hostname is ignored, and the token is not sent there. -
Wait for the next update session. Process start is not a session. The first session records what is already running and does not rebuild
app. -
Push a commit to
main. On the next session the daemon buildshttps://github.com/org/app.git#<commit>, Watchtower recreatesapp, and the replacement receives the stamp.
To follow release tags instead of main, set com.centurylinklabs.watchtower.git-ref to the tag that is running now, for example v1.2.0, and set com.centurylinklabs.watchtower.git-semver-policy to patch, minor, or major.
A pre-release such as v1.2.4-rc.1 is not selected.
Run Once has no memory of the previous tip, so bake org.opencontainers.image.revision into the image, and org.opencontainers.image.version when you use a tag policy.
A Compose project on disk¶
Use this when api and worker are built from a Compose file on disk, and db should keep using the registry. Watchtower checks out the repository in a directory mounted into Watchtower, then applies Compose for the stale services. This is a local path context.
-
Keep
apiandworkeron the same ref and the same semver policy. One project directory is checked out once. If the two services resolve different commits, Watchtower leaves the project untouched.dbhas no Git labels, so it stays on the registry path. -
Choose the path where the project will live inside Watchtower, and bind-mount the host directory there. In the example the path is
/srv/webstack. The Compose project name iswebstack. Those two strings meet inWATCHTOWER_COMPOSE_PROJECT.com.docker.compose.project.working_diris not this opt-in. Watchtower does not check out the host path Compose recorded when the container was created. -
For a private HTTPS origin, create the same secret file as in the single-service walkthrough and give it only to Watchtower. An SSH origin uses the key and
known_hostsfile from Authentication. That key fetches into the mounted directory. It is not used to build a Git URL context. -
Start the stack.
docker-compose.ymlname: webstack services: api: build: ./api labels: com.centurylinklabs.watchtower.git-repo: "https://github.com/org/webstack.git" com.centurylinklabs.watchtower.git-ref: "main" worker: build: ./worker labels: com.centurylinklabs.watchtower.git-repo: "https://github.com/org/webstack.git" com.centurylinklabs.watchtower.git-ref: "main" db: image: postgres:16 watchtower: image: nickfedor/watchtower volumes: - /var/run/docker.sock:/var/run/docker.sock - /srv/webstack:/srv/webstack environment: WATCHTOWER_GIT_ENABLE: "true" WATCHTOWER_COMPOSE_PROJECT: | webstack=/srv/webstack restart: unless-stoppeddocker-compose.ymlname: webstack secrets: git_auth_token: file: ./secrets/git_auth_token.txt services: api: build: ./api labels: com.centurylinklabs.watchtower.git-repo: "https://github.com/org/webstack.git" com.centurylinklabs.watchtower.git-ref: "main" worker: build: ./worker labels: com.centurylinklabs.watchtower.git-repo: "https://github.com/org/webstack.git" com.centurylinklabs.watchtower.git-ref: "main" db: image: postgres:16 watchtower: image: nickfedor/watchtower volumes: - /var/run/docker.sock:/var/run/docker.sock - /srv/webstack:/srv/webstack secrets: - git_auth_token environment: WATCHTOWER_GIT_ENABLE: "true" WATCHTOWER_GIT_AUTH_TOKEN: /run/secrets/git_auth_token WATCHTOWER_COMPOSE_PROJECT: | webstack=/srv/webstack restart: unless-stoppedThe application containers are already running from Compose, with the Git labels above. This command is only Watchtower.
Start Watchtower against the mounted projectdocker run -d \ --name watchtower \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /srv/webstack:/srv/webstack \ -v "$(pwd)/secrets/git_auth_token.txt:/run/secrets/git_auth_token:ro" \ --restart unless-stopped \ -e WATCHTOWER_GIT_ENABLE=true \ -e WATCHTOWER_GIT_AUTH_TOKEN=/run/secrets/git_auth_token \ -e WATCHTOWER_COMPOSE_PROJECT="webstack=/srv/webstack" \ nickfedor/watchtowerEach applied service must keep
com.docker.compose.service. Compose writes that label. Without it, Watchtower cannot name the service and leaves the container untouched. You can setcom.centurylinklabs.watchtower.compose-dir=/srv/webstackonapiandworkerinstead ofWATCHTOWER_COMPOSE_PROJECT. -
Wait for the next update session. The first session records
apiandworkerand does not checkout or apply Compose. -
Push a commit to
main. On the next session Watchtower fetches that commit in/srv/webstack, applies Compose forapiandworker, and writes the stamp.dbis not part of that apply.
If the mounted project has local files that are not in Git, such as .env, enable Git Compose Stash or the checkout stops and the running containers stay as they are.
How It Works¶
Association Versus the Watcher¶
Association and the watcher are independent.
com.centurylinklabs.watchtower.git-watch=true without com.centurylinklabs.watchtower.git-repo or a matching Git Image mapping is not associated.
Watchtower stays on the registry path and logs at debug.
Watcher Precedence¶
com.centurylinklabs.watchtower.git-watch |
Git Enable | Watcher |
|---|---|---|
| absent | false |
off |
| absent | true |
on |
| enabling value | either | on |
| disabling value | either | off |
Accepted git-watch Values¶
Enabling values are true, yes, 1, and t.
Disabling values are false, no, 0, and f.
Those are the same loose booleans as other Watchtower labels.
How Watchtower Chooses a Running Revision¶
Watchtower compares the hosted tip to the first known running revision it can find, in this order:
- Stamp labels on the container (
com.centurylinklabs.watchtower.git-last-commit,com.centurylinklabs.watchtower.git-last-tag) - Image identity (
org.opencontainers.image.revision, a semverorg.opencontainers.image.version, or aname:git-<shortsha>tag) - The remote tip remembered earlier in this Watchtower process
flowchart TD
A[Hosted Git tip] --> B{Stamp labels?}
B -->|Yes| C[Compare to stamp]
B -->|No| D{Image revision or git- tag?}
D -->|Yes| E[Compare to image identity]
D -->|No| F{Remembered tip in this process?}
F -->|Yes| G[Compare to remembered tip]
F -->|No| H[Record tip. Leave container untouched]
C --> I{Remote has advanced?}
E --> I
G --> I
I -->|No| J[Leave container untouched]
I -->|Yes| K[Rebuild from the chosen build context]
classDef step fill:#003343,stroke:#000,stroke-width:2px
classDef decision fill:#003343,stroke:#000,stroke-width:2px
class A,C,E,G,H,J,K step
class B,D,F,I decision
Missing Stamps¶
A missing stamp is not stale. Watchtower does not rebuild only to write labels.
The Run Once configuration option has no process memory
Each invocation is a new process.
Without stamp labels or image identity, Watchtower cannot see that Git moved while it was not running.
Bake org.opencontainers.image.revision into the image, and org.opencontainers.image.version for a tag policy, if you use Run Once.
Update Session¶
No-pull skips Git rebuilds for that container.
/v1/check reports no update and does not contact the Git remote.
Git work runs only during an update session.
A session starts from the Schedule or Interval configuration option, from the Run Once configuration option, or from HTTP /v1/update.
Process start is not a session.
flowchart TD
A[Update session starts] --> B{Associated and watcher on?}
B -->|No| C[Registry digest path]
B -->|Yes| D[Resolve hosted branch or tag tip]
D --> E[Compare to known running revision]
E --> F{Remote has advanced?}
F -->|No| G[Leave running container untouched]
F -->|Yes| H{Build context}
H -->|Git URL| I[Docker daemon ImageBuild]
H -->|Local path| J[Fetch, checkout, Compose apply]
I --> K[Inspect-recreate and write stamps]
J --> L[Compose recreates services and writes stamps]
classDef step fill:#003343,stroke:#000,stroke-width:2px
classDef decision fill:#003343,stroke:#000,stroke-width:2px
class A,C,D,E,G,I,J,K,L step
class B,F,H decision
What Happens in a Session¶
For each associated container whose watcher is on:
- Resolve the repository URL, ref, and semver policy from labels or the Git Image configuration option.
- Check the hosted remote for the current branch tip or a policy-allowed tag.
- Compare that tip to the known running revision.
- If the remote has not advanced, leave the running container untouched.
- If the remote has advanced, rebuild from the chosen build context.
- After a successful rebuild, write stamp labels on the replacement.
Annotated tags are resolved to the peeled commit, not the tag object.
Notifications¶
A custom notification template can print .Changelog, .GitRepo, .GitRef, .Source, .CurrentImageVersion, .LatestImageVersion, .CurrentImageRevision, and .LatestImageRevision for each updated container.
.GitRef is the configured Git ref and nothing else.
A semver-shaped OCI org.opencontainers.image.version is never copied into it.
A Git rebuild has no pulled image, so .LatestImageVersion and .LatestImageRevision stay empty and the new version comes from the resolved Git tag through the changelog {tag} placeholder.
A container Watchtower rebuilds from Git needs no release tag lookup, so enabling --enable-changelog costs such a container nothing.
A container that only carries Git labels, but is still updated from its registry image, needs the lookup like any other registry update.
The built-in report template adds the ref and the changelog to an updated container when they are set.
With report mode off, a Git update sends Found new Git revision, then Built image for a Git URL build, then the same stop and start lines as a registry update. Compose sends the found line once, then those stop and start lines for each recreated container. It does not send a built-image line. The identity is host/path@tag or host/path@branch, with a 12-character commit. The log still records the full commit.
Failed Builds¶
For a Git URL build, the host build finishes before the running container is replaced. If the build fails, the running container is left untouched. Docker Compose can partially recreate services before reporting an apply error. Watchtower does not accept the commit written onto those containers, so a later session in the same process retries from the previous baseline, and continues with other projects and containers. That rejection is held only in memory for the current Watchtower process. A restart, self-update, or separate Run Once invocation may read the new container stamp and therefore will not retry from the previous baseline.
Build Context¶
Two Context Kinds¶
Monitoring is the same for both kinds. Watchtower checks a hosted Git ref. You choose how the image is built.
| Build context | How you select it | Image build | Container update |
|---|---|---|---|
| Git URL context | Git labels only | Docker daemon clones https://host/repo.git#commit and builds |
Recreate from the running inspect config |
| Local path context | compose-dir on the service, or the Compose Project configuration option |
Checkout of the Compose project directory, then Compose apply | Compose recreates those services. Watchtower does not inspect-recreate them. |
How You Select a Context¶
- Set
com.centurylinklabs.watchtower.compose-dir, or map the Compose project with the Compose Project configuration option, to use a local path context. - Omit those to use a Git URL context.
com.docker.compose.project.working_dir is not an opt-in
That label is a host path Compose recorded at create time. Watchtower does not treat it as permission to checkout or apply Compose.
A missing local path does not fall back to a Git URL context
If you set compose-dir or the Compose Project configuration option and the path is missing or has no compose file, that container is left untouched.
Git URL Context¶
When to Use It¶
Use this when the image should be built from a hosted Git URL, the same as Compose build.context: https://host/repo.git.
Typical setup: a single service whose image tracks https://git.example.com/org/app.git on main or a tag policy.
The Dockerfile lives in that repository.
A Git URL context does not read compose.yaml.
New env vars, services, or volumes added only in Compose are not applied.
Use a local path context when the compose file is the source of truth.
What the Daemon Builds¶
On a stale session:
- Watchtower builds a Docker Git URL context:
https://git.example.com/org/app.git#<commit>, or#<commit>:<subdir>whencom.centurylinklabs.watchtower.git-contextor the Git Context configuration option is set. - The Docker daemon (BuildKit) performs a shallow clone of that commit. Watchtower does not clone the tree first.
- The Dockerfile is
com.centurylinklabs.watchtower.git-dockerfile, or the Git Dockerfile configuration option, defaulting toDockerfile. - The image is tagged
name:git-<shortsha>(12 hex characters) and the original image name is retagged. - Watchtower recreates the container from the running inspect config.
- Stamp labels are written onto the new container.
Private Repositories¶
Private Git URL builds require HTTPS
The Docker daemon's Git URL context cannot use the Watchtower SSH key. Use HTTPS with the Git Auth Token configuration option, or the Git Username and Git Password configuration options.
Nested Dockerfiles¶
Keep the context at the repository root when the Dockerfile COPYs files from outside its own directory.
Set com.centurylinklabs.watchtower.git-dockerfile to build/docker/Dockerfile.
Set com.centurylinklabs.watchtower.git-context, or the Git Context configuration option, only when the daemon should receive a subdirectory as the context.
Example¶
docker run -d \
--name app \
--label com.centurylinklabs.watchtower.git-repo="https://git.example.com/org/app.git" \
--label com.centurylinklabs.watchtower.git-ref="main" \
--label com.centurylinklabs.watchtower.git-dockerfile="build/docker/Dockerfile" \
myapp:latest
Local Path Context¶
When to Use It¶
Use this when the running services were created from a Compose file that uses a local build.context (build: ./api), and you mount that Compose project directory into Watchtower.
Typical setup: api and worker with build: ./api or build: ./worker, plus image-only dependencies that stay on the registry path.
A Compose build: key alone does not associate a container.
The container still needs a Git association and the watcher to be on.
How You Opt In¶
Use one of:
com.centurylinklabs.watchtower.compose-diron the service (path inside Watchtower).- The Compose Project configuration option (
name=/pathon Watchtower, keyed bycom.docker.compose.project).
Bind-mount the project directory into Watchtower at that path.
Each applied container must have com.docker.compose.service.
Without that label Watchtower cannot target a service and leaves the running container untouched.
What Watchtower Applies¶
On a stale session:
- Group stale, associated containers that share the same resolved project directory.
- Fetch the hosted remote in that directory once per project.
- Check out the monitored commit.
- Apply Compose for the stale associated service names only.
- Write stamp and association labels onto the new containers.
- Do not also recreate those containers through the inspect path.
Every stale service in that directory must resolve the same commit. One worktree cannot be checked out at two commits. When the commits differ, Watchtower leaves the project untouched and retries on a later session.
Compose uses each service's build.context relative to the project directory.
Image-only services in the same file are not rebuilt from Git.
Fetch uses the same Git Auth Token, Git Password, Git SSH Key Path, Git CA Bundle, and Git Insecure Skip TLS configuration options as monitoring. If fetch fails but the monitored commit is already in the local object store, checkout continues.
Dirty Worktrees¶
If the worktree is dirty and the Git Compose Stash configuration option is off, Watchtower aborts that project and leaves the running containers untouched.
If that configuration option is on, Watchtower saves local files (for example an untracked .env), checks out the monitored commit, then writes those files back.
Compose Interpolation¶
Compose interpolates the project's .env file.
Watchtower's process environment is not interpolated into compose.yaml.
Enable the Git Compose Stash configuration option if that .env is local and not committed.
The Disable Container Restart configuration option checks out the commit and runs Compose build only. It does not recreate running services.
Example¶
name: webstack
services:
api:
build: ./api
labels:
com.centurylinklabs.watchtower.git-repo: "https://git.example.com/org/webstack.git"
com.centurylinklabs.watchtower.git-ref: "v1.2.0"
com.centurylinklabs.watchtower.git-semver-policy: "minor"
worker:
build: ./worker
labels:
com.centurylinklabs.watchtower.git-repo: "https://git.example.com/org/webstack.git"
com.centurylinklabs.watchtower.git-ref: "v1.2.0"
com.centurylinklabs.watchtower.git-semver-policy: "minor"
db:
image: postgres:16
watchtower:
image: nickfedor/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /srv/webstack:/srv/webstack
environment:
WATCHTOWER_GIT_ENABLE: "true"
WATCHTOWER_COMPOSE_PROJECT: |
webstack=/srv/webstack
api and worker track the hosted repo.
db stays on the registry path.
/srv/webstack is the path inside the Watchtower container.
The host directory must be bind-mounted there, and webstack must match the Compose project name.
Local Filesystem Monitoring¶
Watchtower does not monitor a local Git working tree for file saves.
That workflow is Compose Watch, configured with develop.watch and run with docker compose up --watch.
Configuring a Container¶
Enabling Git Monitoring¶
Process-Wide Default¶
Turn the process-wide default on with the Git Enable configuration option.
Per-Container Override¶
Override that default with com.centurylinklabs.watchtower.git-watch.
What Is Written After a Rebuild¶
git-watch=true is written only when the source container already had an explicit enabling watch label.
When the watcher was on only because of the Git Enable configuration option, the replacement has no git-watch label.
Setting Git Enable to false later still applies.
Where Settings Come From¶
Watchtower reads the repository URL, ref, and semver policy from the container's Watchtower labels when com.centurylinklabs.watchtower.git-repo is set.
If that label is absent, Watchtower uses the Git Image mapping whose key matches the container's exact ImageName().
There is no process-wide default repository.
Use this when the Git settings belong on the service itself.
Watchtower uses https://github.com/org/app.git on main with a minor semver policy.
Use the Git Image configuration option when you do not want Git labels on every service.
The mapping key must match ImageName() including the tag (app is not app:latest).
The mapping does not turn the watcher on.
docker run -d \
--name app \
myapp:latest
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
--restart unless-stopped \
-e WATCHTOWER_GIT_ENABLE=true \
-e WATCHTOWER_GIT_IMAGE="myapp:latest=https://github.com/org/app.git#main@minor" \
nickfedor/watchtower
app has no Git labels.
Watchtower uses the mapping for myapp:latest: https://github.com/org/app.git on main with a minor semver policy.
When both are present, the container labels are used. The Git Image mapping is ignored for that container.
services:
watchtower:
image: nickfedor/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
WATCHTOWER_GIT_ENABLE: "true"
WATCHTOWER_GIT_IMAGE: |
myapp:latest=https://github.com/org/app.git#develop@major
app:
image: myapp:latest
labels:
com.centurylinklabs.watchtower.git-repo: "https://github.com/org/app.git"
com.centurylinklabs.watchtower.git-ref: "main"
com.centurylinklabs.watchtower.git-semver-policy: "patch"
docker run -d \
--name app \
--label com.centurylinklabs.watchtower.git-repo="https://github.com/org/app.git" \
--label com.centurylinklabs.watchtower.git-ref="main" \
--label com.centurylinklabs.watchtower.git-semver-policy="patch" \
myapp:latest
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
--restart unless-stopped \
-e WATCHTOWER_GIT_ENABLE=true \
-e WATCHTOWER_GIT_IMAGE="myapp:latest=https://github.com/org/app.git#develop@major" \
nickfedor/watchtower
Watchtower uses main and patch from the labels, not develop and major from the mapping.
Defaults¶
| Field | Default when unset |
|---|---|
com.centurylinklabs.watchtower.git-ref |
main |
com.centurylinklabs.watchtower.git-semver-policy |
none (follow the configured ref) |
Invalid Semver Policy¶
An explicit com.centurylinklabs.watchtower.git-semver-policy that is not none, patch, minor, or major is rejected.
That container is skipped for the session.
patch, minor, and major consider release tags only.
A pre-release such as v1.2.4-rc.1 is ignored.
Build metadata (v1.2.3+sha) does not change the comparison.
A pre-release baseline still advances to the later release (v1.2.3-rc.1 to v1.2.3).
Per-Image Mapping¶
The Git Image configuration option uses image=repo[#ref][@policy].
Repeatable values are newline-separated.
| Part | When omitted |
|---|---|
#ref |
main |
@policy |
none |
Labels¶
Association Labels¶
| Label | Values | Effect |
|---|---|---|
com.centurylinklabs.watchtower.git-repo |
Git clone URL | Associates the container with a repository |
com.centurylinklabs.watchtower.git-ref |
branch or tag | Ref to watch. Defaults to main |
com.centurylinklabs.watchtower.git-host |
HTTP API base URL | HTTP API origin when it is not the clone URL. See Hosts |
com.centurylinklabs.watchtower.git-branch |
branch or tag | Alias for git-ref |
com.centurylinklabs.watchtower.git-semver-policy |
none, patch, minor, or major |
Semver tag advancement. Defaults to none |
com.centurylinklabs.watchtower.git-watch |
loose boolean | Overrides the Git Enable configuration option |
com.centurylinklabs.watchtower.git-dockerfile |
path relative to the build context | Dockerfile for a Git URL context. Defaults to Dockerfile, or the Git Dockerfile configuration option |
com.centurylinklabs.watchtower.git-context |
subdirectory of the Git repository | Git URL context subdirectory (#commit:subdir). Defaults to the Git Context configuration option |
com.centurylinklabs.watchtower.compose-dir |
path inside Watchtower | Opts this service into a local path context |
com.centurylinklabs.watchtower.changelog-url |
URL template | Optional notification changelog URL. Does not enable --enable-changelog |
com.centurylinklabs.watchtower.enable-changelog |
loose boolean | Enables the changelog notification line and the versioned release link for this container |
Stamp Labels¶
| Label | Values | Effect |
|---|---|---|
com.centurylinklabs.watchtower.git-last-commit |
commit SHA | Written after a Git rebuild |
com.centurylinklabs.watchtower.git-last-tag |
tag name | Written after a tag rebuild |
Do not set stamp labels by hand
They are a cache of the running revision, not a reason to update. When a later apply omits an optional field, for example no tag or no dockerfile, Watchtower removes that label from the replacement container.
Authentication¶
HTTPS and HTTP¶
Use a token or a username and password when the clone URL starts with https:// or http://.
Watchtower tries the configured credentials in this order and stops at the first match:
- Git Auth Token
- Git Username together with Git Password
- No credentials. The remote must allow anonymous reads.
A Git URL context build is performed by the Docker daemon, not by Watchtower.
The daemon is given those credentials inside an https:// clone URL.
It is not given an SSH key.
A private repository on this path needs an https:// URL and either a token or a username and password.
A plain http:// URL is not given those credentials for the build.
Use HTTPS for a private repository.
SSH¶
Use an SSH key when the clone URL is ssh:// or git@host:path.
Mount the private key and set Git SSH Key Path.
Also mount a known_hosts file and set Git SSH Known Hosts.
The Watchtower image has no host keys of its own, so the connection fails when that file is missing.
The HTTPS token and password are not sent on the SSH connection. They can still be used for the HTTP API when Hosts names the same hostname as the clone URL.
SSH lists refs, and it checks out a local path context whose origin is SSH. It does not build a Git URL context.
Local Path Origins¶
Checkout uses the origin URL already recorded in that project directory.
An SSH origin uses the SSH key, even when an HTTPS token is also configured. An HTTPS origin uses the token, or the username and password.
Docker Secrets¶
Prefer Docker Secrets for tokens and passwords
Point the Git Auth Token and Git Password configuration options at a mounted file, typically /run/secrets/<name>.
Watchtower reads the file contents at startup.
Do not put the value in the compose file or in the Watchtower process environment.
Tokens and passwords never appear in labels, /v1/config, or events.
/v1/config exposes git_enable only.
TLS¶
Private HTTPS CAs use the Git CA Bundle configuration option.
The Git Insecure Skip TLS configuration option disables verification on clone, ls-remote, REST probes, and local path project checkout.
Example¶
This is the recommended method for Git tokens and passwords.
secrets:
git_auth_token:
file: ./secrets/git_auth_token.txt
services:
watchtower:
image: nickfedor/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
secrets:
- git_auth_token
environment:
WATCHTOWER_GIT_ENABLE: "true"
WATCHTOWER_GIT_AUTH_TOKEN: /run/secrets/git_auth_token
Mount the private key and a known_hosts file.
The runtime image is scratch and has no default host keys.
services:
watchtower:
image: nickfedor/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /path/to/id_ed25519:/run/secrets/id_ed25519:ro
- /path/to/known_hosts:/run/secrets/known_hosts:ro
environment:
WATCHTOWER_GIT_ENABLE: "true"
WATCHTOWER_GIT_SSH_KEY_PATH: /run/secrets/id_ed25519
WATCHTOWER_GIT_SSH_KNOWN_HOSTS: /run/secrets/known_hosts
This is the recommended method for Git tokens and passwords.
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(pwd)/secrets/git_auth_token.txt:/run/secrets/git_auth_token:ro \
--restart unless-stopped \
-e WATCHTOWER_GIT_ENABLE=true \
-e WATCHTOWER_GIT_AUTH_TOKEN=/run/secrets/git_auth_token \
nickfedor/watchtower
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /path/to/id_ed25519:/run/secrets/id_ed25519:ro \
-v /path/to/known_hosts:/run/secrets/known_hosts:ro \
--restart unless-stopped \
-e WATCHTOWER_GIT_ENABLE=true \
-e WATCHTOWER_GIT_SSH_KEY_PATH=/run/secrets/id_ed25519 \
-e WATCHTOWER_GIT_SSH_KNOWN_HOSTS=/run/secrets/known_hosts \
nickfedor/watchtower
Hosts¶
Purpose¶
Watchtower has two ways to see whether a branch or tag has moved:
- Ask Git on that server. This always works.
- Ask GitHub, GitLab, Gitea, or Forgejo through that server's HTTP API. This is faster.
Watchtower already uses the HTTP API on github.com, gitlab.com, and codeberg.org.
You do not set anything extra for those remotes.
On a server you run yourself, the clone URL is often not the HTTP API. A common case is SSH clone plus Gitea on another port:
- Clone URL:
[email protected]:org/app.git - Web UI and HTTP API:
https://git.example.com:3000
Set com.centurylinklabs.watchtower.git-host on that container to the HTTP API base URL.
Watchtower then uses that URL for ref checks.
It derives a releases link when the built-in hostname identifies the provider, or when the API origin has one exact path prefix named github, gitlab, gitea, forgejo, or codeberg.
A pathless self-hosted origin such as https://git.example.com:3000 does not identify the provider, so Watchtower does not guess a changelog link.
Set com.centurylinklabs.watchtower.changelog-url when a custom server needs a different release-page layout.
If the server is not GitHub, GitLab, Gitea, or Forgejo, Watchtower still asks Git.
This label does not turn monitoring on, choose the repository, or supply credentials.
When You Do Not Need It¶
- The repository is on
github.com,gitlab.com, orcodeberg.org - The clone URL is already
https://git.example.com/org/app.giton port 443 and you only need Watchtower to ask Git
When You Need It¶
The container's git-repo is an SSH URL, or the HTTP API is on a different port or path than the clone URL, and you want Watchtower to use that HTTP API.
The label's hostname must match the clone URL.
[email protected]:org/app.git may use https://git.example.com:3000.
A different hostname is ignored, and Watchtower asks Git on the clone URL.
The process token is not sent to that other host.
What to Put In It¶
The API base URL only, for example https://git.example.com:3000 or https://git.example.com/gitlab.
Do not include a repository path.
For an unknown self-hosted hostname, only the exact single-segment prefixes github, gitlab, gitea, forgejo, and codeberg provide a provider hint for changelog derivation.
A pathless or custom multi-segment prefix does not identify the provider.
Example¶
services:
app:
image: myapp:latest
labels:
com.centurylinklabs.watchtower.git-repo: "[email protected]:org/app.git"
com.centurylinklabs.watchtower.git-ref: "main"
com.centurylinklabs.watchtower.git-host: "https://git.example.com:3000"
docker run -d \
--name app \
--label com.centurylinklabs.watchtower.git-repo="[email protected]:org/app.git" \
--label com.centurylinklabs.watchtower.git-ref="main" \
--label com.centurylinklabs.watchtower.git-host="https://git.example.com:3000" \
myapp:latest
The example does the following:
git-repois the SSH clone URL Watchtower (or the Docker daemon) uses to fetch Gitgit-hostis the Gitea HTTP API at port 3000- Watchtower asks that API whether
mainhas moved
Common mistakes¶
| What you see | Cause | What to change |
|---|---|---|
| Repository label is set and nothing rebuilds | The watcher is off | Set WATCHTOWER_GIT_ENABLE=true, or com.centurylinklabs.watchtower.git-watch=true |
git-watch=true and the container stays on the registry path |
No repository association | Set git-repo, or a Git Image mapping whose key matches ImageName() including the tag |
Labels and --git-image disagree |
Labels win | Remove the mapping or the labels. Do not expect both to merge |
| First session does not rebuild | There is no baseline yet, or the image already matches the remote | Push a new commit, or bake org.opencontainers.image.revision before using Run Once |
| Compose project never updates | com.docker.compose.project.working_dir was used as the opt-in |
Set com.centurylinklabs.watchtower.compose-dir or Compose Project. The path is inside Watchtower |
| Compose apply is skipped | The path is missing, has no compose file, or the container has no com.docker.compose.service |
Mount the project and keep the Compose service label |
Local files such as .env disappear, or the project is skipped |
The worktree is dirty | Commit the files, or enable Git Compose Stash |
| Two services in one project never update together | They resolved different commits | Point them at the same ref and policy. One directory is checked out once |
| Private Git URL build fails | The clone URL is SSH | Use HTTPS and a token. SSH is for a local path checkout |
| Self-hosted API is never used | git-host names a different hostname than the clone URL |
Use the same hostname. Port and path may differ |
A new v1.2.4-rc.1 tag is ignored |
patch, minor, and major select release tags only |
Publish v1.2.4, or follow the pre-release ref with policy none |
| A session sees a newer ref and does not rebuild | No-pull or Monitor Only is set | Those options check Git and do not build. /v1/check with no-pull does not contact Git and reports no update |
| An invalid policy skips one container | git-semver-policy is not none, patch, minor, or major |
Fix or remove the label. Other containers continue |
Behavior¶
First Update Session¶
The first session after setup records what is already running. It does not rebuild only to write a stamp.
- Watchtower starts and waits for a session.
- The session checks the remote.
- Watchtower compares that tip to stamp labels, then to the running image.
- If those match the remote, or if none of them are present, the running container is left untouched.
- When nothing on the image identifies the running revision, Watchtower remembers the current remote tip in memory for this process.
A later session in the same process rebuilds only if the remote advances past that remembered tip.
After the Remote Advances¶
- Push a commit to
main, or publish a policy-allowed tag. - Wait for the next update session.
- Watchtower compares the remote tip to the known running revision.
- If the tip has advanced, Watchtower rebuilds using the configured build context and writes a stamp on the replacement.
- If a Git URL build fails, the running container is left untouched. If a Compose apply fails, Docker Compose may have partially recreated services. Watchtower does not accept that commit stamp for the rest of this process, so a later session retries from the previous baseline. A restart, self-update, or separate Run Once invocation has no memory of that rejection and may read the new container stamp instead.
Interaction with Other Update Options¶
| Configuration option | When the watcher is on |
|---|---|
| Monitor Only | Check Git and report stale. Do not checkout, build, apply Compose, or recreate |
| Disable Image Pulling | Check Git only. Do not checkout or ask Docker to build |
| Disable Container Restart | Build if allowed. Do not recreate |
| Cooldown Delay | Skip for images Watchtower just produced. Applied only when the remote has advanced |
HTTP /v1/update |
Same update path |
| Pinned digest + associated + watch on | Git path wins |
Monitor Only and Disable Image Pulling¶
The Monitor Only and Disable Image Pulling configuration options never write stamp labels. They report stale only when the remote has advanced past the known running revision.
Disable Container Restart¶
The Disable Container Restart configuration option can ask Docker to build, or Compose to build, but cannot persist the stamp on the running container. The next session uses image identity or the remembered tip, and rebuilds only if the remote has advanced again.
When the watcher is off, the container stays on the registry digest path.