Pavefile Reference

Syntax and layout of pave’s configuration file is described here. For information on the various sections and task module library, skip ahead to the next chapter, Pavefile Reference Part II.


At the most basic level, a pavefile is expected to be encoded in UTF-8 format and named ./pave.yml by default.

Pave utilizes the user-friendly, human-optimized YAML file format with a few extensions. If you haven’t encountered it yet, you may wish you had found it earlier. There’s not much to learn to get started, less than other formats. No verbose “angle-bracket” tax, less punctuation and synchronization necessary than XML or JSON, and no harsh limitations like .ini files.

General Layout

Comments start with the “#” character, and indentation must be done with spaces. The form name: value represents a dictionary mapping, often called a hash, or associative array. A value is retrieved by its name. The - item form represents a list, an expandable array indexed by number.

A basic file will look like this, with section names at the far left:

    name: value

main:                               # starts a map of a map, to
    name1:  value1                  # strings, or a
    name2:  True                    # boolean, or ...

tasks:                              # a map to a list of
    - "list item 2"                 # quoted-strings, or
    - 42                            # numbers, or ...
    - name3:  |                     # text block, that preserves newlines
              ls -l /foo
              echo %name
    - name4:  >                     # text block, wrapped. May omit > char.
        This form is good for long command lines.


... are discussed in depth on the next page (at vars) but the short story is that they are created in the vars: section (see above) and can be expanded in string values with %varname style syntax.


  • If a declaration or command-line string contains positional arguments and an argument needs to contain whitespace, then it should be quoted just like it would in a shell. Also:

  • Use double quotes and C/Python-style escape sequences to denote control (or difficult to type) characters in strings:

    "\n", "\x##" and "\u####"
  • Conversely, use the “stronger” single quotes when a string has backslashes that need to be sent literally, such as definition of regular expressions. If not you’ll get errors, such as “s is not a valid escape sequence.”:

    - '#\s*password_encryption password_encryption'
  • Quoting a quoted string (with the other kind of quotes) may need to be done if the string starts with a quote but ends early, :/ e.g.:

    - ' "-s /tmp/memcached.socket" /etc/memcached.conf '

Blocks, Braces

  • If you’d like to avoid one level of the quoting mess above, block styles (see yaml docs above) are the way to go. They are useful for longer text (or command-lines) with special characters or regexes.

  • A block is signified by use of an optional block character, > or |, and newline followed by the indented body. To enter a block that will be wrapped (newlines converted to spaces) you may omit the character, or add it explicitly. This one is good for long command-lines:

    important:                  # Add > char here to explicitly wrap.
        sudo foo install

    When newlines need to be preserved, use the | char, which is helpful when writing haiku I suppose:

    status: |
        I wakey wakey.
        Production release today.
        No breaky breaky.
  • If using brace variable expansion (python string.format), use quotes for strings when they begin with a brace character:

    - '{user}'

Conditional Execution

Poor Man’s Conditionals

If your needs in the area are modest, or you’d prefer to avoid external dependencies, you’ll likely be able to squeak by with using one of the alternatives below. Separate pavefiles with includes are an option as well.

There are a few types of simple conditionals supported in the pavefile, executed either locally or remotely. They are supported by all standard modules. Add these to items under the tasks: section as needed.

  • Local conditionals:

      This clause will compare a variable from the vars: section to a value locally, and if true, execution of the attached item will continue. E.g.:

      - scp:
          - if: inst_ssl == True
            do: # ...

      This is the only place where the variable name needn’t be dereferenced with the % character. If there were whitespace in an unquoted variable for example, an error would occur, however this convenience-form avoids it and is easier to type.

      Comparisons are currently done as strings only, meaning ‘True’ equals a boolean True, while supported comparisons are == and !=.

  • Remote conditionals:

      Execute this statement to do reconnaissance at the remote end before primary task execution. If the statement returns with a exit status code of 0 (meaning true), the primary statement is run also. Conversely, a return code of >= 1 (false) will skip the task. Change/Skip events are handled automatically.

      - run:
          - if-exec: test ! -f media/js/site.js.gz
            do: # ...
  • Manual remote conditionals

    • Similar to if-exec: above, use test (aka [ ... ]), grep or other command-line program before the primary statement. See the Events section below on manual event dispatch.

  • Remote conditional handling II:

    • if-platform: PLATFORM
      if-platform: KEY COMPARISON VALUE
      Make decisions based on what we’re talking to:

      - packages:
          if-platform: Ubuntu
          install: sysvbanner
      - packages:
          if-platform: arch[0] == 64bit
          install: ia32-libs

      The available platform keys with example values are given below. The single token PLATFORM format is usually a shortcut to a simplified version of linux_dist[0] on Linux:

      arch:        ['64bit','ELF']
      encoding:    'UTF-8'
      linux_dist:  ['Ubuntu','13.10','saucy']
      machine:     'x86_64'
      node:        ''
      os_release:  '3.11.0-15-generic'
      proc:        'x86_64'
      py_vers:     '2.7.5+'
      py_vert:     ['2','7','5+']
      system:      'Linux'
      version:     '#23-Ubuntu SMP Mon Dec 9 18:17:04 UTC 2013'

      Additional example values:

      mac_vers:    ['10.9.1', ['', '', ''], 'x86_64']  # system: 'Darwin'
      win_vers:    ['XP', '5.1.2600', 'SP3', 'Uniprocessor Free']

      This information may be found under ~/.cache/pave/platform.json on the client and each target. It is also logged and printed to the console at debug level when pave is run.


For more robust conditional execution, there is the option of using a templating system to build your pavefile on the fly. Remote execution and platform checks are not available with this method, but continue with those previously mentioned. Currently jinja2 is supported.


  1. Install it if it has not been already, e.g.:

    sudo apt-get install python-jinja2      # yum, brew, pip, etc.
  2. Create a pavefile with variables (vars: section) at the top. Do not include jinja markup in this section. Assign variables (with standard expansions if desired).

  3. Add jinja markup to the rest of the document as needed, saving with a .j2 file extension.

  4. Run pave with the -f/--filename command-line parameter, using a .j2 extension, e.g.:

    pave -f paradise.j2


Events are simple signals that are output during task execution. Modules or commands use them to tell pave what has occurred. Events are generated and handled automatically in all modules in the included library, with one exception. When bare strings (commands) are children of tasks:, run: events must be dispatched manually (see below).

Three types of events are tracked by pave.

Change or Skip

When a change has been made or pre-task test is passed (meaning a change needs to be made) a “change event” is generated.

A skip event is given instead, if a task decides the change is not currently necessary.


Error events are a third kind of event that may occur. These will halt execution on the affected host if the warn-only: option is disabled in the main: section.

Change and error events are counted and returned to the user at the end of a completed task list.

Manual Dispatch from Command-Strings

  • Typically a successful command returns a 0 status code, failure non-zero.

  • To harness these follow a command-string with,

    • " && echo CHANGED", which will register a change event,
    • " || echo SKIPPED", a skip event, and/or
    • " || true ", to prevent an unimportant error from halting execution.

    Here’s an example that combines a manual conditional with manual event dispatch:

    - "[ '%mode' == 'dev' ] && wget %url && echo CHANGED || echo SKIPPED"

    A multi-line statement can be sent as well (assuming the remote shell supports it) and you have correctly marked it as a yaml text block (see | or > chars in General Layout )

  • CHANGED wins over SKIPPED if the both are output somehow.

Using Custom Modules

A custom or third-party module to use in the tasks: section should follow the interface and format of the included library modules, as discussed in the Developer Guide.

To use such a module, you may move it to a location in python’s sys.path (the current directory ”.” may already be there). For convenience the folder ~/.config/pave/lib/ is added to the path automatically, and as such is a good place to put custom modules.

Alternatively, you can modify the path to contain additional folders per project. This can be done by adding the path to /main/sys.path option in the pavefile, or setting the PYTHONPATH environment variable.

Disabling Sections/Tasks

By convention, all data under disabled: is ignored—A place to temporarily bypass tasks without having to futz around commenting them.

Task selection, using the -s/--select command-line parameter is also available as an alternative. See the Command-line Reference.

Full Examples

Next, kindly continue on to the next chapter, Pavefile Reference Part II, for details on the standard sections and task module library.