Over the last few months I have been slowly adding features to my home setup (I don’t call it a home lab). I have several services I use at home like Home Assistant, Zigbee2mqtt, ESPHome dashboard and many more, running on a K3s node (mini pc with core-i3 and 16GB of RAM). Everything is defined in yaml manifests stored in a private Github repo and reconciled in the Kubernetes cluster by ArgoCD.
Like a lot of self-hosters, one of the things I run is Homepage dashboard. It is a great Open-Source dashboard project with yaml configuration, making it perfect for my Gitops setup.
I also use Renovate configured as a weekly cronjob to automatically open pull requests in my repo with the new image tags of the containers I use. This setup is great because I don’t need to think about upgrades, I just check the PRs every Sunday and merge them if I’m happy with the changelogs. Note that this blog doesn’t explain how to setup Renovate.
Running Renovate weekly is perfectly fine for my use case but, now and again, I want to run it manually for various reasons. The simple way to do it is to open my terminal, switch to the right kube context and create a new job from the existing cronjob (kubectl create job --from=cronjob/renovate manualrun -n renovate
). This is perfectly fine but, being as lazy as I am, I wanted a button to click on my dashboard to start Renovate.
There are various ways to do this, like OliveTin, which is a web ui to start shell commands. While the project looks interesting, I wasn’t too keen on giving Kubernetes API access to it or running commands on the node. As a result, I decided on using Argo Events with a simple webhook.
What is a webhook?
Webhooks are used everywhere, it means that something exposes an http endpoint, and when accessed on a specific path, an action is triggered. For instance, you could create a webhook in Home Assistant to turn on a light when accessed. So when you run curl https://homeassistant.int.vxav.fr/turnonthelights
, the web server picks up the request and triggers the action.
This is what I am going to use here with Argo Events. The idea is to create a Renovate pod when I access argo-events.int.vxav.fr/renovate
, which I can simple add as a button on my dashboard.
Argo Events
Argo Events lets you run triggers (Kubernetes resources, service messages and many more) based on an events (Github comment, release, webhook, schedule and many more). The Sensor
Custom Resource (CR) is what will link Triggers
and Events
. The same thing could be achieved with Tekton pipelines which work great (we automate our E2E testing at Giant Swarm with it), but I thought I’d stick with Argo since I already use ArgoCD for Gitops.
This was the super short summary of Argo Events but you can learn more about the components in the documentation.
Note that I am not using Argo Workflows here, only Argo Events. Argo Workflows can be triggered by Argo Events and are very powerful workflow engine for Kubernetes but way overkill for my simple use case.
Installation of Argo Events via ArgoCD
There are several ways to install Argo Events (manifests, kustomize or community maintained Helm chart). I am going to use the upstream kustomization to install all the components, you can find the other procedures in the documentation.
Because I use ArgoCD to deploy resources to my clusters, I will be printing the manifests I have in my Gitops repo for Argo Events and the ArgoCD app for it.
ArgoCD app
Having the ArgoCD app defined in the repo saves you from manually creating it in the UI. In my case, the file is stored under /gitops/argocd/apps/argo-events.yaml
:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: argo-events
namespace: argocd
spec:
destination:
namespace: argo-events
server: https://kubernetes.default.svc
project: default
source:
path: gitops/argo-events
repoURL: [email protected]:vxav/home-automation.git
targetRevision: main
syncPolicy:
automated:
prune: true
selfHeal: true
Argo Events controllers and other components
I decided to use the upstream kustomization because I am not too keen on using community maintained Helm charts as these tend to become obsolete over time, and copying all the manifests in my repo isn’t ideal for staying up to date. In my case, the structure looks like so:
└── gitops/
└── argo-events/
├── event-source.yaml
├── ingress.yaml
├── kustomization.yaml
├── namespace.yaml
└── sensor-renovate.yaml
- kustomization.yaml
The kustomization will apply the upstream manifests containing everything you need to run Argo Events, along with the other yaml files I created in the directory.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: argo-events
resources:
- github.com/argoproj/argo-events/manifests/cluster-install
- namespace.yaml
- sensor-renovate.yaml
- ingress.yaml
- event-source.yaml
- namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
labels:
kubernetes.io/metadata.name: argo-events
name: argo-events
- event-source.yaml
The event source is how we create the webhook to listen on. In this case it will create a pod listening on port 12000 with a webhook at /renovate
and GET
method. So a simple curl command to the pod would return success
. At this point the service is only accessible within the Kubernetes cluster so we’ll need an ingress to be able to consume it from the LAN (next step).
apiVersion: argoproj.io/v1alpha1
kind: EventSource
metadata:
name: webhook
spec:
service:
ports:
- port: 12000
targetPort: 12000
webhook:
renovate:
port: "12000"
endpoint: /renovate
method: GET
- ingress.yaml
My ingress is deployed as a Traefik IngressRoute custom resource (CR). The implementation will vary depending on your environment. The end result here is that I will be able to hit the renovate
Argo Events webhook by simply browsing to argo-events.int.vxav.fr/renovate
In my case I use external-dns to create a CNAME record (argo-events.int.vxav.fr
) that points to an A record (ingress.int.vxav.fr
) which resolves to the IP of the service of type load balancer serving the ingress controller.
I also add a certificate CR from cert-manager to have https but I didn’t include it here for better readability. I think there are ways to handle lets encrypt certificates within Traefik itself without cert-manager but I haven’t gotten around looking into this yet.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
annotations:
external-dns.alpha.kubernetes.io/target: ingress.int.vxav.fr
kubernetes.io/ingress.class: traefik
name: argo-events
namespace: argo-events
labels:
app: argo-events
spec:
entryPoints:
- websecure
routes:
- match: Host(`argo-events.int.vxav.fr`)
kind: Rule
services:
- kind: Service
name: webhook-eventsource-svc
namespace: argo-events
port: 12000
tls:
secretName: argo-events-cert
- sensor-renovate.yaml
The sensor instructs what Kubernetes resource to create when a specific dependency is accessed (webhook in our case).
As you can see, the eventSourceName
and eventName
point to the event source and webhook we created earlier. In the service account section, I used the embedded argo-events one as it has enough permissions (and it’s one less thing to create). Finally, the Trigger
section contains the definition of the pod to create. Note that the pod name
spec has been replaced with generateName
to get an automatically generated unique name.
The rest of the pod definition is essentially a copy of my cronjob.
As a first step, you can change this to a generic random pod for testing purpose.
apiVersion: argoproj.io/v1alpha1
kind: Sensor
metadata:
name: webhook
spec:
template:
serviceAccountName: argo-events-sa
dependencies:
- name: webhook-renovate
eventSourceName: webhook
eventName: renovate
triggers:
- template:
name: start-renovate
k8s:
operation: create
source:
resource:
apiVersion: v1
kind: Pod
metadata:
generateName: renovate-webhooked-
namespace: renovate
spec:
containers:
- env:
- name: LOG_LEVEL
value: debug
- name: RENOVATE_REVIEWERS
value: vxav
- name: RENOVATE_PLATFORM
value: github
- name: RENOVATE_AUTODISCOVER
value: "false"
- name: RENOVATE_BASE_DIR
value: /tmp/renovate/
- name: RENOVATE_CONFIG_FILE
value: /opt/renovate/config.json
- name: RENOVATE_TOKEN
valueFrom:
secretKeyRef:
key: pat
name: renovate-gh-pat
image: renovate/renovate:37.267.1
imagePullPolicy: Always
name: renovate
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /opt/renovate/
name: config-volume
- mountPath: /tmp/renovate/
name: work-volume
dnsPolicy: ClusterFirst
restartPolicy: Never
schedulerName: default-scheduler
terminationGracePeriodSeconds: 30
volumes:
- configMap:
defaultMode: 420
name: renovate-config
name: config-volume
- name: work-volume
ArgoCD reconciliation
Once you merge all these into the repo, ArgoCD will create the Argo App.
which will then apply the resources in /gitops/argo-events/
and install everything for you.
Once you observed that everything has been created successfully in the Kubernetes cluster, you can check that it works by accessing the webhook and it should start a Renovate pod.
curl https://argo-events.int.vxav.fr/renovate
success
This should start the Renovate pod in Kubernetes which will perhaps result in new PRs on the repo if new image tags are available.
> kubectl get pod -n renovate --watch
NAME READY STATUS RESTARTS AGE
renovate-webhooked-g97hc 1/1 Running 0 118s
renovate-webhooked-g97hc 0/1 Completed 0 4m31s
Adding a button in Homepage dashboard
Again, you may or may not be using Homepage but I’ll go ahead and add it here anyway. Just generally speaking, since you can trigger the Renovate action by browsing to the specific url, you can add it wherever you will need it.
In the case of Homepage, I stored the Renovate logo in my public repo and the config is in my services.yaml
:
- App Management:
...
- Renovate:
icon: https://avatars3.githubusercontent.com/u/38656520?s=400&v=4
href: https://argo-events.int.vxav.fr/renovate
description: Run Renovate (Argo Event webhook)
And this is what it looks like on the dashboard. It will now start a Renovate pod when I click on that button.
Wrap up
As you can imagine, this is a pretty simple use case of using Argo Events at home to make my life a tiny bit easier but I can think of a lot of possibilities for this tool in the professional space, especially when paired with Argo Worflows or Tekton Pipelines.
I am yet to find more uses for this in my home setup but adding webhooks and triggers will now be straightforward. I can then add the interface to homepage, home assistant, a function in my zshrc profile. The possibilities for triggers are also very wide as you can craft whatever container you like to run specific actions.