From e7ba9cefb5fbdb09d8a18bb332298a56574507a8 Mon Sep 17 00:00:00 2001 From: Jay Date: Fri, 6 Nov 2026 02:49:37 +0100 Subject: [PATCH 1/3] [Repo] ci: Validate compose files and the 3-file convention Runs docker compose config for every service with its .env.example, and checks that each service folder holds exactly docker-compose.yml, .env.example and README.md. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/validate-compose.yml | 62 ++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/validate-compose.yml diff --git a/.github/workflows/validate-compose.yml b/.github/workflows/validate-compose.yml new file mode 100644 index 0000000..e004c3d --- /dev/null +++ b/.github/workflows/validate-compose.yml @@ -0,0 +1,62 @@ +name: Validate Compose + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + structure: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Check the 3-file convention + run: | + status=0 + for dir in */; do + dir="${dir%/}" + files="$(ls -A "$dir" | LC_ALL=C sort | tr '\n' ' ')" + if [ "$files" != ".env.example README.md docker-compose.yml " ]; then + echo "::error file=$dir::$dir must contain exactly docker-compose.yml, .env.example and README.md (found: $files)" + status=1 + fi + done + exit "$status" + + compose-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Validate every docker-compose.yml + run: | + # Dokploy attaches dokploy-network on deploy, so declare it as an + # external network for validation only. + override="$RUNNER_TEMP/networks.override.yml" + printf 'networks:\n dokploy-network:\n external: true\n' > "$override" + + status=0 + for file in */docker-compose.yml; do + dir="$(dirname "$file")" + # Secrets left empty in .env.example on purpose would fail + # ${VAR:?msg}; give them a dummy value for validation only. + env_file="$RUNNER_TEMP/$dir.env" + sed -E 's/^([A-Za-z_][A-Za-z0-9_]*)=$/\1=ci-placeholder/' \ + "$dir/.env.example" > "$env_file" + echo "::group::$file" + if docker compose -f "$file" -f "$override" \ + --env-file "$env_file" config -q; then + echo "OK" + else + echo "::error file=$file::docker compose config failed" + status=1 + fi + echo "::endgroup::" + done + exit "$status" From 7b3bfc3cc55902dcac943d922a4da86e56dfeeb3 Mon Sep 17 00:00:00 2001 From: Jay Date: Fri, 6 Nov 2026 02:49:37 +0100 Subject: [PATCH 2/3] [Repo] docs: Document how each service is updated Co-Authored-By: Claude Opus 5.5 --- apprise/README.md | 6 ++++++ archisteamfarm/README.md | 6 ++++++ cloudflared/README.md | 7 +++++++ garage/README.md | 3 ++- homepage/README.md | 6 ++++++ myspotify/README.md | 9 +++++++++ n8n/README.md | 6 ++++++ pihole/README.md | 6 ++++++ plex/README.md | 6 ++++++ qbittorrent/README.md | 8 ++++++++ speedtest/README.md | 6 ++++++ traccar/README.md | 6 ++++++ watchtower/README.md | 8 +++++--- wgdashboard/README.md | 6 ++++++ 14 files changed, 85 insertions(+), 4 deletions(-) diff --git a/apprise/README.md b/apprise/README.md index 8f9e686..3051676 100644 --- a/apprise/README.md +++ b/apprise/README.md @@ -41,3 +41,9 @@ The API and the admin UI have no authentication of their own. Keep the service off the internet and, if the local network is not trusted, put an authentication layer in front of it (for example a reverse proxy with basic auth). + +## Updates + +Watchtower updates the `apprise` container automatically, following the +`latest` tag. Dokploy redeploys the stack when files under `apprise/**` +change on `main`. diff --git a/archisteamfarm/README.md b/archisteamfarm/README.md index 31608f9..096127f 100644 --- a/archisteamfarm/README.md +++ b/archisteamfarm/README.md @@ -38,3 +38,9 @@ With Docker Compose the real names are prefixed with the project name Connects to `cloudflare-exposed` (must be created by the `cloudflared` service first). + +## Updates + +Watchtower updates the `archisteamfarm` container automatically, +following the `latest` tag. Dokploy redeploys the stack when files under +`archisteamfarm/**` change on `main`. diff --git a/cloudflared/README.md b/cloudflared/README.md index bfd4ad8..2c1a64b 100644 --- a/cloudflared/README.md +++ b/cloudflared/README.md @@ -55,3 +55,10 @@ networks: [tunnel-docs]: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/ [zero-trust]: https://one.dash.cloudflare.com/ [cloudflared-repo]: https://github.com/cloudflare/cloudflared + +## Updates + +Watchtower updates the `cloudflare-tunnel` container automatically, +following the `latest` tag. Dokploy redeploys the stack when files under +`cloudflared/**` change on `main`. While the tunnel restarts, every +service exposed through it is briefly unreachable. diff --git a/garage/README.md b/garage/README.md index adabbcc..4e336c9 100644 --- a/garage/README.md +++ b/garage/README.md @@ -58,4 +58,5 @@ docker exec garage /garage bucket allow --read --write backups --key backups-key Garage publishes only exact version tags (no `latest` or `v2`), so Watchtower cannot update it. Dependabot opens a pull request for each new 2.x release instead; major versions need a manual migration and are -ignored. +ignored. Dokploy redeploys the stack when files under `garage/**` +change on `main`, so merging a Dependabot bump rolls it out. diff --git a/homepage/README.md b/homepage/README.md index 02f1d26..4899d8e 100644 --- a/homepage/README.md +++ b/homepage/README.md @@ -64,3 +64,9 @@ with the named volume: Connects to `cloudflare-exposed` (must be created by the `cloudflared` service first). + +## Updates + +Watchtower updates the `homepage` container automatically, following the +`latest` tag. Dokploy redeploys the stack when files under `homepage/**` +change on `main`. diff --git a/myspotify/README.md b/myspotify/README.md index 71b5bbd..7e17c5c 100644 --- a/myspotify/README.md +++ b/myspotify/README.md @@ -68,3 +68,12 @@ Moving to `mongo:7` is not a simple retag: 7.0 cannot open 8.x data files, and `mongodump`/`mongorestore` only supports restoring into the same major version or FCV. Export the data with `mongoexport`/`mongoimport` instead. + +## Updates + +Watchtower updates all three containers automatically: the backend and +frontend follow `latest`, and `myspotify-mongo` follows `8`, so it stays +within MongoDB 8. A new MongoDB major is a manual change to the image tag +and needs a dump and restore (see the kernel workaround notes above). +Dokploy redeploys the stack when files under `myspotify/**` change on +`main`. diff --git a/n8n/README.md b/n8n/README.md index d8bdb5c..d16038c 100644 --- a/n8n/README.md +++ b/n8n/README.md @@ -49,3 +49,9 @@ name (for example `_n8n-data`). n8n off the internet. - Nodes that read or write files are limited to `/tmp/github-bundles` (`N8N_RESTRICT_FILE_ACCESS_TO`), outside the data volume. + +## Updates + +Watchtower updates the `n8n` container automatically, following the +`latest` tag. Dokploy redeploys the stack when files under `n8n/**` +change on `main`. diff --git a/pihole/README.md b/pihole/README.md index c042635..1b29c97 100644 --- a/pihole/README.md +++ b/pihole/README.md @@ -30,3 +30,9 @@ nebula-sync). Connects to `cloudflare-exposed` (must be created by the `cloudflared` service first). + +## Updates + +Watchtower updates the `pihole` container automatically, following the +`latest` tag. Dokploy redeploys the stack when files under `pihole/**` +change on `main`. diff --git a/plex/README.md b/plex/README.md index 1363717..3a71572 100644 --- a/plex/README.md +++ b/plex/README.md @@ -34,3 +34,9 @@ the backup so its databases stay consistent. The linuxserver image adds the Plex user to the device's group on start. Enable it in Plex under *Settings → Transcoder → Use hardware acceleration when available*. + +## Updates + +Watchtower updates the `plex` container automatically, following the +`latest` tag. Dokploy redeploys the stack when files under `plex/**` +change on `main`. diff --git a/qbittorrent/README.md b/qbittorrent/README.md index f69d33a..eee58ef 100644 --- a/qbittorrent/README.md +++ b/qbittorrent/README.md @@ -75,3 +75,11 @@ rather than auto-updated. To update, set both values in `docker-compose.yml` from the new release (the SHA-256 is shown next to `vuetorrent.zip` on its GitHub release page, or `sha256sum vuetorrent.zip`) and redeploy. Older version folders stay in the volume (about 11 MB each). + +## Updates + +Not updated automatically: the qBittorrent image, the `alpine` helper and +VueTorrent are all pinned, and none of them is opted in to Watchtower or +Dependabot. Bump them by hand (see *Pinned version* and *VueTorrent* +above). Dokploy redeploys the stack when files under `qbittorrent/**` +change on `main`. diff --git a/speedtest/README.md b/speedtest/README.md index 0a68069..160e0bf 100644 --- a/speedtest/README.md +++ b/speedtest/README.md @@ -30,3 +30,9 @@ Connects to `cloudflare-exposed` (must be created by the `cloudflared` service first). [speedtest-tracker]: https://github.com/alexjustesen/speedtest-tracker + +## Updates + +Watchtower updates the `speedtest` container automatically, following the +`latest` tag. Dokploy redeploys the stack when files under `speedtest/**` +change on `main`. diff --git a/traccar/README.md b/traccar/README.md index 766d8d4..3fd932e 100644 --- a/traccar/README.md +++ b/traccar/README.md @@ -38,3 +38,9 @@ attaches its own network automatically on deploy. - Default ports: `25002` (mapped to 5013) and `25003` (mapped to 5023). + +## Updates + +Watchtower updates the `traccar` container automatically, following the +`latest` tag. The MariaDB database is not part of this stack. Dokploy +redeploys the stack when files under `traccar/**` change on `main`. diff --git a/watchtower/README.md b/watchtower/README.md index 5ba7efc..ac1b4cd 100644 --- a/watchtower/README.md +++ b/watchtower/README.md @@ -24,6 +24,8 @@ Sends notifications via Telegram. ## Updates -Watchtower updates itself too: its own container has the opt-in label. -Services pinned to an exact version (for example Garage) are updated -through Dependabot pull requests instead. +Watchtower updates itself too, following the `latest` tag: its own +container has the opt-in label. Services pinned to an exact version +(for example Garage) are updated through Dependabot pull requests +instead. Dokploy redeploys the stack when files under `watchtower/**` +change on `main`. diff --git a/wgdashboard/README.md b/wgdashboard/README.md index fb22659..436d467 100644 --- a/wgdashboard/README.md +++ b/wgdashboard/README.md @@ -34,3 +34,9 @@ keys, so treat it and its backups as secrets. Declares no network. Needs the `NET_ADMIN` capability to manage the WireGuard interface. + +## Updates + +Watchtower updates the `wgdashboard` container automatically, following +the `latest` tag. Dokploy redeploys the stack when files under +`wgdashboard/**` change on `main`. From 7c6be6d77404bd2d1d7dfe1dbe85abb365fb6e50 Mon Sep 17 00:00:00 2001 From: Jay Date: Fri, 6 Nov 2026 02:49:37 +0100 Subject: [PATCH 3/3] [Repo] docs: Add a Backup column to the services table Co-Authored-By: Claude Opus 5.5 --- README.md | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 87cbf02..399043a 100644 --- a/README.md +++ b/README.md @@ -8,22 +8,25 @@ Docker Compose stacks, deployed via [Dokploy](https://dokploy.com). ## Services -| Service | Description | -| --- | --- | -| [Apprise](apprise/) | Notification gateway | -| [ArchiSteamFarm](archisteamfarm/) | Steam account management | -| [Cloudflared](cloudflared/) | Cloudflare Tunnel | -| [Garage](garage/) | S3-compatible storage for backups | -| [Homepage](homepage/) | Dashboard and service portal | -| [MySpotify](myspotify/) | Spotify listening statistics | -| [n8n](n8n/) | Workflow automation | -| [Pi-hole](pihole/) | DNS and ad blocking | -| [Plex](plex/) | Media server | -| [qBittorrent](qbittorrent/) | Torrent client | -| [Speedtest](speedtest/) | Internet speed tracking | -| [Traccar](traccar/) | GPS tracking server | -| [Watchtower](watchtower/) | Automatic container updates | -| [WGDashboard](wgdashboard/) | WireGuard VPN server and dashboard | +| Service | Description | Backup | +|-----------------------------------|------------------------------------|--------------------------------------| +| [Apprise](apprise/) | Notification gateway | Daily (config volume) | +| [ArchiSteamFarm](archisteamfarm/) | Steam account management | Daily (config and plugins volumes) | +| [Cloudflared](cloudflared/) | Cloudflare Tunnel | Not needed (stateless) | +| [Garage](garage/) | S3-compatible storage for backups | No (it is the backup target) | +| [Homepage](homepage/) | Dashboard and service portal | Daily (config volume) | +| [MySpotify](myspotify/) | Spotify listening statistics | Daily (MongoDB dump) | +| [n8n](n8n/) | Workflow automation | Daily (data volume) | +| [Pi-hole](pihole/) | DNS and ad blocking | No (by choice) | +| [Plex](plex/) | Media server | Weekly (config volume) | +| [qBittorrent](qbittorrent/) | Torrent client | Daily (config volume) | +| [Speedtest](speedtest/) | Internet speed tracking | Daily (config volume) | +| [Traccar](traccar/) | GPS tracking server | Daily (database, managed separately) | +| [Watchtower](watchtower/) | Automatic container updates | Not needed (stateless) | +| [WGDashboard](wgdashboard/) | WireGuard VPN server and dashboard | Daily (data and config volumes) | + +Backups are Dokploy backups to S3-compatible storage, configured outside +this repository. ## Prerequisites