In this tutorial we’ll learn:

  • Why you might benefit from a custom Rundeck script plugin
  • How to create a basic script plugin
  • How to deploy the necessary Rundeck configuration to use it

The end result of this tutorial as well as a Docker environment to run it in can be found on GitHub.

Why create your own script plugin?

Rundeck is an open source ops automation platform that comes with a lot of functionality out of the box, like running script commands on your nodes with a command step. If you can already run commands with the default functionality, why would you want to write a new plugin to do that?

Several reasons:

  • To encapsulate and reuse functionality across projects and jobs
  • To expose script parameters to the UI with descriptions, types and defaults
  • To securely access secrets stored in Key Storage

Encapsulate and reuse functionality across projects and jobs

Command defined as a command step are specific to a job. If you need to reuse the script in several jobs in the same or different projects, you would have to copy-paste it each time which can get to be cumbersome and error-prone.

Using a custom plugin encapsulates the script so that job writers across any projects can use the script’s functionality. When the plugin author uploads a new version the plugin, all jobs automatically use the latest version.

Expose script parameters to the UI with descriptions, types and defaults

Command line scripts can come with many parameters. When running them from command line, if you’re lucky, you are able to get the help text from them. Some scripts may not have help on their parameters. When creating a custom script plugin, you can define, document and provide default values that make sense in the context of your organization for the parameters of your script. Conversely, you can also leave out parameters to a script that you don’t want job authors to have access to.

All the parameters defined in the plugin will show up in the Rundeck job editting UI, providing the job authors with afforances to guide them in using the script.

Securely access secrets stored in Key Storage

Many scripts require some type of secret to authorize the script to do its work so the job user needs to know the secret to use the script. This increases the risk of that secret getting exposed depending how your share secrets in your organization.

A more secure solution would be to authorize the job user to use the secret without them actually know its value. Plugins can access secrets by referring to paths in the Rundeck Key Storage. Rundeck will evaluate the value of the secret into the execution without the job user ever knowing the value. This reduces the scope of who needs to know the secret to the Rundeck administrator populating the Key Storage. It also lets the administrator rotate the secrets transparent to the job user.

How to create a simple workflow step script plugin

Now that we see the value in creating our own workflow step script plugin, we can walk through a simple Hello World example.

Script to wrap

We’re going to wrap a simple bash script called

It prints the hostname it’s called on and the first and second parameters passed to it. We can wrap a script from any scripting language as long as the intepreter is already deployed on the remote nodes.

Basic plugin structure

A Rundeck plugin is a zipped directory with the following structure:
helloworld-plugin      -- root directory of zip contents, same name as zip file
├── plugin.yaml        -- plugin metadata file
└── contents           -- where to put your scripts

plugin.yaml is the only required file in the zip. This is the plugin.yaml for our Hello World plugin:

The significant section is the item in the providers array. It says we’re creating a RemoteScriptNodeStep named HelloBash. A RemoteScriptNodeStep means the script will run on the remote nodes, as opposed to a WorkflowNodeStep, which runs on the Rundeck server itself and receives the nodes as parameters.

Script invokation

The values for script-interpreter, script-file, and script-args are invoked on the remote node as follows: only needs to be bundled in the plugin zip contents directory and Rundeck takes care of copying it to the remote nodes that it runs on.

The script-args value we pass to the script, ${} is one of many context variables that Rundeck sets.

Script parameters

The config key defines the inputs to the script, their types, defaults and how they’re rendered in the UI.

The who_i_am item describes a select box with predefined values of machine or mannequin, defaulting to machine.

The secret_secret item is a string value but the valueConversion: "STORAGE_PATH_AUTOMATIC_READ" tells Rundeck to interpret that string as a path in Key Storage and pass the value of that key to the script. This is how we can securely reference secrets without exposing them to the job users.

Setting up the plugin environment

How can we actually see this plugin in action? Rundeck is designed for real world multi-node environments but we don’t want the hassle of actually spinning one up, so we use Docker to simulate a multi-node environment right on our workstation.

The environment setup can be downloaded at

To build and start the Docker environment, run:

This make command is specific to this tutorial. It will do several things:

  1. Zip the plugin source files into the proper zip structure and save it in the Rundeck image’s build directory
  2. Run Docker Compose to build all the images specified in docker-compose.yml and run containers from those images. Docker Compose will run the containers in the foreground, outputting all their logs to your terminal.

The containers in our environment are:

  • rundeck: The Rundeck server. The Dockerfile copies the zipped plugin into the image’s libext directory where Rundeck will watch for new plugins.
  • rundeck-cli: The Rundeck command line client. This image doesn’t run as a service but instead we invoke it from command line to push configuration and invoke jobs on the Rundeck server.
  • web_1 & web_2: Containers that simulate your application nodes. They run both an ssh daemon and a sample web app. These are the nodes that Rundeck will execute our plugin on.

Pushing configuration to Rundeck

Once our Docker environment is up and the Rundeck server is listening for requests, we can push a sample project and job that will use our plugin. We do that from a separate terminal that our make compose command:

This will:

  • Check for changes in our plugin source files, and if so, copy a new plugin zip into the running Rundeck container.
  • Push all the Rundeck yaml config in rundeck-project to the running Rundeck server.
  • Push the keys in rundeck-project/key-storage to the Key Storage.

With this command, you can iterate on your plugin or job configuration without having to stop and start the Docker environment. Additionally, it will only push config that has changed.

The configuration we’re pushing defines the nodes, project and job that use our plugin.

If you just want to run the job, you can skip ahead to Running the Hello World job, otherwise we walk through the config involved in the sections below.


In order for Rundeck to know which nodes exist in your environment and how to connect to them, we provide a resource source yaml file:


To use the above defined resource source file, we set the following properties on the project:


project.ssh.user and project.ssh-keypath configure the ssh user and private key to use when connecting to any node with this project. It’s up to you to properly distribute the ssh keys to your nodes. In our playground environment, we’ve bundled the keys into the Docker images.

resources.source.1.config.file points to the nodes.yaml file we created previously.

Both the ssh private key and the nodes.yaml file need to be deployed to the Rundeck server and be readable by the rundeck user.


To define our job, we create another yaml file, rundeck-project/hello_test_job.yaml:

Some values to note:

  • The nodefilters key is letting us specify a regex to match the tags of the nodes we want to run this job on. In our case, any of the web nodes.
  • The sequence key defines what the job should run. In our case, we’re running one command, a node step of our HelloBash script from our custom plugin.
  • The configuration key of the node step is how we pass configuration into our script. Note that we’re passing in a Key Storage path instead of an actual value.

Running the Hello World job

Now that we’ve spun up a Rundeck environment and defined a job that uses our plugin, we can run it using the rd command line client. If you had the rd client installed natively, it would be as simple as:

But since we’re running rd from a Docker container, we need some extra commands to get it to run:

The rd alias we’ve created lets us type less and use the rd command as if it were running natively on our workstation.

All this is encapsulated in a make command:

The script outputs the hostnames it’s running on, which will most likely be different but similar to the above, as well as the secret “I’m Kilroy!”. Remember that we passed in a Key Storage path value keys/projects/hello-project/mysecret for that parameter. To double check that that’s the correct value, you can cat the value on disk that we pushed to the Rundeck server:

Another thing to notice is that even though we’ve securely passed the secret to the script plugin, once the script plugin has it, they can do whatever with the value, including expose it in the logs, so the script writer needs to be cautious about that.

Next: a real world use case

Now that we understand the basics of how a custom script plugin works, we can use it to solve a real world use case: rotating database credentials.