Skip to content

HOWTO – Bootstrap Jenkins Controller on Control Node (ctrl-01)

Bring the control node (ctrl-01) up to a healthy Jenkins controller using Ansible roles, a Docker Engine baseline, and JCasC. At the end of this HOWTO, ctrl-01 runs a controller in Docker with a pinned plugin set and reproducible configuration, ready to orchestrate platform pipelines with work executed on agents.

Difficulty: Intermediate
Prerequisites: SSH access to ctrl-01, Ansible control machine, and a secrets method (Ansible Vault / AKV / CI variables) to provide the initial admin password.


Demo

No academy walkthrough recording is linked here yet.

Context

This HOWTO applies when:

  • ctrl-01 already has a consistent OS baseline and network reachability.
  • You want Jenkins to be the governed CI control plane that triggers platform runs.
  • You want the controller to remain lightweight (controller-only operation) and push workload to agents (Docker bootstrap now; RKE2 pod agents in steady state).

This HOWTO is aligned to:


Steps

1. Confirm baseline and connectivity

  1. Confirm Ansible connectivity:
ansible ctrl_nodes -m ping -i <inventory>
  1. Confirm OS facts (optional):
ansible ctrl_nodes -m setup -a 'filter=ansible_distribution*' -i <inventory>

Expected result:

  • Ping succeeds.
  • Target OS matches your platform baseline.

2. Apply Docker Engine baseline (ADR-0608)

Apply the Docker Engine baseline role to ensure Docker Engine and the Compose v2 plugin are present and healthy.

Example playbook:

- name: Bootstrap Docker Engine baseline on ctrl-01
  hosts: ctrl_nodes
  become: true
  gather_facts: true

  collections:
    - hybridops.common

  roles:
    - role: hybridops.common.docker_engine
      vars:
        docker_engine_users:
          - "{{ ansible_user | default('root') }}"

Run:

ansible-playbook -i <inventory> deployment/playbooks/bootstrap_docker_ctrl01.yml

Expected result on ctrl-01:

docker version
docker compose version

3. Deploy the Jenkins controller (ADR-0603)

Provide the admin password via your secret method (Vault/AKV/CI vars). Example vars:

jenkins_admin_username: "admin"
jenkins_admin_password: "{{ vault_jenkins_admin_password }}"
jenkins_enable_healthcheck: true

# Bootstrap/lab only: enable a single executor to run trivial jobs before agents exist.
jenkins_controller_num_executors: 1

Example playbook:

- name: Bootstrap Jenkins controller on ctrl-01
  hosts: ctrl_nodes
  become: true
  gather_facts: true

  collections:
    - hybridops.app

  roles:
    - role: hybridops.app.jenkins_controller

Run:

ansible-playbook -i <inventory> deployment/playbooks/bootstrap_jenkins_ctrl01.yml

Expected result:

  • Container jenkins-controller is running.
  • HTTP endpoint responds on the configured port.
  • JCasC files are rendered under the controller home and loaded at startup.

4. Validate readiness

  1. Container status:
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | rg 'jenkins-controller'
  1. HTTP endpoint:
curl -sS -o /dev/null -w "HTTP=%{http_code}\n" http://localhost:8080/login
  1. JCasC rendered files:
sudo ls -la /opt/jenkins/jenkins_home/casc_configs/
  1. Controller executor policy:

  2. Bootstrap: jenkins_controller_num_executors: 1 (temporary)

  3. Standard: set back to 0 once agents are configured

Validation

Consider this complete when:

  • Docker baseline is installed and verified (docker + docker compose).
  • Jenkins controller container is healthy and stable.
  • JCasC is loading (no Configuration as Code boot failures in logs).
  • You can authenticate to /whoAmI/api/json using admin credentials.

Suggested evidence capture:

  • docker ps output
  • docker logs --tail 200 jenkins-controller
  • HTTP probe output (curl -I / status codes)
  • The rendered casc_configs/ directory listing

Troubleshooting

  • Controller restart loop / JCasC boot failure
    Inspect logs and validate the rendered YAML:

    docker logs --tail 200 jenkins-controller
    sudo sed -n '1,200p' /opt/jenkins/jenkins_home/casc_configs/00-controller.yaml
    

  • Permissions errors under JENKINS_HOME
    Ensure ownership matches the container user:

    sudo chown -R 1000:1000 /opt/jenkins/jenkins_home
    

  • Endpoint never becomes healthy
    Increase memory (jenkins_memory) and re-check logs.


References


Maintainer: HybridOps
License: MIT-0 for code, CC-BY-4.0 for documentation