Skip to content
Latchkey

CI workflow (kyl191/ansible-role-openvpn)

The CI workflow from kyl191/ansible-role-openvpn, explained and optimized by Latchkey.

C

CI health: C - fair

Point runs-on at Latchkey and get run de-duplication, job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: kyl191/ansible-role-openvpn.github/workflows/ci.ymlLicense MITView source

What it does

This is the CI workflow from the kyl191/ansible-role-openvpn repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
---
name: CI

on: # yamllint disable-line rule:truthy
  push:
    branches:
      - master
  pull_request:
    branches:
      - master

jobs:
  check-syntax:
    runs-on: ubuntu-latest
    name: check-syntax
    steps:
      - uses: actions/checkout@v4
      - name: Run ansible-lint
        uses: ansible/ansible-lint@main
        with:
          requirements_file: requirements.yml

  build-rhel-legacy:
    runs-on: ubuntu-latest
    name: rhel-legacy-${{ matrix.version }}
    needs:
      - check-syntax
    strategy:
      fail-fast: false
      matrix:
        version:
          - "almalinux:8"
          - "rockylinux:8"

    container:
      image: diodonfrost/ansible-${{ matrix.version }}
      env:
        container: docker
      volumes:
        - /sys/fs/cgroup:/sys/fs/cgroup
        - ${{ github.workspace }}:/etc/ansible/roles/kyl191.openvpn
      options: "--cap-add NET_ADMIN --cap-add SYS_ADMIN --device /dev/net/tun"
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Update ansible
        run: dnf install -y python39 && pip3.9 install -U ansible
      - name: Install required dependencies from Ansible Galaxy
        run: ansible-galaxy install -r /etc/ansible/roles/kyl191.openvpn/requirements.yml
      - name: Make sure ansible connection is sane
        run: ansible -m setup -c local -i 127.0.0.1, all
      - name: Run ansible playbook
        run: >
          ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/test.yml -vv
          -e '{"openvpn_use_tls_crypt": false}'
      - name: Check idempotency
        run: >
          ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/test.yml -vv
          -e '{"openvpn_use_tls_crypt": false}'
      - name: Container state debug output
        continue-on-error: true
        run: |
          ls -lR /etc/openvpn
          echo "cat openvpn_udp_1194.conf"
          find /etc/openvpn/ -maxdepth 3 -name openvpn_udp_1194.conf -type f -exec cat {} \;
          echo "cat alpha-*.ovpn"
          find /etc/openvpn/ -maxdepth 3 -name "alpha-*.ovpn" -type f -exec cat {} \;

  build-debian-like:
    runs-on: ubuntu-latest
    name: debian-like-${{ matrix.version }}
    needs:
      - check-syntax
    strategy:
      fail-fast: false
      matrix:
        version:
          - "ubuntu:22.04"
          - "debian:12"

    container:
      image: diodonfrost/ansible-${{ matrix.version }}
      env:
        container: docker
      volumes:
        - /sys/fs/cgroup:/sys/fs/cgroup
        - ${{ github.workspace }}:/etc/ansible/roles/kyl191.openvpn
      options: "--cap-add NET_ADMIN --cap-add SYS_ADMIN --device /dev/net/tun"
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Upgrade ansible
        run: apt update && apt-get install --only-upgrade ansible -y
      - name: Install required dependencies from Ansible Galaxy
        run: ansible-galaxy install -r /etc/ansible/roles/kyl191.openvpn/requirements.yml
      - name: Make sure ansible connection is sane
        run: ansible -m setup -c local -i 127.0.0.1, all
      - name: Run ansible playbook
        run: ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/test.yml -vv -e openvpn_firewall=iptables
      - name: Check idempotency
        run: ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/test.yml -vv -e openvpn_firewall=iptables
      - name: Container state debug output
        continue-on-error: true
        run: |
          ls -lR /etc/openvpn
          echo "cat openvpn_udp_1194.conf"
          find /etc/openvpn/ -maxdepth 3 -name openvpn_udp_1194.conf -type f -exec cat {} \;
          echo "cat alpha-*.ovpn"
          find /etc/openvpn/ -maxdepth 3 -name "alpha-*.ovpn" -type f -exec cat {} \;

  build-systemd:
    runs-on: ubuntu-latest
    name: ${{ matrix.version }}
    needs:
      - check-syntax
    strategy:
      fail-fast: false
      matrix:
        version:
          - "fedora-42"
          - "fedora-43"
          - "almalinux-9"
          - "almalinux-10"
          - "rockylinux-9"
          - "rockylinux-10"
          - "centos-stream9"
          - "centos-stream10"
          - "debian-13"
          - "ubuntu-24.04"
          - "ubuntu-25.10"
          # - "ubi9-init"
          # - "ubi10-init"
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Login to GHCR
        run: echo "${{ secrets.GITHUB_TOKEN }}" | sudo podman login ghcr.io -u ${{ github.actor }} --password-stdin
      - name: Create container
        run: sudo podman create --name ${{ matrix.version }} --privileged --cap-add NET_ADMIN --cap-add SYS_ADMIN --device /dev/net/tun --cgroupns=host --network=bridge --systemd=always --volume=${GITHUB_WORKSPACE}:/etc/ansible/roles/kyl191.openvpn --user=root ghcr.io/kyl191/ansible-images:${{ matrix.version }}
      - name: Start container
        run: sudo podman start ${{ matrix.version }}
      # https://www.jeffgeerling.com/blog/2020/resolving-fedora-dnf-error-no-such-file-or-directory-varlibdnfrpmdblockpid
      # Add '|| true' so the CI doesn't exit if the second command fails
      - name: Wait for container to start
        run: |
          until sudo podman exec ${{ matrix.version }} systemctl is-active --quiet multi-user.target; do
            echo "Waiting for systemd... Current failed units:"
            sudo podman exec ${{ matrix.version }} systemctl list-units --failed --no-legend --no-pager 2>/dev/null || true
            sleep 5
          done
        timeout-minutes: 5
      - name: Install required dependencies from Ansible Galaxy
        run: sudo podman exec ${{ matrix.version }} ansible-galaxy install -r /etc/ansible/roles/kyl191.openvpn/requirements.yml
      - name: Make sure ansible connection is sane
        run: sudo podman exec ${{ matrix.version }} ansible -m setup -c local -i 127.0.0.1, all
      - name: Run ansible playbook
        run: >
          sudo podman exec ${{ matrix.version }} ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/test.yml -vv -e '{"openvpn_ci_build": false}'
      - name: Check idempotency
        run: >
          sudo podman exec ${{ matrix.version }} ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/test.yml -vv -e '{"openvpn_ci_build": false}'
      - name: Move generated client config file
        run: sudo podman exec ${{ matrix.version }} cp /etc/openvpn/server/alpha-localhost.ovpn /etc/openvpn/client/alpha-localhost.conf
      - name: Attempt openvpn connection
        run: sudo podman exec ${{ matrix.version }} systemctl start openvpn-client@alpha-localhost.service
      - name: Wait for a bit
        run: sleep 5
      - name: Stop openvpn connection
        run: sudo podman exec ${{ matrix.version }} systemctl stop openvpn-client@alpha-localhost.service
      - name: Check revocation
        run: >
          sudo podman exec ${{ matrix.version }} ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/revocation-test.yml -vv -e '{"openvpn_ci_build": false}'
      - name: Container state debug output
        continue-on-error: true
        run: |
          sudo podman exec ${{ matrix.version }} ls -lR /etc/openvpn
          echo "cat openvpn_udp_1194.conf"
          sudo podman exec ${{ matrix.version }} find /etc/openvpn/ -maxdepth 3 -name openvpn_udp_1194.conf -type f -exec cat {} \;
          echo "cat alpha-*.ovpn"
          sudo podman exec ${{ matrix.version }} find /etc/openvpn/ -maxdepth 3 -name "alpha-*.ovpn" -type f -exec cat {} \;
          echo cat /var/log/openvpn.log
          sudo podman exec ${{ matrix.version }} cat /var/log/openvpn.log
          echo journalctl -u openvpn-client@alpha-localhost.service
          sudo podman exec ${{ matrix.version }} journalctl -u openvpn-client@alpha-localhost.service

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

---
name: CI
 
on: # yamllint disable-line rule:truthy
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
jobs:
  check-syntax:
    timeout-minutes: 30
    runs-on: latchkey-small
    name: check-syntax
    steps:
      - uses: actions/checkout@v4
      - name: Run ansible-lint
        uses: ansible/ansible-lint@main
        with:
          requirements_file: requirements.yml
 
  build-rhel-legacy:
    timeout-minutes: 30
    runs-on: latchkey-small
    name: rhel-legacy-${{ matrix.version }}
    needs:
      - check-syntax
    strategy:
      fail-fast: false
      matrix:
        version:
          - "almalinux:8"
          - "rockylinux:8"
 
    container:
      image: diodonfrost/ansible-${{ matrix.version }}
      env:
        container: docker
      volumes:
        - /sys/fs/cgroup:/sys/fs/cgroup
        - ${{ github.workspace }}:/etc/ansible/roles/kyl191.openvpn
      options: "--cap-add NET_ADMIN --cap-add SYS_ADMIN --device /dev/net/tun"
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Update ansible
        run: dnf install -y python39 && pip3.9 install -U ansible
      - name: Install required dependencies from Ansible Galaxy
        run: ansible-galaxy install -r /etc/ansible/roles/kyl191.openvpn/requirements.yml
      - name: Make sure ansible connection is sane
        run: ansible -m setup -c local -i 127.0.0.1, all
      - name: Run ansible playbook
        run: >
          ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/test.yml -vv
          -e '{"openvpn_use_tls_crypt": false}'
      - name: Check idempotency
        run: >
          ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/test.yml -vv
          -e '{"openvpn_use_tls_crypt": false}'
      - name: Container state debug output
        continue-on-error: true
        run: |
          ls -lR /etc/openvpn
          echo "cat openvpn_udp_1194.conf"
          find /etc/openvpn/ -maxdepth 3 -name openvpn_udp_1194.conf -type f -exec cat {} \;
          echo "cat alpha-*.ovpn"
          find /etc/openvpn/ -maxdepth 3 -name "alpha-*.ovpn" -type f -exec cat {} \;
 
  build-debian-like:
    timeout-minutes: 30
    runs-on: latchkey-small
    name: debian-like-${{ matrix.version }}
    needs:
      - check-syntax
    strategy:
      fail-fast: false
      matrix:
        version:
          - "ubuntu:22.04"
          - "debian:12"
 
    container:
      image: diodonfrost/ansible-${{ matrix.version }}
      env:
        container: docker
      volumes:
        - /sys/fs/cgroup:/sys/fs/cgroup
        - ${{ github.workspace }}:/etc/ansible/roles/kyl191.openvpn
      options: "--cap-add NET_ADMIN --cap-add SYS_ADMIN --device /dev/net/tun"
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Upgrade ansible
        run: apt update && apt-get install --only-upgrade ansible -y
      - name: Install required dependencies from Ansible Galaxy
        run: ansible-galaxy install -r /etc/ansible/roles/kyl191.openvpn/requirements.yml
      - name: Make sure ansible connection is sane
        run: ansible -m setup -c local -i 127.0.0.1, all
      - name: Run ansible playbook
        run: ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/test.yml -vv -e openvpn_firewall=iptables
      - name: Check idempotency
        run: ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/test.yml -vv -e openvpn_firewall=iptables
      - name: Container state debug output
        continue-on-error: true
        run: |
          ls -lR /etc/openvpn
          echo "cat openvpn_udp_1194.conf"
          find /etc/openvpn/ -maxdepth 3 -name openvpn_udp_1194.conf -type f -exec cat {} \;
          echo "cat alpha-*.ovpn"
          find /etc/openvpn/ -maxdepth 3 -name "alpha-*.ovpn" -type f -exec cat {} \;
 
  build-systemd:
    timeout-minutes: 30
    runs-on: latchkey-small
    name: ${{ matrix.version }}
    needs:
      - check-syntax
    strategy:
      fail-fast: false
      matrix:
        version:
          - "fedora-42"
          - "fedora-43"
          - "almalinux-9"
          - "almalinux-10"
          - "rockylinux-9"
          - "rockylinux-10"
          - "centos-stream9"
          - "centos-stream10"
          - "debian-13"
          - "ubuntu-24.04"
          - "ubuntu-25.10"
          # - "ubi9-init"
          # - "ubi10-init"
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Login to GHCR
        run: echo "${{ secrets.GITHUB_TOKEN }}" | sudo podman login ghcr.io -u ${{ github.actor }} --password-stdin
      - name: Create container
        run: sudo podman create --name ${{ matrix.version }} --privileged --cap-add NET_ADMIN --cap-add SYS_ADMIN --device /dev/net/tun --cgroupns=host --network=bridge --systemd=always --volume=${GITHUB_WORKSPACE}:/etc/ansible/roles/kyl191.openvpn --user=root ghcr.io/kyl191/ansible-images:${{ matrix.version }}
      - name: Start container
        run: sudo podman start ${{ matrix.version }}
      # https://www.jeffgeerling.com/blog/2020/resolving-fedora-dnf-error-no-such-file-or-directory-varlibdnfrpmdblockpid
      # Add '|| true' so the CI doesn't exit if the second command fails
      - name: Wait for container to start
        run: |
          until sudo podman exec ${{ matrix.version }} systemctl is-active --quiet multi-user.target; do
            echo "Waiting for systemd... Current failed units:"
            sudo podman exec ${{ matrix.version }} systemctl list-units --failed --no-legend --no-pager 2>/dev/null || true
            sleep 5
          done
        timeout-minutes: 5
      - name: Install required dependencies from Ansible Galaxy
        run: sudo podman exec ${{ matrix.version }} ansible-galaxy install -r /etc/ansible/roles/kyl191.openvpn/requirements.yml
      - name: Make sure ansible connection is sane
        run: sudo podman exec ${{ matrix.version }} ansible -m setup -c local -i 127.0.0.1, all
      - name: Run ansible playbook
        run: >
          sudo podman exec ${{ matrix.version }} ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/test.yml -vv -e '{"openvpn_ci_build": false}'
      - name: Check idempotency
        run: >
          sudo podman exec ${{ matrix.version }} ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/test.yml -vv -e '{"openvpn_ci_build": false}'
      - name: Move generated client config file
        run: sudo podman exec ${{ matrix.version }} cp /etc/openvpn/server/alpha-localhost.ovpn /etc/openvpn/client/alpha-localhost.conf
      - name: Attempt openvpn connection
        run: sudo podman exec ${{ matrix.version }} systemctl start openvpn-client@alpha-localhost.service
      - name: Wait for a bit
        run: sleep 5
      - name: Stop openvpn connection
        run: sudo podman exec ${{ matrix.version }} systemctl stop openvpn-client@alpha-localhost.service
      - name: Check revocation
        run: >
          sudo podman exec ${{ matrix.version }} ansible-playbook /etc/ansible/roles/kyl191.openvpn/tests/revocation-test.yml -vv -e '{"openvpn_ci_build": false}'
      - name: Container state debug output
        continue-on-error: true
        run: |
          sudo podman exec ${{ matrix.version }} ls -lR /etc/openvpn
          echo "cat openvpn_udp_1194.conf"
          sudo podman exec ${{ matrix.version }} find /etc/openvpn/ -maxdepth 3 -name openvpn_udp_1194.conf -type f -exec cat {} \;
          echo "cat alpha-*.ovpn"
          sudo podman exec ${{ matrix.version }} find /etc/openvpn/ -maxdepth 3 -name "alpha-*.ovpn" -type f -exec cat {} \;
          echo cat /var/log/openvpn.log
          sudo podman exec ${{ matrix.version }} cat /var/log/openvpn.log
          echo journalctl -u openvpn-client@alpha-localhost.service
          sudo podman exec ${{ matrix.version }} journalctl -u openvpn-client@alpha-localhost.service
 

What changed

1 third-party action is referenced by a movable tag. Pin it to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.

What Latchkey heals here

This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:

This workflow runs 4 jobs (16 with the matrix expanded) per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow