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 deliberatelyThe 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
auditdThis 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 auditdOn RHEL 6:
sudo service auditd status
sudo service auditd start
sudo chkconfig auditd onStep 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.rpmThen 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 placeDebian 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.debRHEL 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.rpmStep 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:
interpretersis 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.ostypedistinguishesserverfrom workstation behavior.proxyURLis only needed if the host reaches the internet through a proxy. Drop the line otherwise, and see Proxy Support if you keep it.loglevelstarts aterrordeliberately. 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.
| Field | Default | What it controls |
|---|---|---|
presenceCacheMinutes | 360 (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 |
eventCacheSize | 100000 | How 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 |
eventBatchMaxSize | 200 | Maximum events per upload |
eventFlushSeconds | 20 | How long a partial batch waits before being sent anyway |
backscanFailureThresholdPercent | 5.0 | The 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.serviceOn RHEL 6:
sudo service stairwell start
sudo chkconfig stairwell onStep 5: Confirm it is working
journalctl -u stairwell.service -f
# or, on RHEL 6
sudo tail -f /var/log/stairwell/fileshipper.logA 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
| Method | Best for |
|---|---|
| Ansible | Most estates. Idempotent and parallel |
| Puppet, Chef, SaltStack | Fleets where configuration management already owns the host |
| Internal package repository | Turning Stairwell into part of your normal patch cycle |
| cloud-init / user data | Cloud instances, especially short-lived ones |
| Shell script | Small 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: startedTwo 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.servicePass 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?
- Install (Script) and Install (APT), which are less work than this page if they fit.
- Install in Sleep Mode, to have hosts register now and backscan when you say so.
- Linux Troubleshooting, for
auditd, SELinux, and AppArmor. - Asset Identifiers, if hostnames or MAC addresses are not stable in your estate.
Updated 23 days ago