In the world of self-hosting, containerized applications reign supreme these days. Personally, all of my self-hosted applications are deployed via Docker and Docker Compose. One thing that has always bothered me about my setup is how inconvenient the update process can be. I have tried a variety of different platforms such as watchtower, wud, and diun - all of which aim to make the process of managing your docker container's updates a bit easier through automated updates, notifications, etc. While all of these programs are fantastic, I was still left wanting for a better pipeline for managing updates.
I realized that I had an "ideal" situation in mind for managing updates. For quite some time, I have stored all of my Docker Compose configuration files in a repository on my Gitea instance. What I really wanted was something like Dependabot to parse my repo for image versions, check if there are any updates for it, and then automatically open a pull request for the update (and notify me as well). Taking things a bit further, I wanted any version updates merged into the repo to automatically redeploy with the new version. The idea was to make version updates literally as simple as clicking a button.
For a while I assumed none of this was possible using existing FOSS projects, so I didn't really bother looking into it. Things changed when I first learned about Renovate - which is essentially a self-hostable version of Dependabot. Next, I learned about Komodo - which is capable of quite a bit, but most importantly it can manage and deploy docker containers from compose files in a Git repo. After a bit of research, trial, and error I determined that Renovate, Komodo, and software I was already using actually made managing updates in the way that I had imagine possible. In this guide, I will show you how.
Overview
To get all of this working, we need to configure a few different services (if you aren't running them already):
- Some kind of Git Provider (Gitea, Forgejo, Github) - A repository will hold all of your your Docker Compose files and act as a single point of truth for the other programs we are using. In this article I will be using Gitea.
- Some kind of CI/CD platform (Gitea/Forgejo/GitHub Actions, Drone) that is integrated with your Git provider - This will allow us to run the next service. In this article I will be using Gitea Actions.
- Renovate - This will parse the compose files in your repository and search for any updates.
- Komodo - This will handle the management & deployment of your Docker Compose stacks.
As you can see, there is quite a bit of work to be done here - but don't let that scare you! Think of this process as an investment - once things are set up here, managing your docker containers and their updates will be smooth sailing! That said, if you are just getting started on your self-hosting journey, this setup may not be for you. This guide will assume some level of knowledge and familiarity of Docker, Docker Compose, Git, and the operating system of your host machine.
I will go over setting these various services up in some detail, but if you find that your setup differs or you have different configuration requirements, you may need to refer to these programs' documentation.
Result
Here's a summary of the result we are trying to achieve with all of this:
-
Your Docker Compose files exist in a remote Git repository (Gitea in this case).
-
Komodo Clones this repo, and deploys your services using the Docker Compose files in that repo.
-
Renovate runs on a schedule (nightly in this case), scans your repo and checks if there are any updates for the images in your Docker Compose files.
- If there is an update for an image, Renovate opens a Pull Request with the new version tag, and assigns the PR to your user.
- You receive an email notification about the update's PR.
- If you merge the PR, a webhook notifies Komodo about the merge
-
When Komodo's webhook is triggered:
- It pulls the latest commit of your repo from your Git provider
- Pulls updated docker images
- Redeploys containers that have had their image version tag updated
Prerequisites
- A host machine with Docker & Docker Compose installed and running. I will be using my custom built NAS running Unraid.
- An email provider that supports SMTP (for sending notifications via Gitea)
Setting Up Gitea
As mentioned, I will be using Gitea as my Git provider, though if I could do it all over again, I might choose Forgejo. Thankfully, at the time of writing, the setup and configuration of Gitea and Forgejo are essentially identical, and the process is easy and well documented here and here respectively.
Deploying Gitea
Here is my Docker Compose configuration:
# docker-compose.yaml
services:
server:
image: gitea/gitea:1.23.7
container_name: gitea-server
restart: always
environment:
- TZ=America/Los_Angeles
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=${GITEA_DB_PASSWORD}
volumes:
# replace the left-hand side from the ':' with your own path
- /mnt/user/appdata/gitea/data:/data
ports:
- 3105:3000
- 222:22
depends_on:
- db
db:
image: postgres:17.4
container_name: gitea-db
restart: always
environment:
- TZ=America/Los_Angeles
- POSTGRES_DB=gitea
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=${GITEA_DB_PASSWORD}
volumes:
# replace the left-hand side from the ':' with your own path
- /mnt/user/appdata/gitea/database:/var/lib/postgresql/data
# .env
GITEA_DB_PASSWORD="your own database password"
From here, run docker compose up
from inside the folder your files are in, and you should be off to the races (once going through a short setup page). There's a lot of configuration you can do from here, but I will go over the necessary options we need for our setup.
Configuring Gitea
In app.ini
First we will be editing Gitea's configuration file. This will have been generated from the options you selected on the setup screen, and it will be located at /gitea/conf/app.ini
relative to the path you mapped to the path /data
on the server container in your compose file (mine is located at /mnt/user/appdata/gitea/data/gitea/conf/app.ini
). Here are the options to set/verify:
- Set
ENABLE_NOTIFY_MAIL = true
under the[service]
heading. - Configure the mailer under the
[mailer]
heading - this is essential if you want to receive notifications about updates. You can reference the docs page here about setting up email, which covers setting up with Gmail, but any email provider with SMTP support will work. - Set
ENABLED = true
under the[actions]
heading - actions should be enabled by default, but set this anyways to be sure. - Set
ALLOWED_HOST_LIST = loopback,private,*.yourdomain.com
under the[webhook]
heading. This heading didn't exist for me, so I had to create it. Set*.yourdomain.com
to your own domain if you are using a reverse proxy (which you should) for accessing your different services.
Assuming I didn't miss anything, that should be everything you need to do in app.ini
for now, but be sure to configure Gitea to your liking, it's a fantastic tool. You can find Gitea's configuration reference here. Be sure to run docker compose up -d
again to restart Gitea so it can pickup your configuration changes.
In Gitea's Interface
There's some things we need to do in Gitea's administration interface. Go to Site Administration
from the profile button in the upper right of the screen.
- Test your mailer configuration under
Configuration > Summary > Mailer Configuration
. There will be a field to type in an email address and send a test email. If this spits an error, you may need to take another look at the configuration for it inapp.ini
(docs reference). - Ensure your user has email notifications enabled via your user's settings under
Account > Manage Email Addresses > Set Email Preference
Make A Repo
At this point, you should create a private repository for your Docker Compose files if you haven't already. There are a variety of ways you can organize compose files, but I prefer to use folders to group my compose files with their .env
files, and any other files that are relevant to a particular stack:
/docker-compose
/stack-one
docker-compose.yaml
/stack-two
docker-compose.yaml
.env
As long as your files end up in a repo, you should be good. Make sure you do not have any secrets in your compose files, and that you have set up a .gitignore
file to ignore any .env
files to avoid any confidential information getting committed to your repo.
# .gitignore
*.env
Setting up CI/CD
As mentioned in the overview, I will be using Gitea Actions for my CI/CD. Later on, when we are configuring actions/workflows, it is my understanding that the workflow syntax should be similar if not identical between Gitea/Forgejo/Github Actions.
Preparation
Gitea Actions uses 'runners' to run your CI/CD actions/workflows. Before we deploy the runner, we need to get a runner registration token from Gitea. This can be done in one of a few places, depending on how you want to scope the runner to your Gitea instance (reference). For this guide I will recommend registering your runner to the "Instance" level. To do this, go to Site Administration > Actions > Runners
and click on Create new Runner
and copy the token that displays in the popover. Save this for the next step.
Deploying the Runner
There are a few ways to deploy a runner for Gitea Actions, outlined here, but for this guide we will be deploying the runner via docker by adding it to our existing Gitea compose file - here's what it should look like:
services:
server:
# ...
db:
# ...
runner:
image: gitea/act_runner:0.2.11
container_name: gitea_runner
restart: always
depends_on:
- server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# replace the left-hand side from the ':' with your own path
- /mnt/user/appdata/gitea/runner/config.yaml:/config.yaml
# replace the left-hand side from the ':' with your own path
- /mnt/user/appdata/gitea/runner/data:/data
environment:
- TZ=America/Los_Angeles
- CONFIG_FILE=/config.yaml
- GITEA_INSTANCE_URL=gitea-server:3000
- GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_TOKEN}
- GITEA_RUNNER_NAME=your-runner-name # change this
Make sure to add an environment variable GITEA_RUNNER_TOKEN
to the .env
file we setup earlier, and paste the registration token we obtained in the previous step:
# .env
GITEA_DB_PASSWORD="your own database password"
GITEA_RUNNER_TOKEN="your token from the previous step"
Before you deploy the container, we need to generate a config file for the runner. To do this run the following command and move the output file to the location you specified for config.yaml
in the compose file:
docker run --entrypoint="" --rm -it docker.io/gitea/act_runner:latest act_runner generate-config > config.yaml
Now you can run docker compose up runner -d
to launch your runner. If you look in the Gitea admin interface, you should see your runner has been registered.
Setting Up Komodo
Komodo is a super cool project. You can do a lot with it, and if I am being honest, the way we will be utilizing it in this guide is pretty simple compared to what it is capable of. The role Komodo will play in our setup is to:
- Manage and deploy our Docker Compose stacks/files
- Integrate with Gitea to update relevant containers when changes are made
Deploying Komodo
Komodo provides very detailed and commented example compose files in their documentation here. I recommend using the MondoDB setup, as the other database options use an adapter to work - I'd prefer to remove points of failure. If you have a specific place on your host machine that you would like your compose files to live (I do), I would recommend adding an additional volume mapping to the periphery container. Here is what my compose file for Komdo looks like (the .env file is far too long to post here, and full of secrets - reference the official example here):
services:
mongo:
image: mongo
container_name: komodo-mongo
command: --quiet --wiredTigerCacheSizeGB 0.25
restart: unless-stopped
logging:
driver: ${COMPOSE_LOGGING_DRIVER:-local}
volumes:
- /mnt/user/appdata/komodo/mongo/db:/data/db
- /mnt/user/appdata/komodo/mongo/config:/data/configdb
environment:
MONGO_INITDB_ROOT_USERNAME: ${KOMODO_DB_USERNAME}
MONGO_INITDB_ROOT_PASSWORD: ${KOMODO_DB_PASSWORD}
labels:
komodo.skip: # Prevent Komodo from stopping with StopAllContainers
core:
image: ghcr.io/moghtech/komodo-core:${COMPOSE_KOMODO_IMAGE_TAG:-latest}
container_name: komodo-core
restart: unless-stopped
depends_on:
- mongo
logging:
driver: ${COMPOSE_LOGGING_DRIVER:-local}
ports:
- 9120:9120
env_file: ./.env
environment:
KOMODO_DATABASE_ADDRESS: mongo:27017
KOMODO_DATABASE_USERNAME: ${KOMODO_DB_USERNAME}
KOMODO_DATABASE_PASSWORD: ${KOMODO_DB_PASSWORD}
volumes:
## Core cache for repos for latest commit hash / contents
- /mnt/user/appdata/komodo/core/repos:/repo-cache
labels:
komodo.skip: # Prevent Komodo from stopping with StopAllContainers
periphery:
image: ghcr.io/moghtech/komodo-periphery:${COMPOSE_KOMODO_IMAGE_TAG:-latest}
container_name: komodo-periphery
restart: unless-stopped
logging:
driver: ${COMPOSE_LOGGING_DRIVER:-local}
env_file: ./.env
environment:
PERIPHERY_REPO_DIR: ${PERIPHERY_ROOT_DIRECTORY:-/etc/komodo}/repos
PERIPHERY_STACK_DIR: ${PERIPHERY_ROOT_DIRECTORY:-/etc/komodo}/stacks
PERIPHERY_SSL_KEY_FILE: ${PERIPHERY_ROOT_DIRECTORY:-/etc/komodo}/ssl/key.pem
PERIPHERY_SSL_CERT_FILE: ${PERIPHERY_ROOT_DIRECTORY:-/etc/komodo}/ssl/cert.pem
volumes:
## Mount external docker socket
- /var/run/docker.sock:/var/run/docker.sock
## Allow Periphery to see processes outside of container
- /proc:/proc
## Specify the Periphery agent root directory - must be the same inside and outside the container.
- ${PERIPHERY_ROOT_DIRECTORY:-/etc/komodo}:${PERIPHERY_ROOT_DIRECTORY:-/etc/komodo}
## Custom location for docker compose repo
- /mnt/user/compose:/mnt/user/compose
labels:
komodo.skip: # Prevent Komodo from stopping with StopAllContainers
Once you run docker compose up -d
, you should be able to access the admin interface. Enter a username and a password, then click Sign Up to register the admin account.
Using Komodo
As mentioned, Komodo is capable of quite a bit and the interface can be a bit overwhelming. Worry not, we will be using it in a pretty straightforward way!
Configuring Git Credentials
Since our compose repository is private, we need to tell Komodo how to authenticate with our Gitea instance. here's how to do that:
- Log into your Gitea instance, go to your user's settings by clicking the profile icon in the upper right corner of the screen and clicking
Settings
. - Click on the
Applications
tab in the sidebar, and generate a new token with a name likekomodo-token
andread:repository
permissions. Copy/save the token (it will only show this once). - Log into your Komodo dashboard, and click on the
Settings
tab in the lower left corner of the screen. - Click on the
Providers
tab near the top of the screen, and clickNew Account
under theGit Providers
heading. Fill in the URL of your Gitea instance (omit http/https), your username, and the token obtained in step 2.
Configure Your Repo
Now that we have configured access to our Gitea instance, we can clone our docker compose repo in Komodo:
-
Click on the
Repos
tab in the sidebar. -
Click
New Repo
, giving it a name, then clickingCreate
. -
On the following screen set:
- Server - There should only be one option
- Builder - 'local'
- Source - Select the provider you configured in the previous section
- Account - Select the account you configured in the previous section
- Repo - Enter the scope/reponame of your compose repo - mine is
ncunningham/docker-compose
- Clone Path (Optional) - If you configured an additional volume mapping for the periphery container in your compose file, use that path here.
-
Click
Save
and then clickClone
under theExecute
heading near the top of the page.
Assuming you configured everything correctly, Komodo should be able to successfully clone your repo!
Configure Your Stacks
Now we will configure Komodo to manage and deploy your compose files:
-
Click on
Stacks
in the sidebar. -
Click on
New Stack
, giving it a name matching it's folder in your repo, then clickCreate
. -
Scroll down a bit and click on the drop-down
Choose Mode
, selecting the optionFiles On Server
. -
Set the following values:
- Run Directory - the full path to the desired stack. Mine would be
/mnt/appdata/compose/stackname
because I manually specified the path that I cloned my repo to. If you did not do this, your repo would be located at whatever path you configuredPERIPHERY_ROOT_DIR
to be in your Komodo's.env
file. - File Paths - if your compose files are not named
compose.yaml
exactly, specify the name of the file here (I usedocker-compose.yaml
for mine). - Environment - if your compose file uses any environment variables, place them here. It will automatically generate a
.env
file next to your compose file with the values entered.
- Run Directory - the full path to the desired stack. Mine would be
Repeat this process for each of the Docker Compose files you have defined in your repo. Do not add the Docker Compose file for Komodo if you configured Komodo in the same repo as the rest of your services - I don't even know what would happen if you did this, but it's not a good idea on principle - see ouroboros - Komodo can't manage itself. Now Komodo is managing your compose stacks, and provides a pretty nice interface for stopping, starting, etc. your containers! We will return to Komodo a bit later towards the end of this guide.
Setting Up Renovate
As mentioned in the overview, Renovate is the tool we are using to check for updates to our docker images. If you are familiar with GitHub's Dependabot or similar tools, Renovate works similarly and will check your Docker Compose files for version updates on whatever interval you would like.
While you can deploy Renovate as it's own container, I found that running it on demand via Gitea Actions works best, which is why we set that up earlier.
Preparation
Before Renovate can do it's job, it's important that the images in your compose files are tagged properly. Often, installation guides and example compose files will either use no image tag, or the tag 'latest' or something similar. Outside of the context of this guide, this is often convenient, but its not great if you want to ensure that there aren't breaking changes, regressions, or other issues with whatever the 'latest' image is. Within the context of this guide, in order for Renovate to know what version your images are, your images need to be tagged with greater specificity.
The best case scenario is that you tag an image in your compose files with the full semantic version, but not all projects use image tagging effectively or follow best practices. Thankfully there are a variety of ways to tag your images which I will outline below in order of worst to best:
# bad - don't do this
image: gitea/gitea
image: gitea/gitea:latest
# ok - better than nothing, but not specific enough for renovate to do it's job
image: gitea/gitea:1
image: gitea/gitea:1.23
# good - the @sha256 pins latest to a specific build digest, but obfuscates the real version of the image
image: gitea/gitea:latest@sha256:01bb6f98fb9e256554d59c85b9f1cb39f3da68202910ea0909d61c6b449c207d
# better - pins the image to a clear and specific image version
image: gitea/gitea:1.23.6
# best - pins the image to a specific version AND digest, makes the specific version immutable
image: gitea/gitea:1.23.6@sha256:01bb6f98fb9e256554d59c85b9f1cb39f3da68202910ea0909d61c6b449c207d
For my purposes, I currently use the full semver tag, without the build digest. Before proceeding any further, I would go through your compose files and make sure they are appropriately tagged!
Create A Renovate Bot Account
Technically this is optional and you could use your own user to run renovate, but I wouldn't. Unfortunately Gitea doesn't support something akin to a "Service Account", so it is best to simply create a new user in Gitea to act on behalf of renovate. Depending on your Gitea configuration, you may have to create this new user from your admin panel, or you may simply be able to log out of your primary account and register for a new account on your Gitea instance. I called my new user "renovate-bot". I whipped up this profile image using Renovate's logo to make things look nicer, you are welcome to use it.
Create A Renovate Configuration Repository
Still logged in as your Renovate Bot account, create a new repository to configure and run Renovate from.
Configure Renovate
First, create a file in the root of the repo called config.js
and paste the following contents:
module.exports = {
platform: 'gitea',
endpoint: 'https://example.com/api/v1/', // set this to the url of your gitea instance
gitAuthor: 'Renovate Bot <renovate-bot@example.com>', // set the email address to whatever email your gave this user in your gitea
username: 'renovate-bot',
autodiscover: true,
onboardingConfig: {
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
extends: ['config:recommended'],
},
optimizeForDisabled: true,
persistRepoData: true,
};
This file is the base configuration that Renovate will use.
Configure Renovate's Workflow
Next you will want to create a file located in .gitea/workflows/renovate.yaml and paste the following contents:
name: renovate
on:
workflow_dispatch: # allows the workflow to be run manually when desired
branches:
- main
schedule: # runs this workflow at the scheduled time (uses UTC, adjust for your timezone)
- cron: "0 12 * * *"
push: # runs this workflow when pushes to the main branch are made
branches:
- main
jobs:
renovate:
runs-on: ubuntu-latest
container: ghcr.io/renovatebot/renovate:latest
steps:
- uses: actions/checkout@v4
- run: renovate
env:
RENOVATE_CONFIG_FILE: ${{ gitea.workspace }}/config.js
LOG_LEVEL: "debug"
RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
GITHUB_COM_TOKEN: ${{ secrets.RENOVATE_GITHUB_TOKEN }} # optional
This file defines a workflow for your Gitea Actions Runner to run.
Provide Renovate Secrets
In order for renovate to run, it needs to authenticate as your Renovate Bot user. To do this we need to provide the runner with an access token via your repository's Actions Secrets:
-
Go to the account settings of your Renovate Bot user, click on
Applications
and thenGenerate Token
with a name likerenovate-token
and the following permissions:- read:misc
- read:notification
- read:organization
- read:package
- write:issue
- write:repository
- read:user
-
Go to the repository settings of your Renovate Config repo, clicking on
Actions > Secrets
in the sidebar and create a new secret calledRENOVATE_TOKEN
with the contents of your access token from the previous step. -
Optionally, you can provide a token from GitHub as well, which Renovate uses to fetch change logs for updates. You can see more about that here.
Renovate should now be able to successfully run via Gitea Actions, though it will have nothing to do yet.
Configure Renovate In Your Docker Compose Repo
Now that we have configured Renovate to run, we must configure Renovate to:
- Know about your Docker Compose repo
- Know what to do with your Docker Compose repo
If you haven't already, sign back into your primary Gitea user.
Add Renovate Bot as a Collaborator
In order for Renovate to discover your Docker Compose repo, you must add the Renovate Bot account we created as a collaborator:
- Go to the repository settings of your Docker Compose repo
- Click on the
Collaborators
tab of the sidebar - Search for and add your Renovate Bot user as a collaborator
Now when Renovate runs, it will be able to access your Docker Compose repo.
Configure Renovate in the Repo
If you have used something like GitHub's Dependabot before, you know it is configured via a file called dependabot.yaml
that is located locally in each repo that it runs in. Renovate works similarly, via a file renovate.json
that we must create at the root of our Docker Compose repository. Here is what the basic configuration file should look like:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"dependencyDashboard": true,
"dependencyDashboardTitle": "Renovate Dashboard",
"assignees": ["your-gitea-user"],
"labels": ["renovate"],
"configMigration": true,
"prHourlyLimit": 0,
"docker-compose": {
"hostRules": [
{
"matchHost": "docker.io",
"concurrentRequestLimit": 2
}
],
}
}
This file defines how renovate should behave inside of this repo. Notably it tells renovate to:
- Create an issue in your repo that acts as a Renovate "Dashboard" which will provide a summary of Renovate's status in the repo.
- Assign pull requests that Renovate opens to your Gitea user. This will send you an email if you have Gitea's mailer configured and notifications turned on.
- Apply a
renovate
label to pull requests that Renovate opens. - Remove rate limits on PR creation (there are no limits on a self hosted Gitea instance)
- Rate limit queries to DockerHub, which does impose some sort of limits on frequency of pulls.
Unfortunately, you will likely need to add additional configuration depending on the images you are deploying, and the way in which those images are tagged. You can do this via entries under "packageRules" in your renovate.json
file (configuration reference). Here's some examples of the issues I encountered that required additional configuration:
- The redis and database containers used in immich are pinned at a specific version recommended by the developers. I don't want Renovate to try to update these.
- The linuxserver.io Jellyfin image I use has non semver tags that are numerically greater than the current semver of Jellyfin (
v2021
>10.10.6
). I want Renovate to ignore those versions. - The Duplicacy image that I use has tags that are prefixed with 'release-' (
release-1.8.0
) and renovate was having trouble with this. I want Renovate to identify the semver within the tag, omitting the prefix.
So I have ended up with a config file that looks more like this:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"dependencyDashboard": true,
"dependencyDashboardTitle": "Renovate Dashboard",
"assignees": ["your-gitea-user"],
"labels": ["renovate"],
"configMigration": true,
"prHourlyLimit": 0,
"docker-compose": {
"hostRules": [
{
"matchHost": "docker.io",
"concurrentRequestLimit": 2
}
],
"packageRules": [
{
"matchPackageNames": "redis",
"matchCurrentVersion": "6.2-alpine",
"enabled": false
},
{
"matchPackageNames": "tensorchord/pgvecto-rs",
"enabled": false
},
{
"matchPackageNames": "ghcr.io/hotio/duplicacy",
"versionCompatibility": "^(?<compatibility>release-)(?<version>.+)$"
},
{
"matchPackageNames": ["ghcr.io/linuxserver/jellyfin"],
"allowedVersions": "<2021"
},
]
}
}
Going into the details of these packageRules
is beyond the scope of this article, but all of this is to say that you will likely need to do some additional Renovate Configuration to get things working as desired.
Bringing Everything Together
At this point we have:
- Setup a Gitea instance
- Setup a Gitea Actions Runner
- Created a repostory in Gitea where our Docker Compose files will live
- Setup Komodo
- Cloned our Docker Compose repo from Gitea into Komodo
- Configured Komodo to deploy our Docker Compose stacks from the repo
- Created a user to act as a bot for Renovate to run as
- Created a repo under the Renovate Bot user to configure and run Renovate
- Configured Renovate to run via Gitea Actions on a daily schedule
- Configured Renovate to open Pull Requests in our Docker Compose repo for any updates that it finds, which will alert us via email.
Now that I've written all of this out, Kudos if you are still with me! The good news is that we are almost at the end, and we are about to bring all these different components together.
Create A Procedure In Komodo
Back in the Komodo dashboard:
-
Click on the
Procedures
tab in the sidebar, select New Procedure, giving it a name like "update-stacks" and clickingCreate
. -
Under the
Config
heading, clickAdd Stage
with the following properties:- Execution -
Pull Repo
- Target - Select your Docker Compose repo
- Execution -
-
Add a second stage with the following properties:
- Execution -
Batch Deploy Stack If Changed
- Target -
*
- Execution -
-
Under the
Webhook URL - Run
heading, copy the webhook URL for this Procedure. Either set a secret unique to this specific webhook, or set nothing and it will use the default secret configured in Komodo's .env file asKOMODO_WEBHOOK_SECRET
. -
Click Save
Use the Procedure's Webhook in Gitea
Back in the Gitea:
-
Go to the settings of your Docker Compose repo, selecting the
Webhooks
tab in the sidebar, and clickAdd Webhook
choosing the type "Gitea" -
On the following screen, set these properties:
- Target URL - paste the webhook URL for your procedure from the previous section here.
- Secret - paste the webhook secret configured in the previous step here.
- Trigger On -
Push Events
- Branch filter - set to "main"
-
Click
Add Webhook
-
Once the webhook is created, you can test if it works via the
Test Delivery
button under theRecent Deliveries
section of the webhook's config page. A green checkmark is success! If its not working, make sure you are using the right secret.
If you go back to the page for your procedure in Komodo, you will see that triggering the webhook test in Gitea will have triggered the procedure to run in Komodo. Now when you merge a Pull Request from renovate into your main branch, the procedure in Komodo will run, which will pull the latest change from your repo, and then redeploy any containers that have updated image tags.
Wrapping Things Up
Well that was a lot of work, but if you made it this far it will hopefully pay off - now updating your self-hosted services is literally as easy as merging a pull request. As mentioned in the Renovate configuration section, it is very likely you may need to add some additional configuration for Renovate in your Docker Compose repo. You really won't know what oddities you will face until Renovate either opens an issue complaining about something, or opens a Pull Request for a weird tag that it thinks is an update, but isn't.
Having gotten through the process of fine tuning Renovate and Komodo, I am very pleased with how this has all turned out. Prior to this, I was using Watchtower (which seems to not be maintained anymore) to blindly update all of my containers using latest
tagged images. Being in control of my update process is a solid improvement, and that control does not come at the expense of the convenience of automation. A few weeks ago, I wouldn't have thought this kind of setup for managing updates was possible, but thanks to the magic off FOSS, it is!