{"id":49401,"date":"2025-05-21T06:19:31","date_gmt":"2025-05-21T06:19:31","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=49401"},"modified":"2026-02-21T07:28:37","modified_gmt":"2026-02-21T07:28:37","slug":"rundeck-methods-to-run-ansible-from-rundeck","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/rundeck-methods-to-run-ansible-from-rundeck\/","title":{"rendered":"Rundeck &#8211; Methods to Run Ansible from Rundeck"},"content":{"rendered":"\n<p>Here&#8217;s a <strong>complete guide<\/strong> to running Ansible Playbooks using <strong>Rundeck<\/strong>, covering <strong>all supported methods<\/strong>, including what you&#8217;ve done, plus troubleshooting options and best practices.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 Goal<\/h2>\n\n\n\n<p><strong>Run an Ansible Playbook (<code>web.yaml<\/code>) on remote hosts from Rundeck.<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\uddf0 Prerequisites<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ansible installed on Rundeck server (<code>ansible<\/code>, <code>ansible-playbook<\/code>)<\/li>\n\n\n\n<li>Playbook and inventory exist, e.g.:\n<ul class=\"wp-block-list\">\n<li><code>\/tmp\/ansible\/web.yaml<\/code><\/li>\n\n\n\n<li><code>\/tmp\/ansible\/inventory<\/code><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Hosts reachable via SSH (key-based preferred)<\/li>\n\n\n\n<li>Rundeck has access to private key<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 Methods to Run Ansible from Rundeck<\/h2>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 <strong>Method 1: Ansible Plugin (Node Step or Workflow Step)<\/strong><\/h3>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>Rundeck Plugin<\/strong>: <a href=\"https:\/\/github.com\/Batix\/rundeck-ansible-plugin\" target=\"_blank\" rel=\"noopener\">Ansible Plugin for Rundeck<\/a><\/p>\n\n\n\n<p><strong>Best for:<\/strong> Structured jobs with SSH auth and node filtering.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Configuration<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Plugin Step Type: <strong>Ansible Playbook Workflow Node Step<\/strong><\/li>\n\n\n\n<li>Playbook: <code>web.yaml<\/code><\/li>\n\n\n\n<li>Inventory: <code>-i \/tmp\/ansible\/inventory<\/code><\/li>\n\n\n\n<li>Auth: Private key or SSH password<\/li>\n\n\n\n<li>User: <code>ubuntu<\/code><\/li>\n\n\n\n<li>Extra vars (optional): <code>foo=bar<\/code><\/li>\n\n\n\n<li>Set: <code>Use become: Yes<\/code> (if needed)<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">\ud83d\udd0d Troubleshooting<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Set <strong>Node Filter<\/strong> to <code>.*<\/code> (or <code>name: .*<\/code>) to apply job globally<\/li>\n\n\n\n<li>Rundeck will pass <code>--limit &lt;node&gt;<\/code> unless you&#8217;re careful<\/li>\n\n\n\n<li>Inventory must include same hostnames\/IPs as Rundeck nodes<\/li>\n<\/ul>\n\n\n\n<p>Scree -1<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"526\" src=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-8-1024x526.png\" alt=\"\" class=\"wp-image-49405\" srcset=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-8-1024x526.png 1024w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-8-300x154.png 300w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-8-768x394.png 768w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-8.png 1480w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"311\" src=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-9-1024x311.png\" alt=\"\" class=\"wp-image-49406\" srcset=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-9-1024x311.png 1024w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-9-300x91.png 300w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-9-768x234.png 768w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-9.png 1430w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"297\" src=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-10-1024x297.png\" alt=\"\" class=\"wp-image-49407\" srcset=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-10-1024x297.png 1024w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-10-300x87.png 300w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-10-768x223.png 768w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/05\/image-10.png 1433w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">#inventory code<\/span>\nmore inventory\n&#91;web]\n<span class=\"hljs-number\">65.2<\/span><span class=\"hljs-number\">.153<\/span><span class=\"hljs-number\">.2<\/span>\n<span class=\"hljs-number\">52.66<\/span><span class=\"hljs-number\">.236<\/span><span class=\"hljs-number\">.156<\/span>\n<span class=\"hljs-number\">13.232<\/span><span class=\"hljs-number\">.137<\/span><span class=\"hljs-number\">.62<\/span>\n<span class=\"hljs-number\">13.201<\/span><span class=\"hljs-number\">.133<\/span><span class=\"hljs-number\">.240<\/span>\n\n&#91;db]\n<span class=\"hljs-number\">3.7<\/span><span class=\"hljs-number\">.65<\/span><span class=\"hljs-number\">.62<\/span>\n\n<span class=\"hljs-comment\">#web.yaml<\/span>\n---\n- name: Update web servers\n  hosts: web\n\n  tasks:\n  - name: Install Apache in ubuntu\n    ansible.builtin.apt:\n      name: <span class=\"hljs-string\">\"git\"<\/span>\n      state: latest\n  - name: Copy index.html\n    ansible.builtin.copy:\n      src: index.html\n      dest: \/tmp\/index.html\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 <strong>Method 2: Ansible as Command Step (Central Execution)<\/strong><\/h3>\n\n\n\n<p><strong>Run centrally<\/strong> from Rundeck server (not per node). This avoids node filtering issues.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Example<\/h4>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">ansible-playbook -i \/tmp\/ansible\/inventory \/tmp\/ansible\/web.yaml -u ubuntu -b --private-key=<span class=\"hljs-regexp\">\/tmp\/<\/span>ansible\/key.pem\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\">Steps<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Add a <strong>Command Step<\/strong><\/li>\n\n\n\n<li>Shell command as above<\/li>\n\n\n\n<li>Optional: Make key available using Secure Option<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">\u2705 When to Use<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You don\u2019t need per-node execution<\/li>\n\n\n\n<li>Want Rundeck to just centrally launch playbooks<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 <strong>Method 3: Script Step with Arguments<\/strong><\/h3>\n\n\n\n<p>Same as Method 2, but using a bash script.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Example<\/h4>\n\n\n\n<p>Create script <code>\/tmp\/run_ansible.sh<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-meta\">#!\/bin\/bash<\/span>\nansible-playbook -i \/tmp\/ansible\/inventory \/tmp\/ansible\/web.yaml -u ubuntu -b --private-key=<span class=\"hljs-regexp\">\/tmp\/<\/span>ansible\/key.pem\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Set <strong>Rundeck step type<\/strong>: Script file or inline<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 <strong>Method 4: Ansible Ad-Hoc Step (Ansible Plugin)<\/strong><\/h3>\n\n\n\n<p>You can also use:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>Ansible Ad-Hoc Command Node Step<\/code><\/li>\n\n\n\n<li>For one-liner tasks (e.g., <code>apt update<\/code>, <code>ping<\/code>, <code>copy<\/code>)<\/li>\n<\/ul>\n\n\n\n<p>Example module: <code>ping<\/code>, <code>shell<\/code>, <code>copy<\/code><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 <strong>Method 5: Job with Embedded Inventory (YAML)<\/strong><\/h3>\n\n\n\n<p>In Rundeck Project configuration, embed static inventory.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Example:<\/h4>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">&#91;web]\n192.168.1.10 ansible_user=ubuntu\n<\/code><\/span><\/pre>\n\n\n<p>Then playbook can use <code>hosts: web<\/code>, and Rundeck calls it with <code>--limit<\/code> to match that.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 <strong>Method 6: Dynamic Inventory Integration<\/strong><\/h3>\n\n\n\n<p>Use AWS\/GCP inventory plugin or a custom dynamic inventory script.<\/p>\n\n\n\n<p>Steps:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Create a script like <code>aws_ec2.py<\/code> or a custom script<\/li>\n\n\n\n<li>In Ansible arguments: <code>-i \/tmp\/ansible\/my_dynamic_inventory.py<\/code><\/li>\n\n\n\n<li>Make sure script is executable and returns correct JSON<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\uddea Bonus: Add a Debug Step Before Playbook<\/h2>\n\n\n\n<p>Add a <strong>shell step before Ansible call<\/strong>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">ansible -i \/tmp\/ansible\/inventory web --<span class=\"hljs-keyword\">list<\/span>-hosts\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This lets you verify what Ansible sees at runtime.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\uddfe Sample Rundeck Job YAML (Centrally Executed)<\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">- defaultTab: nodes\n  <span class=\"hljs-attr\">description<\/span>: Run Ansible playbook centrally\n  <span class=\"hljs-attr\">executionEnabled<\/span>: <span class=\"hljs-literal\">true<\/span>\n  <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-number\">1234<\/span>-ansible-job\n  <span class=\"hljs-attr\">loglevel<\/span>: INFO\n  <span class=\"hljs-attr\">name<\/span>: run-ansible-central\n  <span class=\"hljs-attr\">nodeFilterEditable<\/span>: <span class=\"hljs-literal\">false<\/span>\n  <span class=\"hljs-attr\">scheduleEnabled<\/span>: <span class=\"hljs-literal\">true<\/span>\n  <span class=\"hljs-attr\">sequence<\/span>:\n    commands:\n    - description: Run Ansible centrally\n      <span class=\"hljs-attr\">exec<\/span>: ansible-playbook -i \/tmp\/ansible\/inventory \/tmp\/ansible\/web.yaml -u ubuntu -b --private-key=<span class=\"hljs-regexp\">\/tmp\/<\/span>ansible\/key.pem\n  <span class=\"hljs-attr\">nodefilters<\/span>:\n    dispatch:\n      threadcount: <span class=\"hljs-number\">1<\/span>\n      <span class=\"hljs-attr\">keepgoing<\/span>: <span class=\"hljs-literal\">false<\/span>\n    <span class=\"hljs-attr\">filter<\/span>: <span class=\"hljs-string\">\".*\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 Summary Table<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Method<\/th><th>Description<\/th><th>Best Use Case<\/th><\/tr><\/thead><tbody><tr><td>\ud83d\udd0c Plugin (Node Step)<\/td><td>Uses Rundeck Ansible plugin to run playbook on matched nodes<\/td><td>When your inventory matches Rundeck nodes<\/td><\/tr><tr><td>\ud83d\udda5\ufe0f Command Step<\/td><td>Runs Ansible centrally from Rundeck server<\/td><td>For single control execution<\/td><\/tr><tr><td>\ud83e\uddfe Script Step<\/td><td>Shell script wraps Ansible call<\/td><td>Repeatable, customizable<\/td><\/tr><tr><td>\u26a1 Ad-Hoc Step<\/td><td>Use Ansible module like <code>ping<\/code> or <code>shell<\/code><\/td><td>For one-time actions<\/td><\/tr><tr><td>\ud83d\udcc2 Embedded Inventory<\/td><td>Static inventory within project<\/td><td>When inventory doesn&#8217;t change<\/td><\/tr><tr><td>\ud83d\udd01 Dynamic Inventory<\/td><td>External inventory script (AWS, GCP, etc.)<\/td><td>For cloud infra or frequent host changes<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here&#8217;s a complete guide to running Ansible Playbooks using Rundeck, covering all supported methods, including what you&#8217;ve done, plus troubleshooting options and best practices. \u2705 Goal Run an Ansible Playbook&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[2],"tags":[],"class_list":["post-49401","post","type-post","status-publish","format-standard","hentry","category-uncategorised"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/49401","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=49401"}],"version-history":[{"count":5,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/49401\/revisions"}],"predecessor-version":[{"id":58979,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/49401\/revisions\/58979"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=49401"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=49401"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=49401"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}