At home I like to keep things simple and that's why everything I self-host is maintained using docker-compose. I'm also a big changelog fan, and by that I mean I love keeping on top of new improvements, fixes and security updates.
In this post I explain how to introduce Renovate into your docker-compose setup, this allows us to pin docker image versions in our docker-compose but also declare them as dependencies that Renovate will raise pull requests for when a new image is published. This way we can avoid using
:latest tags, stay up to date but also keep on top of what's new.
We'll then use GitHub Actions to SSH into our Docker hosts, copy across the updated
docker-compose up -d. We'll use Tailscale and Tailscale SSH with ephemeral nodes to enable temporarily connectivity from GitHub's runners into my Home Lab.
Option #1 (the easy way)
There are a number of ways to keep your self-hosted services (docker images) up-to-date. One is to use a
:latest docker tag and have containrrr/watchtower periodically check for new images, pull them and restart your services on a schedule. This method is perfect for most people in a Home Lab environment.
Watchtower will pull down your new image, gracefully shut down your existing container and restart it with the same options that were used when it was deployed initially.
Option #2 (the control freak way?)
Commit all of your
compose.yaml to a git repository, I create a folder for each service that I host.
Here is an example that will output compose.yaml for all running Docker containers on a single host.
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/red5d/docker-autocompose $(docker ps -aq)
Install Renovate to your GitHub Repo. In my
compose.yaml above as an example, Renovate will detect the docker image
cloudflare/cloudflared:2023.10.0 as a dependancy.
Install the Renovate app and select the repos you would like. For each selected repo, an Onboarding PR will be created. Merge the onboarding PR and from now on Renovate will create new PRs whenever a new image is published to Docker Hub.
Next we'll setup a GitHub action workflow that uses Tailscale SSH to connect to our Docker Host, pushes the updated
compose.yaml (after merging a PR from Renovate) and updates the running service using
docker-compose up -d
- Waits for updates to our
compose.yaml, a new PR merge from Renovate would trigger this.
- Spins up an ephemeral Tailscale node and uses Tailscale SSH to SSH into our Docker host.
- Copies across the new
compose.yamlto the appropriate directory and runs
docker-compose up -dso that Docker pulls the new image and replaces the container.
It looks a bit like this:
Declaring Tailscale as a dependancy
In our GitHub Action we are telling it to use Tailscale version 1.52.1, we can tell Renovate this is a dependancy and Renovate will check for new versions of Tailscale.
Here is an example of
renovate.json that will look for the version string and compare it against the latest tailscale/tailscale GitHub Release.
And now we'll get Pull requests with full release notes inline for us to review and then merge, perfect! 🎉