Install (Manual)

Install the Linux forwarder by hand -- package, config file, service -- then the Ansible and shell patterns for doing the same thing across a fleet.

The Linux forwarder is a package plus a configuration file plus a service. Install it by hand once to understand the shape, then automate it; the second half of this page has the Ansible and shell versions.

Before you start you need a Stairwell environment ID, a forwarder authentication token, and auditd enabled on the host.

🚧

Pick your first machine deliberately

The forwarder's first act is a full-disk backscan, which is I/O-heavy for a few hours and then stops. Start on something non-critical that can absorb it. Every subsequent install is cheaper, because Stairwell already knows the files the earlier ones sent. Forwarder Performance Tuning has the deployment order that makes the whole rollout cheapest.

Step 1: auditd

This is the single most common reason a Linux deployment underdelivers, and it fails quietly. The package installs without a running audit daemon, the service starts, and the initial backscan still collects everything already on disk. What you lose is everything after that: no file created or changed on the host is sighted. Coverage looks right on day one and quietly stops. Confirm it before you go further.

sudo systemctl status auditd
sudo systemctl start auditd
sudo systemctl enable auditd

On RHEL 6:

sudo service auditd status
sudo service auditd start
sudo chkconfig auditd on

Step 2: Install the package

Examples below use 2.5.1. Check Linux Forwarder Downloads for the current release and match the elN to your major release, not to your kernel.

RHEL, Rocky, Alma (7, 8, 9, 10):

# RHEL 10
curl -LO https://downloads.stairwell.com/linux/2.5.1/stairwell-2.5.1-1.el10.amd64.rpm

# RHEL 9
curl -LO https://downloads.stairwell.com/linux/2.5.1/stairwell-2.5.1-1.el9.amd64.rpm

# RHEL 8
curl -LO https://downloads.stairwell.com/linux/2.5.1/stairwell-2.5.1-1.el8.amd64.rpm

# RHEL 7
curl -LO https://downloads.stairwell.com/linux/2.5.1/stairwell-2.5.1-1.el7.amd64.rpm

Then install with -i on a clean host, or -U if a forwarder is already present:

sudo rpm -i stairwell-2.5.1-1.el8.amd64.rpm    # fresh install
sudo rpm -U stairwell-2.5.1-1.el8.amd64.rpm    # upgrade in place

Debian and Ubuntu (20.04, 22.04, 24.04):

curl -LO https://downloads.stairwell.com/linux/2.5.1/stairwell-2.5.1-1.amd64.deb
sudo apt install ./stairwell-2.5.1-1.amd64.deb

RHEL 6, end of life and receiving no security updates, still has a package:

curl -LO https://downloads.stairwell.com/linux/2.5.1/stairwell-2.5.1-1.el6.amd64.rpm
sudo rpm -i stairwell-2.5.1-1.el6.amd64.rpm

Step 3: Configure

sudo vi /etc/stairwell/config.json
{
  "logger": { "loglevel": "error" },
  "asset": {
    "EnvId": "ABCDEF-ABCDEF-123ABC-ABCD1234",
    "Token": "ABCDEFG1234567HIJKLMNOP789012QRSTUVW345678XYZABCD901"
  },
  "interpreters": ["sh","bash","python3","go","ruby","perl","lua","Rscript"],
  "ostype": "server",
  "proxyURL": "https://your.proxy.url:1234",
  "enableEvents": true
}

The EnvId and Token above are shaped like the real thing but are not real -- substitute your own. A few of the other fields are worth understanding rather than copying:

  • interpreters is the list of script interpreters whose executions the forwarder treats as worth reporting. Trim it and you lose visibility into that language; extend it if your estate runs something not listed.
  • ostype distinguishes server from workstation behavior.
  • proxyURL is only needed if the host reaches the internet through a proxy. Drop the line otherwise, and see Proxy Support if you keep it.
  • loglevel starts at error deliberately. Raise it while diagnosing, then put it back -- see Logging Level.

This file survives package upgrades, so you configure it once per host.

Setting the asset identity

One optional field belongs in asset, alongside EnvId and Token:

"asset": {
  "EnvId": "...",
  "Token": "...",
  "idempotencyKey": "a-stable-unique-value-for-this-machine"
}

Left out, the forwarder builds a key from the hostname and the primary MAC address. That is right for most fleets and wrong in two specific ones: machines whose MAC changes under them arrive as duplicate assets, and machines cloned from a template that already had the forwarder installed collide into a single asset. If either describes your estate, set this to something stable and unique -- a hardware serial or a cloud instance ID -- before the first registration, because it is only read then. See Asset Identifiers.

Optional tuning

Four more fields exist, all optional, all with defaults that suit most deployments. Change them when you have a specific reason, not preemptively.

FieldDefaultWhat it controls
presenceCacheMinutes360 (6 hours)How long before an unchanged file may be reported again. Raising it cuts repeat sightings on busy hosts; lowering it reports the same files more often
eventCacheSize100000How many files that suppression can remember at once. A host touching more distinct files than this in the window will re-report some of them
eventBatchMaxSize200Maximum events per upload
eventFlushSeconds20How long a partial batch waits before being sent anyway
backscanFailureThresholdPercent5.0The share of backscan uploads that may fail before the whole backscan is marked failed rather than complete

The first two are the pair worth understanding, because they work together and they are the lever on sighting volume from a noisy machine. The forwarder remembers files it has already reported and stays quiet about them until the window passes; a bigger cache and a longer window mean less repetition, at the cost of taking longer to notice that a file it once saw is still there. Neither affects whether new or changed files are reported -- those always are.

backscanFailureThresholdPercent is worth knowing before you go looking for it. A backscan that hits the threshold is marked failed, not partially complete, and the asset will not show a completed backscan even though most of its files uploaded fine. On a host with a large unreadable directory, that reads as a broken deployment when it is really a permissions problem in one place.

Step 4: Start the service

sudo systemctl enable --now stairwell.service

On RHEL 6:

sudo service stairwell start
sudo chkconfig stairwell on

Step 5: Confirm it is working

journalctl -u stairwell.service -f
# or, on RHEL 6
sudo tail -f /var/log/stairwell/fileshipper.log

A running service is where the check starts, not where it ends. Open Assets in the platform, find the host, and confirm objects are arriving. A host whose backscan completed but which never sights anything afterwards is the auditd failure, and it is much easier to catch now than in three months. Linux Troubleshooting starts there and works outward.

Doing this at fleet scale

MethodBest for
AnsibleMost estates. Idempotent and parallel
Puppet, Chef, SaltStackFleets where configuration management already owns the host
Internal package repositoryTurning Stairwell into part of your normal patch cycle
cloud-init / user dataCloud instances, especially short-lived ones
Shell scriptSmall or simple environments

Ansible

---
- name: Install Stairwell Forwarder
  hosts: linux_hosts
  become: yes
  vars:
    stairwell_version: "2.5.1"
    stairwell_env_id: "{{ vault_stairwell_env_id }}"
    stairwell_token: "{{ vault_stairwell_token }}"

  tasks:
    - name: Download RPM
      get_url:
        url: "https://downloads.stairwell.com/linux/{{ stairwell_version }}/stairwell-{{ stairwell_version }}-1.el8.amd64.rpm"
        dest: "/tmp/stairwell.rpm"
      when: ansible_os_family == "RedHat"

    - name: Install RPM
      yum:
        name: "/tmp/stairwell.rpm"
        state: present
      when: ansible_os_family == "RedHat"

    - name: Download DEB
      get_url:
        url: "https://downloads.stairwell.com/linux/{{ stairwell_version }}/stairwell-{{ stairwell_version }}-1.amd64.deb"
        dest: "/tmp/stairwell.deb"
      when: ansible_os_family == "Debian"

    - name: Install DEB
      apt:
        deb: "/tmp/stairwell.deb"
        state: present
      when: ansible_os_family == "Debian"

    - name: Configure Stairwell
      copy:
        dest: /etc/stairwell/config.json
        mode: "0600"
        content: |
          {
            "logger": { "loglevel": "error" },
            "asset": {
              "EnvId": "{{ stairwell_env_id }}",
              "Token": "{{ stairwell_token }}"
            },
            "interpreters": ["sh","bash","python3","go","ruby","perl","lua","Rscript"],
            "ostype": "server",
            "enableEvents": true
          }

    - name: Enable and start service
      systemd:
        name: stairwell.service
        enabled: yes
        state: started

Two things about that playbook. The token comes from Ansible Vault rather than a literal, because a forwarder token in a git-tracked playbook is a credential in your repository history forever. And it pulls the el8 RPM for every RedHat-family host: if your estate spans RHEL 7 through 10, derive the elN from ansible_distribution_major_version instead.

Shell

#!/bin/bash
set -e

VERSION="2.5.1"
ENV_ID="YOUR_ENV_ID"
TOKEN="YOUR_TOKEN"

if [ -f /etc/debian_version ]; then
  curl -LO https://downloads.stairwell.com/linux/$VERSION/stairwell-$VERSION-1.amd64.deb
  sudo apt install -y ./stairwell-$VERSION-1.amd64.deb
else
  curl -LO https://downloads.stairwell.com/linux/$VERSION/stairwell-$VERSION-1.el8.amd64.rpm
  sudo rpm -U stairwell-$VERSION-1.el8.amd64.rpm
fi

sudo tee /etc/stairwell/config.json > /dev/null <<EOF
{
  "logger": { "loglevel": "error" },
  "asset": {
    "EnvId": "$ENV_ID",
    "Token": "$TOKEN"
  },
  "interpreters": ["sh","bash","python3","go","ruby","perl","lua","Rscript"],
  "ostype": "server",
  "enableEvents": true
}
EOF

sudo chmod 600 /etc/stairwell/config.json
sudo systemctl enable --now stairwell.service

Pass ENV_ID and TOKEN in from your secret store rather than editing them into the script.

Whichever method you use: pilot on non-critical hosts, stagger the rollout so a thousand backscans do not start at once, and keep tokens out of version control.

What should I read next?


Did this page help you?