diff --git a/.all-contributorsrc b/.all-contributorsrc
index 4c07c48..270f462 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -5,6 +5,30 @@
"imageSize": 100,
"commit": false,
"contributors": [
+ {
+ "login": "piksel",
+ "name": "nils måsén",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/807383?v=4",
+ "profile": "https://piksel.se",
+ "contributions": [
+ "code",
+ "doc",
+ "maintenance",
+ "review"
+ ]
+ },
+ {
+ "login": "simskij",
+ "name": "Simon Aronsson",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/1596025?v=4",
+ "profile": "http://simme.dev",
+ "contributions": [
+ "code",
+ "doc",
+ "maintenance",
+ "review"
+ ]
+ },
{
"login": "Codelica",
"name": "James",
@@ -273,18 +297,6 @@
"code"
]
},
- {
- "login": "simskij",
- "name": "Simon Aronsson",
- "avatar_url": "https://avatars0.githubusercontent.com/u/1596025?v=4",
- "profile": "http://simme.dev",
- "contributions": [
- "code",
- "maintenance",
- "review",
- "doc"
- ]
- },
{
"login": "Ansem93",
"name": "Ansem93",
@@ -508,16 +520,6 @@
"doc"
]
},
- {
- "login": "piksel",
- "name": "nils måsén",
- "avatar_url": "https://avatars2.githubusercontent.com/u/807383?v=4",
- "profile": "https://piksel.se",
- "contributions": [
- "doc",
- "code"
- ]
- },
{
"login": "arnested",
"name": "Arne Jørgensen",
@@ -841,6 +843,12 @@
"code"
]
},
+ {
+ "login": "andriibratanin",
+ "name": "Andrii Bratanin",
+ "avatar_url": "https://avatars.githubusercontent.com/u/20169213?v=4",
+ "profile": "https://github.com/andriibratanin"
+ },
{
"login": "IAmTamal",
"name": "Tamal Das ",
@@ -858,6 +866,16 @@
"contributions": [
"doc"
]
+ },
+ {
+ "login": "nothub",
+ "name": "Florian Hübner",
+ "avatar_url": "https://avatars.githubusercontent.com/u/48992448?v=4",
+ "profile": "http://hub.lol",
+ "contributions": [
+ "doc",
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 8f0c724..c479d05 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -44,7 +44,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -55,7 +55,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@v3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -69,4 +69,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v3
diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml
index 9204541..4818e54 100644
--- a/.github/workflows/publish-docs.yml
+++ b/.github/workflows/publish-docs.yml
@@ -20,11 +20,11 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
- go-version: 1.18.x
+ go-version: 1.20.x
- name: Build tplprev
run: scripts/build-tplprev.sh
- name: Setup python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: 'pip'
diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index 819a554..f6866af 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -18,10 +18,10 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
- go-version: 1.18.x
+ go-version: 1.20.x
- uses: dominikh/staticcheck-action@ba605356b4b29a60e87ab9404b712f3461e566dc #v1.3.0
with:
- version: "2022.1.1"
+ version: "2023.1.6"
install-go: "false" # StaticCheck uses go v1.17 which does not support `any`
test:
name: Test
@@ -29,7 +29,7 @@ jobs:
fail-fast: false
matrix:
go-version:
- - 1.18.x
+ - 1.20.x
platform:
- macos-latest
- windows-latest
@@ -43,7 +43,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
- go-version: 1.18.x
+ go-version: 1.20.x
- name: Run tests
run: |
go test -v -coverprofile coverage.out -covermode atomic ./...
@@ -62,7 +62,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
- go-version: 1.18.x
+ go-version: 1.20.x
- name: Build
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 #v3
with:
diff --git a/.github/workflows/release-dev.yaml b/.github/workflows/release-dev.yaml
index df97308..95ee68d 100644
--- a/.github/workflows/release-dev.yaml
+++ b/.github/workflows/release-dev.yaml
@@ -11,10 +11,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v4
with:
- go-version: 1.18
+ go-version: 1.20.x
- name: Build
run: ./build.sh
test:
@@ -24,7 +26,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
- go-version: 1.18
+ go-version: 1.20.x
- name: Test
run: go test -v -coverprofile coverage.out -covermode atomic ./...
- name: Publish coverage
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b6ef374..370d395 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -2,9 +2,7 @@ name: Release (Production)
on:
workflow_dispatch: {}
- release:
- types:
- - created
+ push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- '**/v[0-9]+.[0-9]+.[0-9]+'
@@ -21,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
- go-version: 1.18.x
+ go-version: 1.20.x
- uses: dominikh/staticcheck-action@ba605356b4b29a60e87ab9404b712f3461e566dc #v1.3.0
with:
version: "2022.1.1"
@@ -32,7 +30,7 @@ jobs:
strategy:
matrix:
go-version:
- - 1.18.x
+ - 1.20.x
platform:
- ubuntu-latest
- macos-latest
@@ -46,7 +44,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
- go-version: 1.18.x
+ go-version: 1.20.x
- name: Run tests
run: |
go test ./... -coverprofile coverage.out
@@ -59,7 +57,7 @@ jobs:
- lint
env:
CGO_ENABLED: 0
- TAG: ${{ github.event.release.tag_name }}
+ TAG: ${{ github.ref_name }}
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -68,7 +66,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
- go-version: 1.18.x
+ go-version: 1.20.x
- name: Login to Docker Hub
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc #v2
with:
@@ -93,7 +91,7 @@ jobs:
echo '{"experimental": "enabled"}' > ~/.docker/config.json
- name: Create manifest for version
run: |
- export DH_TAG=$(echo $TAG | sed 's/^v*//')
+ export DH_TAG=$(git tag --points-at HEAD | sed 's/^v*//')
docker manifest create \
containrrr/watchtower:$DH_TAG \
containrrr/watchtower:amd64-$DH_TAG \
diff --git a/README.md b/README.md
index ba6642d..f550302 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,8 @@ $ docker run --detach \
containrrr/watchtower
```
+Watchtower is intended to be used in homelabs, media centers, local dev environments, and similar. We do **not** recommend using Watchtower in a commercial or production environment. If that is you, you should be looking into using Kubernetes. If that feels like too big a step for you, please look into solutions like [MicroK8s](https://microk8s.io/) and [k3s](https://k3s.io/) that take away a lot of the toil of running a Kubernetes cluster.
+
## Documentation
The full documentation is available at https://containrrr.dev/watchtower.
@@ -44,75 +46,75 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
diff --git a/cmd/root.go b/cmd/root.go
index 90c338c..eef13ce 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -29,18 +29,20 @@ import (
)
var (
- client container.Client
- scheduleSpec string
- cleanup bool
- noRestart bool
- monitorOnly bool
- enableLabel bool
- notifier t.Notifier
- timeout time.Duration
- lifecycleHooks bool
- rollingRestart bool
- scope string
- labelPrecedence bool
+ client container.Client
+ scheduleSpec string
+ cleanup bool
+ noRestart bool
+ noPull bool
+ monitorOnly bool
+ enableLabel bool
+ disableContainers []string
+ notifier t.Notifier
+ timeout time.Duration
+ lifecycleHooks bool
+ rollingRestart bool
+ scope string
+ labelPrecedence bool
)
var rootCmd = NewRootCommand()
@@ -93,6 +95,7 @@ func PreRun(cmd *cobra.Command, _ []string) {
}
enableLabel, _ = f.GetBool("label-enable")
+ disableContainers, _ = f.GetStringSlice("disable-containers")
lifecycleHooks, _ = f.GetBool("enable-lifecycle-hooks")
rollingRestart, _ = f.GetBool("rolling-restart")
scope, _ = f.GetString("scope")
@@ -108,7 +111,7 @@ func PreRun(cmd *cobra.Command, _ []string) {
log.Fatal(err)
}
- noPull, _ := f.GetBool("no-pull")
+ noPull, _ = f.GetBool("no-pull")
includeStopped, _ := f.GetBool("include-stopped")
includeRestarting, _ := f.GetBool("include-restarting")
reviveStopped, _ := f.GetBool("revive-stopped")
@@ -120,7 +123,6 @@ func PreRun(cmd *cobra.Command, _ []string) {
}
client = container.NewClient(container.ClientOptions{
- PullImages: !noPull,
IncludeStopped: includeStopped,
ReviveStopped: reviveStopped,
RemoveVolumes: removeVolumes,
@@ -134,7 +136,7 @@ func PreRun(cmd *cobra.Command, _ []string) {
// Run is the main execution flow of the command
func Run(c *cobra.Command, names []string) {
- filter, filterDesc := filters.BuildFilter(names, enableLabel, scope)
+ filter, filterDesc := filters.BuildFilter(names, disableContainers, enableLabel, scope)
runOnce, _ := c.PersistentFlags().GetBool("run-once")
enableUpdateAPI, _ := c.PersistentFlags().GetBool("http-api-update")
enableMetricsAPI, _ := c.PersistentFlags().GetBool("http-api-metrics")
@@ -185,7 +187,7 @@ func Run(c *cobra.Command, names []string) {
metrics.RegisterScan(metric)
}, updateLock)
httpAPI.RegisterFunc(updateHandler.Path, updateHandler.Handle)
- // If polling isn't enabled the scheduler is never started and
+ // If polling isn't enabled the scheduler is never started, and
// we need to trigger the startup messages manually.
if !unblockHTTPAPI {
writeStartupMessage(c, time.Time{}, filterDesc)
@@ -365,6 +367,7 @@ func runUpdatesWithNotifications(filter t.Filter) *metrics.Metric {
LifecycleHooks: lifecycleHooks,
RollingRestart: rollingRestart,
LabelPrecedence: labelPrecedence,
+ NoPull: noPull,
}
result, err := actions.Update(client, updateParams)
if err != nil {
diff --git a/dockerfiles/Dockerfile b/dockerfiles/Dockerfile
index b6f8676..2fc571d 100644
--- a/dockerfiles/Dockerfile
+++ b/dockerfiles/Dockerfile
@@ -1,4 +1,4 @@
-FROM --platform=$BUILDPLATFORM alpine:3.18.3 as alpine
+FROM --platform=$BUILDPLATFORM alpine:3.19.0 as alpine
RUN apk add --no-cache \
ca-certificates \
diff --git a/docs/arguments.md b/docs/arguments.md
index a467538..d7ed0b0 100644
--- a/docs/arguments.md
+++ b/docs/arguments.md
@@ -230,6 +230,19 @@ __Do not__ Monitor and update containers that have `com.centurylinklabs.watchtow
no `--label-enable` argument is passed. Note that only one or the other (targeting by enable label) can be
used at the same time to target containers.
+## Filter by disabling specific container names
+Monitor and update containers whose names are not in a given set of names.
+
+This can be used to exclude specific containers, when setting labels is not an option.
+The listed containers will be excluded even if they have the enable filter set to true.
+
+```text
+ Argument: --disable-containers, -x
+Environment Variable: WATCHTOWER_DISABLE_CONTAINERS
+ Type: Comma- or space-separated string list
+ Default: ""
+```
+
## Without updating containers
Will only monitor for new images, send notifications and invoke
the [pre-check/post-check hooks](https://containrrr.dev/watchtower/lifecycle-hooks/), but will __not__ update the
@@ -346,6 +359,11 @@ Environment Variable: WATCHTOWER_HTTP_API_PERIODIC_POLLS
Update containers that have a `com.centurylinklabs.watchtower.scope` label set with the same value as the given argument.
This enables [running multiple instances](https://containrrr.dev/watchtower/running-multiple-instances).
+!!! note "Filter by lack of scope"
+ If you want other instances of watchtower to ignore the scoped containers, set this argument to `none`.
+ When omitted, watchtower will update all containers regardless of scope.
+
+
```text
Argument: --scope
Environment Variable: WATCHTOWER_SCOPE
diff --git a/docs/container-selection.md b/docs/container-selection.md
index 4b6facd..8327c66 100644
--- a/docs/container-selection.md
+++ b/docs/container-selection.md
@@ -58,6 +58,7 @@ If instead you want to [only include containers with the enable label](https://c
If you wish to create a monitoring scope, you will need to [run multiple instances and set a scope for each of them](https://containrrr.github.io/watchtower/running-multiple-instances).
Watchtower filters running containers by testing them against each configured criteria. A container is monitored if all criteria are met. For example:
+
- If a container's name is on the monitoring name list (not empty `--name` argument) but it is not enabled (_centurylinklabs.watchtower.enable=false_), it won't be monitored;
- If a container's name is not on the monitoring name list (not empty `--name` argument), even if it is enabled (_centurylinklabs.watchtower.enable=true_ and `--label-enable` flag is set), it won't be monitored;
diff --git a/docs/http-api-mode.md b/docs/http-api-mode.md
index 2cf082a..69812bb 100644
--- a/docs/http-api-mode.md
+++ b/docs/http-api-mode.md
@@ -35,3 +35,11 @@ Notice that there is an environment variable named WATCHTOWER_HTTP_API_TOKEN. To
```bash
curl -H "Authorization: Bearer mytoken" localhost:8080/v1/update
```
+
+---
+
+In order to update only certain images, the image names can be provided as URL query parameters. The following `curl` command would trigger an update for the images `foo/bar` and `foo/baz`:
+
+```bash
+curl -H "Authorization: Bearer mytoken" localhost:8080/v1/update?image=foo/bar,foo/baz
+```
diff --git a/docs/notifications.md b/docs/notifications.md
index 3042919..d5da4fe 100644
--- a/docs/notifications.md
+++ b/docs/notifications.md
@@ -24,7 +24,7 @@ system, [logrus](http://github.com/sirupsen/logrus).
- `--notification-skip-title` (env. `WATCHTOWER_NOTIFICATION_SKIP_TITLE`): Do not pass the title param to notifications. This will not pass a dynamic title override to notification services. If no title is configured for the service, it will remove the title all together.
- `--notification-log-stdout` (env. `WATCHTOWER_NOTIFICATION_LOG_STDOUT`): Enable output from `logger://` shoutrrr service to stdout.
-## [shoutrrr](https://github.com/containrrr/shoutrrr) notifications
+## [Shoutrrr](https://github.com/containrrr/shoutrrr) notifications
To send notifications via shoutrrr, the following command-line options, or their corresponding environment variables, can be set:
@@ -57,6 +57,10 @@ outputs timestamp and log level.
custom format.
i.e., The day of the year has to be 1, the month has to be 2 (february), the hour 3 (or 15 for 24h time) etc.
+!!! note "Skipping notifications"
+ To skip sending notifications that do not contain any information, you can wrap your template with `{{if .}}` and `{{end}}`.
+
+
Example:
```bash
diff --git a/docs/private-registries.md b/docs/private-registries.md
index 68fbe84..5367a8c 100644
--- a/docs/private-registries.md
+++ b/docs/private-registries.md
@@ -124,7 +124,7 @@ in a volume that may be mounted onto your watchtower container.
1. Create the Dockerfile (contents below):
```Dockerfile
- FROM golang:1.17
+ FROM golang:1.20
ENV GO111MODULE off
ENV CGO_ENABLED 0
diff --git a/docs/running-multiple-instances.md b/docs/running-multiple-instances.md
index 3899095..5a82c80 100644
--- a/docs/running-multiple-instances.md
+++ b/docs/running-multiple-instances.md
@@ -1,10 +1,11 @@
By default, Watchtower will clean up other instances and won't allow multiple instances running on the same Docker host or swarm. It is possible to override this behavior by defining a [scope](https://containrrr.github.io/watchtower/arguments/#filter_by_scope) to each running instance.
-Notice that:
-- Multiple instances can't run with the same scope;
-- An instance without a scope will clean up other running instances, even if they have a defined scope;
+!!! note
+ - Multiple instances can't run with the same scope;
+ - An instance without a scope will clean up other running instances, even if they have a defined scope;
+ - Supplying `none` as the scope will treat `com.centurylinklabs.watchtower.scope=none`, `com.centurylinklabs.watchtower.scope=` and the lack of a `com.centurylinklabs.watchtower.scope` label as the scope `none`. This effectly enables you to run both scoped and unscoped watchtower instances on the same machine.
-To define an instance monitoring scope, use the `--scope` argument or the `WATCHTOWER_SCOPE` environment variable on startup and set the _com.centurylinklabs.watchtower.scope_ label with the same value for the containers you want to include in this instance's scope (including the instance itself).
+To define an instance monitoring scope, use the `--scope` argument or the `WATCHTOWER_SCOPE` environment variable on startup and set the `com.centurylinklabs.watchtower.scope` label with the same value for the containers you want to include in this instance's scope (including the instance itself).
For example, in a Docker Compose config file:
@@ -12,16 +13,29 @@ For example, in a Docker Compose config file:
version: '3'
services:
- app-monitored-by-watchtower:
+ app-with-scope:
image: myapps/monitored-by-watchtower
- labels:
- - "com.centurylinklabs.watchtower.scope=myscope"
+ labels: [ "com.centurylinklabs.watchtower.scope=myscope" ]
- watchtower:
+ scoped-watchtower:
image: containrrr/watchtower
- volumes:
- - /var/run/docker.sock:/var/run/docker.sock
+ volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ]
command: --interval 30 --scope myscope
- labels:
- - "com.centurylinklabs.watchtower.scope=myscope"
+ labels: [ "com.centurylinklabs.watchtower.scope=myscope" ]
+
+ unscoped-app-a:
+ image: myapps/app-a
+
+ unscoped-app-b:
+ image: myapps/app-b
+ labels: [ "com.centurylinklabs.watchtower.scope=none" ]
+
+ unscoped-app-c:
+ image: myapps/app-b
+ labels: [ "com.centurylinklabs.watchtower.scope=" ]
+
+ unscoped-watchtower:
+ image: containrrr/watchtower
+ volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ]
+ command: --interval 30 --scope none
```
diff --git a/docs/template-preview.md b/docs/template-preview.md
index 3ae4321..3d99ce9 100644
--- a/docs/template-preview.md
+++ b/docs/template-preview.md
@@ -40,7 +40,7 @@
}
#tplprev button {
border-radius: 0.1rem;
- color: var(--md-typeset-color);
+ color: var(--md-primary-bg-color);
background-color: var(--md-primary-fg-color);
flex:1;
min-width: 12ch;
@@ -78,6 +78,8 @@
flex:1;
width:100%
}
+ #result b {color: var(--md-code-hl-special-color)}
+ #result i {color: var(--md-code-hl-keyword-color)}
#tplprev .loading {
position: absolute;
inset: 0;
@@ -90,12 +92,14 @@
-