Jenkins, Configuration as Code, and Job Definition (featuring Ansible)

I have been using Jenkins on and off since it was still called Hudson - with the “off” periods being more because it didn’t need any further maintenance or changes, it just happily keeps chugging along in the background). Over that time there have been some big leaps in the approach to configuration like declarative pipeline definitions allowing programatically defined pipelines, but I had thought that the initial configuration of Jenkins was still limited to ClickOps (or using their REST API directly).

I have been working lately on digitising a bunch of DVD box sets that I have that are either not available on streaming services, or include soundtracks that have changed in what is available. Part of my workflow for this includes includes transcoding the files to reduce their file size from the raw DVD rip. For years I have used the video_transcoding project from Lisa Melton for this purpose.

While there are more dedicated tools available for this type of batch processing, I figured it was time to reintroduce Jenkins into my homelab for a bit of future experimentation.

The Initial Setup & Configuration as Code

In the Jenkins documentation there was a docker run example that I could have converted into a docker compose file, but I was being a bit lazy and jumped over to ChatGPT to get me started. This, as well as giving me what I wanted, suggested some Jenkins plugins that I had not encountered before - including the Jenkins Configuration as Code Plugin. To quote it’s documentation:

The ‘as code’ paradigm is about being able to reproduce and/or restore a full environment within minutes based on recipes and automation, managed as code.

Manage configuration as human-readable config file(s) Setting up Jenkins is a complex process, as both Jenkins and its plugins require some tuning and configuration, with dozens of parameters to set within the web UI manage section.

Jenkins Configuration as Code provides the ability to define this whole configuration as a simple, human-friendly, plain text yaml syntax. Without any manual steps, this configuration can be validated and applied to a Jenkins controller in a fully reproducible way. With JCasC, setting up a new Jenkins controller will become a no-brainer event.

I always aim for easily reproducible setups, so this is perfect for my setup.

The initial configuration files ChatGPT gave me were not entirely correct, but it gave me enough of a pointer that I could look for the right information.

At present my Configuration-as-Code directory contains files to configure:

  1. Using ephemeral Docker containers for build nodes
  2. SSO authentication using Authentik and OpenID Connect (I have not got to role based authentication yet, so it’s pretty wide open once you are authenticated)
  3. The URL Jenkins is running at
  4. Some pipeline jobs which are static, as in their pipeline definition is set at the point I configure Jenkins and do not come from a source control repository of their own

This fourth point had some interesting challenges that I thought was worth sharing.

» Continue reading


Creating an "On Call" Light with Toggl and Home Assistant

For years I have relied on Toggl Track to track where my time is being used and stay focused while working. As part of this I categorise my work items using tags such as “doing,” “supporting,” and “meeting”. I started doing this to help me reflect on how much time I am spending in different areas and try to ensure I have a balance.

At the moment I usually split my work week between a couple of days in the office and the remainder at home. There are other people in my home, however, and if I’m not talking on a call it can be hard to tell from outside my office if I’m on a call or not - so it was time to work out how to combine this Toggl time tracking with a “On Call” indicator.

There are a few moving parts to this

  • Software
    • Toggl Track - I use the free plan currently (although they have just recently announced some changes to their usage limits for APIs and Web Hooks - hopefully I’m still under the limits, but time will tell)
    • Home Assistant - I use this for all my home automation, so it would seem a natural place to add this “On Call” automation
    • Home Assistant Cloud - Nabu Casa - I subscribe to this to help fund the development of Home Assistant. It has a couple of added benefits like making Alexa integration easier as well as Web Hooks (which we’ll use later). This is not required to get any of these features working, it just makes it easier and moves it to being a managed service as opposed to something I need to configure and maintain myself
  • Hardware
    • M5Stack ATOM Matrix ESP32 Development Kit - I had one of these lying around that I had purchased because it looked interesting then never found a use for it. It is an ESP32 based device including an addressable 5x5 LED display. Any smart light with colour control would suit this automation, this is just what I had on hand. My ESPHome configuration for this device is available here.

The Setup

The core pieces we need to configure are:

  • A few Input Helpers in Home Assistant to hold our state
  • A Home Assistant automation that is configured to take input from a Web Hook A second Home Assistant automation that is configured to respond to the state parsed from the Web Hook input
  • A script to configure Toggl with the Web Hook destination

» Continue reading


EJB3, Stateful Firewalls, and Connection Timeouts with Payara

Recently I encountered an interesting and frustrating problem when troubleshooting an EJB3 connection issue between two Payara server instances. After a change in firewall vendor, we noticed that remote EJB3 calls would start hanging after a period of inactivity. Initially it seemed like an intermittent issue - but the deeper I looked, the more it felt like it never should have worked before the move either - a lovely Schroedinbug.

In this post, I’ll walk through the issue, why it happened, and the solution that ultimately fixed it.

The Problem

Part of our system uses remote EJB3 calls between two Payara instances, communicating over a network with a stateful firewall in between. After the firewall vendor change, we started seeing EJB3 calls hang indefinitely if the connection had been idle for some time. This would appear to hang indefinitely, or at least until the ORB response timeout setting was hit (this is not configured by default though, and may not be a fast timeout depending on your workload). This in turn can tie up threads and make it difficult to track down the root cause of where the system pressure is coming from.

Here’s a simple view of the setup:

Architecture Diagram

» Continue reading


Avoiding passlib Dependency in Ansible When Generating Traefik User Files

I use Ansible to manage my server configuration, including copying over version-controlled docker-compose files and application configuration. One such configuration is for Traefik, where I maintain a user file for HTTP basic authentication. These credentials are sourced from 1Password at deploy time and stored in a format compatible with htpasswd.

The Original Approach

Previously, I used the ansible.builtin.htpasswd module like so:


    - name: Create traefik user file
      ansible.builtin.htpasswd:
        path: "{{ traefik_conf_dir }}/usersfile"
        name: "{{ lookup('onepassword', 'Traefik', field='username') }}"
        password: "{{ lookup('onepassword', 'Traefik', field='password') }}"
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: 0644

However, after upgrading to TrueNAS 25.04 “Fangtooth”, this failed with the following error:


TASK [Create traefik user file] ********************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ModuleNotFoundError: No module named 'passlib'
fatal: [truenas]: FAILED! => {"changed": false, "msg": "Failed to import the required Python library (passlib) on truenas's Python /usr/bin/python3.11. Please read the module documentation and install it in the appropriate location. If the required library is installed, but Ansible is using the wrong Python interpreter, please consult the documentation on ansible_python_interpreter"}

This module requires the passlib library, which wasn’t available on the system. I didn’t want to modify the system Python installation just to resolve this, especially on an appliance OS like TrueNAS.

» Continue reading


LLMs on a Homelab Without a GPU? Here's What I Found

I’ve recently been experimenting more with the power of Large Language Models (LLMs) and how they can supercharge my productivity. This especially has helped with the problem of a blank page wondering where to start writing – I can dump my unsorted thoughts into a chatbot and get a skeleton of a document or implementation in a few minutes. It’s by no means perfect what comes out, but it’s (usually) a very good start. I’ve been playing with both ChatGPT, and a series of local models running on my laptop with Ollama, Fabric, and OpenWebUI.

I wanted to be able to continue to use some of these self-hosted models while I’m out on the go, so I wondered - could I host these on my homelab server? My main homelab server runs an AMD Ryzen 5600G processor which includes an integrated GPU. Can this be used to improve the performance of a local model when I do not have a dedicated GPU available?

That doesn’t seem possible right now though, at least not in an efficient way. This post is a point-in-time snapshot of what I found when looking in to this.

» Continue reading