🧑‍💻 agile sysadmin

by Ferenc Erki

Virtues of Rex

We follow a set of guiding principles while developing Rex, the friendly automation framework:

  1. Use a programming language
  2. Empower users through trust
  3. Enable graceful bootstrapping

While we summarize these concepts briefly on our website, I consider it worthwhile to elaborate on the underlying details.

Let’s take a closer look at why we find these choices important.

Use a programming language

Most automation projects develop their own approach to describe the desired state of managed endpoints through their custom Domain-specific language (DSL).

This often means either using a readily available data serialization format (like YAML), or inventing their own configuration language. Sooner or later these approaches tend to reach their limitations, and to better accommodate real-world use cases, need to start supporting programming concepts like:

  • variables and templates to dynamically calculate variations based on information retrieved from elsewhere
  • loops to repeat similar steps with different inputs
  • conditions to make decisions dynamically based on information available during runtime

Adding these concepts to the chosen format often means essentially creating an accidental programming language – with all related details and tooling: custom syntax, parsers, linters, formatters, style guides, formatters, documentation, packaging, dependency management, and so on.

These add-ons often feel like an afterthought introduced late in the maturing process of the project, instead of treating them as an integral part of the solution from the beginning.

In contrast, using a general-purpose programming language may prove a valuable alternative to this approach. Preventing the challenges of inventing our own language allows the project focus on its core competencies.

For these reasons, Rex uses Perl as its DSL – a battle-tested, mature programming language. This grants considerable advantages to both contributors and users, for example:

  • whenever reaching the limitations of the built-in Rex features, seamlessly extend it with a powerful language and its ecosystem, through one’s own code or modules from CPAN
  • transparent access to well-established tools and workflows while working on solutions, such as IDE integration, syntax highlighting, linting, formatting, testing, authoring, publishing, and so on
  • Rex can run wherever Perl can run, and Rex supports whatever Perl supports, including the ability to (re)use code written in other languages

Compared to commercial projects, these capabilities prove especially valuable for volunteer-driven projects such as Rex. Building on top of existing solutions allows us to invest our effort only into relevant details.

Staying friendly to real-world use cases through using proven technologies enables us to focus on delivering more value to users sooner.

Concerned about using Perl?

We know it appears fashionable to parrot common assumptions about Perl, which may sound worrying. Rex encourages users to form their own opinion through their own experience, rather than readily accepting others’ opinions.

Rex does not expect prior experience with Perl. Check out our Just enough Perl for Rex page for a quick tutorial.

To establish and maintain consistent results, we recommend using perltidy for code layout, perlcritic for code standards, and perlbrew for development environments.

The above combination helps setting the stage for a delightful Modern Perl experience.

More importantly, though, different situations require different choices. I happily share why I use Perl when it fits my situations:

  • powerful flexibility, which allows choosing the solution and approach that best matches the situation at hand
  • actively maintained, with regular new releases bringing new features
  • still keeping ages of backwards compatibility
  • great community
  • culture infused with decades of learning lessons about best practices
  • especially strong traditions of testing and quality, including crowdsourced testing via CPAN Testers, which appears unique to the Perl ecosystem
  • diverse and relevant library of shared modules on CPAN
  • great multi-platform and Unicode support
  • allows rapid prototyping and proof of concepts, thus finding product-market fit sooner through iterating on experiments, and then growing what works

And as Perl’s own latest v5.40.1 release acknowledgments summarize it on 2025‑01‑18:

Perl continues to flourish into its fourth decade thanks to a vibrant community of users and developers.

Empower users through trust

Silver bullets work best when the challenge involves fighting werewolves. Other situations tend to have more effective and less expensive solutions.

Typical automation situations require managing remote endpoints, while others need to do the same locally. Some setups prefer to push changes to managed endpoints, while pulling the same changes by the managed nodes fit others. Many prefer using a declarative approach, though sometimes an imperative style fits too.

Most projects tend to have a strong opinion about their preferred approach, and sometimes later may add support for the alternatives. This often feels having to follow someone else’s decisions and preferences, which may or may not apply to our own case.

Rex strives to have as few opinions as possible about these details, and lets its users make decisions about what fits their situations at hand. In the spirit of Perl, we acknowledge that “there’s more than one way to do it” (TIMTOWTDI, pronounced Tim Toady.)

Aiming to create a universal solution for all imaginable scenarios would lead to extreme complexity, though. Solving only the subset relevant to the given user case often proves much simpler.

Hence, Rex provides lightweight building blocks to choose from according to the actual requirements. Because, again in the spirit of Perl, ”easy things should be easy and hard things should be possible.”

Ultimately this approach:

  • allows building the automation tool the given use case requires, leading to less waste
  • promotes fitting the solution to the challenge, instead of the other way around
  • encourages discovering the true requirements, instead of following someone else’s opinion, avoiding cargo culting

Rex celebrates such freedom of choice, and stays friendly to its users by trusting their judgment about what and how to automate in their own specific situation.

Examples of different approaches

Imperative and declarative

Imperative means giving commands about how to achieve an expected result, while declarative means specifying the expected result without all the details of how to achieve it.

Also consider idempotency to have the same results when running the same code repeatedly.

Rex offers different abstractions accordingly, for example:

task 'create_directory_imperative', sub {
    mkdir '/path/to/my/directory';
};

task 'create_directory_declarative', sub {
    file '/path/to/my/directory',
      ensure => 'directory';
};

Need to ensure idempotency around imperative commands? Add custom logic:

my $path = '/path/to/my/directory';

if ( not is_dir($path) and not is_file($path) ) {
    mkdir $path;
}

No declarative abstraction for a given use case yet? Use Rex::Resource to implement its details.

Local and remote

Consider the following task to print the output of the hostname command from the managed endpoint:

task 'show_hostname', sub {
    say run 'hostname';
};

Running rex show_hostname on the command line executes this task locally. Running rex -H myserver show_hostname executes the same on myserver over SSH.

Need to run it on myserver by default? Specify the default target for a task like this:

task 'show_hostname' => 'myserver', sub {
    ...
};

Then override the default target either on the command line as above, or programmatically when calling from other tasks.

Need to mix both local and remote execution in a single solution? Target the local machine conventionally as <local>, or use SSH to connect to it just like a remote endpoint.

See also my previous post about Local management with Rex.

Push and pull

Push approach means reaching out to the managed endpoint and carry out operations there. Pull approach means obtaining the code and configuration from a shared source of truth, and then carry out operations locally.

While pushing changes seem popular, some use cases prefer or require pulling.

For a pull approach example, consider storing the task definitions in a version control system, like Git, and having a converge task in Rex which calls other necessary tasks:

task 'converge', sub {
    run_task 'manage_webserver';
    run_task 'manage_database';
    run_task 'deploy_appliction';
};

In this case we can add a cronjob to execute git pull && rex converge periodically, for example every 30 minutes.

Or consider using a task which keeps running, like an agent, and waits for an event before triggering actions. The following example waits for a certain amount of elapsed time:

task 'agent', sub {
    while (TRUE) {
        sleep 30 * 60;
        run 'git pull';
        run_task 'converge';
    }
};

Need to mix both push and pull? Use either of the above together with a Git receive hook, which executes rex converge upon git push. Don’t forget to use some locking to avoid overlapping execution of both, though.

Enable graceful bootstrapping

Getting started with automation initiatives may feel cumbersome or intimidating. It often involves having to convince team members and decisions makers, deploy and configure supporting software on managed endpoints, then implement or convert all the current processes.

Rex itself runs locally, and only requires Perl, which many systems have readily available. It also detects the presence of optional dependencies, and may use them for some advanced or enhanced operations.

It normally connects to remote endpoints via SSH, which teams often already use to manage their systems. When required, Rex stands ready to support other connection methods, thanks to its modular connection layer.

On the remotes, it checks the availability of Perl and other software to decides what to use while carrying out its tasks.

By having so few requirements and opinions, Rex enables incremental automation, and allows treating such projects like any other software development.

This approach provides a straightforward way to rapidly map typical processes into executable code – without having to convince anyone else, or to install anything special on the managed endpoints.

Friendly sharing of such code also enables others to get onboard conveniently by reusing the results – and kindly pointing them to our How to start using Rex page.

My personal getting started story

Long time ago my team looked for automation opportunities for our infrastructure operations. Providing solutions for the foreign exchange market meant both strict regulations, and extreme performance requirements, especially low latency.

Many of today’s popular automation projects either did not exist yet, or just started out around that time. The initially considered alternatives required running extra software on our servers, along with bulky dependencies, which concerned us about their viability for our requirements.

A colleague of mine recommended checking out Rex, and its capabilities surprised me when I gave it a try. It also built on top of the existing technologies we already used in production, making rapid adoption possible for us.

Rex has proved valuable for our situation. Using it for everyday operations enabled our entire team to improve.

It also made me want to understand how it could deliver such powerful solutions, while staying so lightweight compared to alternatives. Ultimately that experience drove me to learn Modern Perl properly, and start contributing.

Epilogue

These principles establish Rex, the friendly automation framework as a powerful alternative among infrastructure automation and configuration management projects.

In the Programming Perl book (the “Camel Book”), Larry Wall describes the three great virtues of programmers as: laziness, impatience, and hubris.

I leave matching those virtues with the above ones as an exercise to readers – after all, TIMTOWTDI.