← Deploying Rundeck the Right Way

Chapter 5

Ansible Integration

In this chapter
<nav id="TableOfContents" aria-label="Chapter sections"> <ul> <li><a href="#installing-ansible-on-the-rundeck-host">Installing Ansible on the Rundeck Host</a></li> <li><a href="#the-ansible-plugin">The Ansible Plugin</a> <ul> <li><a href="#checking-if-the-plugin-is-already-installed">Checking If the Plugin Is Already Installed</a></li> </ul> </li> <li><a href="#ssh-key-setup">SSH Key Setup</a> <ul> <li><a href="#generate-an-ssh-keypair">Generate an SSH Keypair</a></li> <li><a href="#prepare-managed-nodes">Prepare Managed Nodes</a></li> <li><a href="#configure-passwordless-sudo">Configure Passwordless Sudo</a></li> <li><a href="#test-the-connection">Test the Connection</a></li> </ul> </li> <li><a href="#creating-a-rundeck-project">Creating a Rundeck Project</a> <ul> <li><a href="#create-the-project-via-web-ui">Create the Project via Web UI</a></li> <li><a href="#configure-the-ansible-resource-model-source">Configure the Ansible Resource Model Source</a></li> <li><a href="#set-up-the-inventory-file">Set Up the Inventory File</a></li> <li><a href="#create-an-ansible-config-file">Create an Ansible Config File</a></li> <li><a href="#verify-node-discovery">Verify Node Discovery</a></li> </ul> </li> <li><a href="#node-executor-vs-workflow-step">Node Executor vs. Workflow Step</a></li> <li><a href="#your-first-rundeck-job">Your First Rundeck Job</a> <ul> <li><a href="#create-the-job">Create the Job</a></li> <li><a href="#run-the-job">Run the Job</a></li> </ul> </li> <li><a href="#verification">Verification</a></li> <li><a href="#what-can-go-wrong">What Can Go Wrong</a> <ul> <li><a href="#rundeck-shows-zero-nodes">Rundeck Shows Zero Nodes</a></li> <li><a href="#ansible-playbook-command-not-found">&ldquo;ansible-playbook: command not found&rdquo;</a></li> <li><a href="#ssh-connections-time-out">SSH Connections Time Out</a></li> <li><a href="#plugin-version-mismatch-after-rundeck-upgrade">Plugin Version Mismatch After Rundeck Upgrade</a></li> </ul> </li> </ul> </nav>

What you’ll accomplish: Install Ansible on the Rundeck host, configure the Ansible plugin, import your inventory as Rundeck nodes, set up SSH key-based authentication to managed hosts, and run your first Ansible playbook through Rundeck’s web UI.

At this point you have a working Rundeck instance backed by MySQL and fronted by Apache. That’s useful on its own — you can define jobs, run scripts, schedule tasks. But the real power for a home lab comes from connecting Rundeck to Ansible. Instead of SSH-ing into your workstation and running ansible-playbook from the command line, you’ll trigger playbooks from a browser, schedule them, and see full execution history.

This chapter bridges those two worlds.


Installing Ansible on the Rundeck Host

Rundeck doesn’t invoke Ansible remotely — it runs it locally on the Rundeck server. That means Ansible needs to be installed on the same host.

sudo dnf install -y ansible-core

Verify:

ansible --version

You should see ansible [core 2.x.x] with a config file path and Python version. The exact version doesn’t matter much — anything from Rocky 9’s AppStream repository will work with the Rundeck Ansible plugin.


The Ansible Plugin

The Ansible plugin for Rundeck provides five capabilities:

  1. Resource Model Source — Imports hosts from your Ansible inventory into Rundeck’s node list, including facts as node attributes.
  2. Node Executor — Executes commands on individual nodes via Ansible (replaces Rundeck’s built-in SSH executor).
  3. File Copier — Copies files to nodes using Ansible.
  4. Playbook Workflow Step — Runs an Ansible playbook as a job step. This is the one you’ll use most.
  5. Module Workflow Step — Runs a single ad-hoc Ansible module.

Checking If the Plugin Is Already Installed

Recent Rundeck versions may ship with the Ansible plugin included. Check:

ls /var/lib/rundeck/libext/ | grep -i ansible

If you see a JAR file like ansible-plugin-x.x.x.jar, you’re set. If not, download it:

# Replace the version with the latest from https://github.com/rundeck-plugins/ansible-plugin/releases
sudo curl -L -o /var/lib/rundeck/libext/ansible-plugin-3.2.10.jar \
  https://github.com/rundeck-plugins/ansible-plugin/releases/download/v3.2.10/ansible-plugin-3.2.10.jar

sudo chown rundeck:rundeck /var/lib/rundeck/libext/ansible-plugin-3.2.10.jar

Restart Rundeck after adding the plugin:

sudo systemctl restart rundeckd

SSH Key Setup

Before Rundeck can manage your nodes through Ansible, the rundeck system user needs passwordless SSH access to each managed host. This is non-negotiable — Ansible communicates over SSH, and you don’t want Rundeck prompting for passwords.

Generate an SSH Keypair

# Generate an ed25519 key with no passphrase
sudo -u rundeck ssh-keygen -t ed25519 -f /var/lib/rundeck/.ssh/id_ed25519 -N ""

Verify the key was created:

sudo ls -la /var/lib/rundeck/.ssh/

You should see id_ed25519 (private key, permissions 0600) and id_ed25519.pub (public key).

Prepare Managed Nodes

On each node Rundeck will manage, you need to:

  1. Create a rundeck service user.
  2. Deploy the SSH public key.
  3. Configure passwordless sudo for the commands Rundeck will run.

Here’s how to do it manually for one node. The bundled playbook’s node-setup.yml automates this across all your hosts.

# On the MANAGED NODE (not the Rundeck server):

# Create the rundeck user with a locked password (no direct login)
sudo useradd -m -s /bin/bash -G wheel rundeck
sudo passwd -l rundeck

# Create .ssh directory
sudo mkdir -p /home/rundeck/.ssh
sudo chmod 700 /home/rundeck/.ssh
sudo chown rundeck:rundeck /home/rundeck/.ssh

# Add the Rundeck server's public key
# Copy the content of /var/lib/rundeck/.ssh/id_ed25519.pub from your Rundeck server
echo "ssh-ed25519 AAAA... rundeck@rundeck.example.com" | \
  sudo tee /home/rundeck/.ssh/authorized_keys
sudo chmod 600 /home/rundeck/.ssh/authorized_keys
sudo chown rundeck:rundeck /home/rundeck/.ssh/authorized_keys

Configure Passwordless Sudo

Create a sudoers file that grants the rundeck user passwordless access to specific commands:

sudo visudo -f /etc/sudoers.d/rundeck
# /etc/sudoers.d/rundeck
# Allow rundeck user to run specific commands without a password
rundeck ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart *, /usr/bin/systemctl status *, /usr/bin/dnf update *

Warning: Scope the sudo permissions to what Rundeck actually needs. Granting ALL commands with NOPASSWD is convenient but dangerous. If Rundeck is compromised, the attacker has root on every managed node. Start narrow and widen only when a job requires it.

Test the Connection

Back on the Rundeck server, test SSH as the rundeck user:

sudo -u rundeck ssh 192.168.1.51 hostname

If this doesn’t work, nothing else in this chapter will work. Common issues:

  • Wrong permissions on the private key (must be 0600, owned by rundeck)
  • Public key not deployed correctly on the target
  • Firewall blocking SSH on the target
  • Host key verification failing (first connection needs to accept the key)

For the host key issue on first connection:

# Accept the host key non-interactively
sudo -u rundeck ssh -o StrictHostKeyChecking=accept-new 192.168.1.51 hostname

Creating a Rundeck Project

Rundeck organizes everything into projects. You need at least one project to start running jobs.

Create the Project via Web UI

  1. Log in to Rundeck at https://rundeck.example.com.
  2. Click New Project (or the + icon).
  3. Enter a project name: homelab (or whatever describes your environment).
  4. Click Create.

You’ll land on the project’s dashboard, which will show zero nodes and zero jobs. That’s expected — we haven’t configured the node source yet.

Configure the Ansible Resource Model Source

This tells Rundeck to pull its node list from your Ansible inventory instead of maintaining a separate node database.

  1. In your project, go to Project Settings (gear icon) > Edit Configuration > Resource Model Sources.
  2. Click Add Source.
  3. Select Ansible Resource Model Source from the dropdown.
  4. Configure these settings:
SettingValueWhy
Ansible inventory/var/lib/rundeck/inventory/hosts.ymlAbsolute path to your inventory file
Ansible config file path/var/lib/rundeck/.ansible.cfgAnsible config for the rundeck user
SSH Authentication TypeprivateKeyWe set up key-based auth above
SSH UserrundeckThe service user we created on managed nodes
SSH Key File Path/var/lib/rundeck/.ssh/id_ed25519Private key location
  1. Click Save.

Set Up the Inventory File

Create an inventory file that the rundeck user can read:

sudo mkdir -p /var/lib/rundeck/inventory
sudo vi /var/lib/rundeck/inventory/hosts.yml
# /var/lib/rundeck/inventory/hosts.yml
all:
  children:
    rocky:
      hosts:
        web01.example.com:
          ansible_host: 192.168.1.51
        db01.example.com:
          ansible_host: 192.168.1.52
    dbservers:
      hosts:
        db02.example.com:
          ansible_host: 192.168.1.61
sudo chown -R rundeck:rundeck /var/lib/rundeck/inventory/

Create an Ansible Config File

sudo vi /var/lib/rundeck/.ansible.cfg
# /var/lib/rundeck/.ansible.cfg
[defaults]
inventory = /var/lib/rundeck/inventory/hosts.yml
remote_user = rundeck
host_key_checking = False
timeout = 30

[privilege_escalation]
become = True
become_method = sudo
become_user = root
sudo chown rundeck:rundeck /var/lib/rundeck/.ansible.cfg

Setting host_key_checking = False is a pragmatic choice for a home lab. In a production environment you’d manage known_hosts properly, but in a home lab where nodes get rebuilt regularly, strict host key checking just generates noise.

Verify Node Discovery

After saving the resource model source configuration, go to the Nodes tab in your project. You should see your hosts listed with their attributes pulled from the Ansible inventory.

If the Nodes tab is empty, check:

# Can the rundeck user read the inventory?
sudo -u rundeck ansible-inventory --list -i /var/lib/rundeck/inventory/hosts.yml

This should output a JSON representation of your inventory. If it fails, the path is wrong or permissions are broken.


Node Executor vs. Workflow Step

Rundeck offers two ways to run Ansible within a job. Understanding the difference saves confusion later.

Node Step (Node Executor): Rundeck iterates over nodes one at a time and runs Ansible targeting each individual node. Useful when you want Rundeck to control the node-by-node execution flow — for example, running a command on one node, checking the result, then proceeding to the next.

Workflow Step (Playbook Step): Rundeck runs Ansible once and lets Ansible handle the full play across all target nodes. This is better for running existing Ansible playbooks because Ansible manages its own parallelism, error handling, and host grouping.

My recommendation: Use Workflow Steps for running playbooks. Use Node Steps only when you need per-node control flow within Rundeck (e.g., a canary deployment where Rundeck decides whether to continue based on each node’s result).


Your First Rundeck Job

Let’s prove the full chain works: Rundeck triggers Ansible, Ansible connects to your nodes via SSH, and the output flows back to Rundeck.

Create the Job

  1. In your homelab project, go to Jobs > Create a New Job.
  2. Job Name: Test - Ansible Ping
  3. Description: Verify Ansible connectivity to all managed nodes
  4. Under Workflow, click Add a Step.
  5. Under Node Steps, expand Node Step Plugins and select Ansible Playbook Inline Workflow Node Step.
  6. Paste the following into the inline playbook field:
---
- name: Test connectivity
  hosts: all
  gather_facts: false
  tasks:
    - name: Ping all nodes
      ansible.builtin.ping:

    - name: Show hostname
      ansible.builtin.command: hostname
      register: result

    - name: Display hostname
      ansible.builtin.debug:
        msg: "{{ inventory_hostname }} reports hostname: {{ result.stdout }}"
  1. Leave the Authentication and Privilege Escalation sections at their defaults. The SSH key and become settings are handled by Ansible’s own config (ansible.cfg and the playbook’s become: true), not by Rundeck’s step-level settings.
  2. Click Save for the step, then Create for the job.

Run the Job

Click Run Job Now. You’ll see a live execution log showing Ansible connecting to each host, running the ping module, and displaying the hostname. Green means success.

If the job fails:

  • “No hosts matched” — The inventory path is wrong or unreadable. See the Ansible Resource Model Source settings.
  • “Permission denied (publickey)” — SSH key setup is broken. Go back to the SSH key section above.
  • “ansible-playbook: command not found” — Ansible isn’t installed or isn’t in the rundeck user’s PATH. Verify with sudo -u rundeck which ansible-playbook.

Verification

Run through this checklist to confirm everything is connected:

# 1. Ansible is installed and accessible by the rundeck user
sudo -u rundeck ansible --version

# 2. The rundeck user can read the inventory
sudo -u rundeck ansible-inventory --list -i /var/lib/rundeck/inventory/hosts.yml

# 3. The rundeck user can SSH to managed nodes
sudo -u rundeck ssh 192.168.1.51 hostname

# 4. Ansible can reach nodes (the real test)
sudo -u rundeck ansible all -i /var/lib/rundeck/inventory/hosts.yml -m ping

Expected output for the last command:

web01.example.com | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
db01.example.com | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

If all four checks pass, your Rundeck-Ansible integration is solid.


What Can Go Wrong

Rundeck Shows Zero Nodes

Symptom: The Nodes tab in your project is empty even after configuring the Ansible Resource Model Source.

Cause: Usually a file path or permission issue. The rundeck user can’t read the inventory file, the path in the resource model source is wrong, or the inventory file has a syntax error.

Fix:

# Test as the rundeck user
sudo -u rundeck ansible-inventory --list -i /var/lib/rundeck/inventory/hosts.yml

If this fails, fix the inventory path or permissions. If it succeeds but Rundeck still shows zero nodes, restart Rundeck — the resource model source caches results.

“ansible-playbook: command not found”

Symptom: Job execution fails immediately with a PATH error.

Cause: The rundeck user’s environment doesn’t include Ansible’s install path. This happens if Ansible was installed via pip into a location not in the system PATH.

Fix: If you installed via dnf, the binary is at /usr/bin/ansible-playbook and should be in PATH. If it’s elsewhere, set the ansible-playbook path explicitly in the project’s Ansible plugin settings. Or add the path to /var/lib/rundeck/.bashrc.

SSH Connections Time Out

Symptom: Jobs hang for 30 seconds per node and then fail with “Timeout” errors.

Cause: Firewall on the managed node is blocking SSH, or DNS resolution is failing for the hostname.

Fix: Test from the Rundeck host:

sudo -u rundeck ssh -v 192.168.1.51

The verbose flag will show where the connection stalls. Common culprits: firewalld on the target node doesn’t allow SSH, the IP address is wrong, or the target is offline.

Plugin Version Mismatch After Rundeck Upgrade

Symptom: After upgrading Rundeck, the Ansible plugin stops working. Error messages mention class loading failures or incompatible plugin versions.

Cause: The Ansible plugin JAR in /var/lib/rundeck/libext/ is not compatible with the new Rundeck version.

Fix: Download the latest plugin version from the GitHub releases page, replace the old JAR, and restart Rundeck. Always check plugin compatibility notes before upgrading Rundeck.

Want the automation code? Get the production-ready Ansible playbooks that deploy everything in this guide in ~10 minutes.

Get Playbooks — $14