You Ran Lynis and Got 42 Suggestions. Now What?
You installed Lynis, ran it, and got a wall of green, yellow, and red text ending in a number. Now you want that number to go up. That instinct is the trap.
The hardening index is a to-do list, not a score to max out. Chasing 100 means disabling things you use, adding things you never maintain, and breaking your own box at 2 AM. The useful loop is smaller: read the report, fix the five findings that matter on a home-lab box, silence the ones that don’t apply, and re-run on a schedule so you see the trend.
Full example: Clone the working files at github.com/KingPin/sumguy-examples/linux/lynis-hardening-audit
Every output excerpt below comes from a real Lynis 3.1.7 run in a throwaway Debian 13 container. A container is not a real host, so a real box shows more findings (firewall state, kernel modules, boot loader, real SSH exposure). The format and workflow are identical.
Run the Current Version, Not the Packaged One
Distro packages lag. Debian 13 ships Lynis 3.1.4 and Ubuntu 24.04 ships 3.0.9, while the current release is 3.1.7 (June 2026, from the CISOfy repo). Newer releases add tests and fix false positives, so an old package gives you old opinions.
Running from a tarball needs no install:
mkdir -p ~/tools && cd ~/toolscurl -fsSLO https://downloads.cisofy.com/lynis/lynis-3.1.7.tar.gztar xzf lynis-3.1.7.tar.gzcd lynissudo ./lynis audit system --quickA git clone --depth 1 --branch 3.1.7 https://github.com/CISOfy/lynis works the same way. Two gotchas:
- Run it from inside the directory. Call it from anywhere else and it dies with
can't find include directory. - Run it as root. A non-root run prints a “NON-PRIVILEGED SCAN MODE” banner and skips every test that needs root. There is a
--pentestmode for non-privileged scans. It lists what an attacker would look at and still skips the root-only tests.
The --quick flag (short form -Q) turns off the “press Enter to continue” pauses between sections. Without it, Lynis stops after each block and waits for you.
Anatomy of the Screen Output
Lynis prints one section per area, marked with [+]:
[+] Boot and services[+] Kernel[+] Users, Groups and Authentication[+] File systems[+] Networking[+] Software: firewalls[+] SSH Support[+] Logging and files[+] Scheduled tasks[+] Cryptography[+] Kernel HardeningThat is 11 of the 45 sections in a default run. Under each header, every check ends in a status word. These are the ones you will see:
- Checking /tmp mount point [ SUGGESTION ]- Checking running SSH daemon [ FOUND ]- OpenSSH option: PermitRootLogin [ OK ]- Checking minilogd instances [ WARNING ]- Checking running SNMP daemon [ NOT FOUND ]- Program update status [ SKIPPED ]NOT FOUND and SKIPPED mean the test did not apply, usually because the software isn’t installed. Most of a clean report is these. The two that matter are SUGGESTION and WARNING.
At the end, Lynis collects both into two blocks:
Warnings (1): ---------------------------- ! klogd is not running, which could lead to missing kernel messages in log files [LOGG-2138] https://cisofy.com/lynis/controls/LOGG-2138/
Suggestions (42): ---------------------------- * Configure password hashing rounds in /etc/login.defs [AUTH-9230] * Determine why /vmlinuz or /boot/vmlinuz is missing on this Debian/Ubuntu system. [KRNL-5788] * Install a PAM module for password strength testing like pam_cracklib or pam_passwdqc or libpam-passwdqc [AUTH-9262] * To decrease the impact of a full /tmp file system, place /tmp on a separate partition [FILE-6310] * Configure a firewall/packet filter to filter incoming and outgoing traffic [FIRE-4590]A warning means Lynis thinks something is wrong or risky. A suggestion means something could be tighter. Lynis does not rank by importance inside either list. The FILE-6310 suggestion about /tmp sits next to the missing-firewall one with equal visual weight, and they are not remotely equal.
The bracketed code is the test ID: a 4-letter category and a number. AUTH- is authentication, FILE- is filesystems, FIRE- is firewalls, SSH- is SSH, KRNL- is kernel. The ID is your handle for everything else: the URL, the log, the skip list.
The finish block gives the number everyone fixates on:
Lynis security scan details:
Details: Hardening index : 59 [########### ] Tests performed : 228
Software components: - Firewall [X] - Intrusion software [X] - Malware scanner [X]The Two Files That Matter
Every run writes two files, and the screen is only a summary of them.
/var/log/lynis.log is the full trace. It records every test performed, why it passed or failed, and the command it ran. /var/log/lynis-report.dat is the machine-readable result: one key=value per line, with repeating keys for lists.
report_datetime_start=2026-10-30 13:02:06lynis_version=3.1.7os_name=Debianhardening_index=59warning[]=LOGG-2138|klogd is not running, which could lead to missing kernel messages in log files|-|-|suggestion[]=AUTH-9230|Configure password hashing rounds in /etc/login.defs|-|-|suggestion[]=KRNL-6000|One or more sysctl values differ from the scan profile and could be tweaked||Change sysctl value or disable test (skip-test=KRNL-6000:<sysctl-key>)|tests_executed=HRDN-7231|HRDN-7230|HRDN-7222|...Each warning[] and suggestion[] line is TEST-ID|message|details|solution|. The details field is where Lynis puts the specific item, for example which SSH option to change. That makes the report trivial to grep:
sudo grep -E '^(warning|suggestion)\[\]=' /var/log/lynis-report.dat | cut -d'|' -f1,2Why Is Lynis Suggesting This?
A one-line suggestion rarely tells you enough. Ask the log:
sudo ./lynis show details SSH-7408Performing test ID SSH-7408 (Check SSH specific defined options)Test: Checking AllowTcpForwarding in /tmp/lynis.tiCtFWdybcResult: Option AllowTcpForwarding value is YESResult: OpenSSH option AllowTcpForwarding is in a weak configuration state and should be fixedSuggestion: Consider hardening SSH configuration [test:SSH-7408] [details:AllowTcpForwarding (set YES to NO)] [solution:-]Hardening: assigned partial number of hardening points (0 of 3). Currently having 84 points (out of 131)That last line explains the hardening index. Each test is worth points, and the index is points earned divided by points possible. A suggestion on a 3-point test costs you 3. It also tells you what the number measures: how many of Lynis’s opinions your box agrees with. It says nothing directly about how safe the box is.
Two traps with these commands, both verified on 3.1.7:
show detailstakes the test ID as its only argument. Add--no-colorsafter it and it prints nothing.- Any
lyniscommand resets/var/log/lynis-report.datto a header. Even./lynis show versiondoes it. Run yourshowcommands after you have read, copied, or parsed the report, never before.
Triage: What Matters on a Home-Lab Box
A real report has dozens of findings. Sort them into three piles.
Fix now. These shrink real attack surface and cost almost nothing:
SSH-7408hardening:MaxAuthTries,X11Forwarding,AllowAgentForwarding,LogLevel.FIRE-4590no firewall. If anything listens on a network you don’t fully trust, you want one.PKGS-7420no automatic security updates.AUTH-9328a default umask of 022 when 027 is cheap.- Any
WARNINGyou can’t explain. Read it first.
Fix if cheap. Worth doing when you have 10 minutes:
PKGS-7370andPKGS-7394(debsums,apt-show-versions) if you like package verification.KRNL-6000sysctl values, one key at a time. Read thedetailsfirst.ACCT-9628auditd, only if you will read the logs.
Silence. These are right for a datacenter and wrong for your closet:
BANN-7126andBANN-7130legal banners. Nobody is being warned.FILE-6310separate/tmp,/var,/homepartitions on a single-filesystem box.USB-1000andSTRG-1846when you actually use those ports.ACCT-9622process accounting when you already run metrics.
The rule: every finding gets one of three outcomes. Fix it, accept it on purpose, or silence it with a reason written next to it. “I’ll get to it” is not an outcome.
Fixing a Few Real Ones
I ran three fixes in the container and re-ran the audit. SSH first, using a drop-in file so the main config stays untouched:
sudo tee /etc/ssh/sshd_config.d/10-lynis.conf >/dev/null <<'EOF'MaxAuthTries 3X11Forwarding noAllowAgentForwarding noLogLevel VERBOSETCPKeepAlive noClientAliveCountMax 2EOFsudo sshd -t && sudo systemctl reload sshAlways run sshd -t before reloading. A typo in a config you apply over SSH is how you end up walking to the closet. Keep your current session open and test a second login before you close it. The unit is called ssh on Debian and Ubuntu, sshd on most other distros.
I left AllowTcpForwarding alone on purpose. SSH tunnels are how a lot of home labs reach internal dashboards, and disabling them to please Lynis is the exact trade this article is warning about.
Next, the umask and the package tooling:
sudo sed -i 's/^UMASK.*/UMASK 027/' /etc/login.defssudo apt install debsums apt-show-versions unattended-upgradesThe sed only works if a UMASK line already exists. In my container there was none, so I appended UMASK 027 instead. Check with grep UMASK /etc/login.defs. Be aware that 027 means new files are unreadable to “other”. Services that write files other users must read (a shared media folder, say) need testing after this change.
Re-run:
Before: hardening_index=59 warnings=1 suggestions=42After: hardening_index=68 warnings=1 suggestions=33Three changes, nine fewer suggestions, nine more index points. That is what a normal session looks like. Notice what did not happen: no heroic rewrite, no 100.
The one remaining warning, LOGG-2138, is a container artifact (no syslog daemon). On a real host a running rsyslog or the systemd journal handles it.
Silencing the Irrelevant: custom.prf
Lynis reads default.prf from its own directory, then your overrides from /etc/lynis/custom.prf (or a custom.prf next to default.prf in a checkout). Never edit default.prf. It gets replaced on upgrade. Put your decisions in custom.prf, one skip-test= per line:
# USB-1000: this box has USB ports, and I plug in drives on purpose.skip-test=USB-1000
# FILE-6310: single filesystem with subvolumes, test cannot see the separation.skip-test=FILE-6310
# KRNL-6000 checks many sysctl keys. Skip only the one this box needs.# This host runs Docker, which requires IPv4 forwarding.skip-test=KRNL-6000:net.ipv4.conf.all.forwarding
# SSH-7408 checks many options. Skip only the Port suggestion.skip-test=SSH-7408:portThe TEST-ID:sub-item form skips one item inside a test and keeps the rest. That is the right tool for KRNL-6000 and SSH-7408, which bundle many checks under one ID. The syntax comes from default.prf itself, and the KRNL-6000:<sysctl-key> hint is printed in the report’s own solution field.
Confirm Lynis picked the file up. The audit header lists it:
Profiles: /opt/l/lynis/default.prf /etc/lynis/custom.prfWith ten skips from the kit’s custom.prf, my container went from 33 suggestions to 24. The index went from 68 to 67. Skipping does not inflate the score, because a skipped test leaves both the earned and the possible points. I can’t explain the one-point drop from the data I have, so treat it as an observation.
Every skip line gets a comment saying why. In six months you will not remember. Also use --profile to point at a different profile file when you want a separate policy for a different class of box:
sudo ./lynis audit system --quick --profile ./server.prfTrack the Index Weekly
A single audit is a snapshot. The value is the trend: you change a thing, and a week later the number tells you whether the box drifted.
The kit’s lynis-trend.sh runs the audit with --cronjob (it implies audit system, quick mode, and no colors) and appends one row to a CSV:
"$LYNIS" audit system --cronjob > /dev/null
row=$(awk -F= ' /^hardening_index=/ { idx = $2 } /^lynis_version=/ { ver = $2 } /^warning\[\]=/ { w++ } /^suggestion\[\]=/ { s++ } END { if (idx == "") exit 1; printf "%s,%d,%d,%s", idx, w, s, ver }' "$REPORT") || { echo "no hardening_index in $REPORT" >&2; exit 1; }
echo "$(date +%F),$row" >> "$CSV"I ran it twice in the container:
date,hardening_index,warnings,suggestions,lynis_version2026-10-30,68,1,33,3.1.72026-10-30,68,1,33,3.1.7Same number twice, which is what you want from a box that didn’t change. When the number drops, something did change: a new package, a loosened config, a service you forgot you enabled.
A systemd timer runs it weekly. The units are in the kit:
[Timer]OnCalendar=Sun *-*-* 03:30RandomizedDelaySec=15minPersistent=truesudo install -m 755 lynis-trend.sh /usr/local/bin/sudo cp lynis-trend.service lynis-trend.timer /etc/systemd/system/sudo systemctl daemon-reloadsudo systemctl enable --now lynis-trend.timerPersistent=true catches up a missed run after the machine was off. If you run Lynis from a tarball, uncomment the Environment=LYNIS= line in the service file. The script changes into that directory itself, because Lynis refuses to run from anywhere else.
There is one more gotcha. If a Lynis run gets killed midway (for example by piping its output to head), it can leave a stale /var/run/lynis.pid. The next --cronjob run then prints “Quitting, to prevent multiple cron jobs running at the same time” and exits 1. Delete the PID file once you are sure nothing is running.
For alerts, a one-line check on the CSV is enough. Compare the last two hardening_index values and mail yourself when the newer one is lower.
When Lynis Isn’t Enough
Lynis is an opinionated checklist that reads your box and tells you what it thinks. It fits a home lab or a small fleet well. If you need to prove compliance against a named standard (CIS Benchmarks, STIG, PCI), step up to OpenSCAP with the matching SCAP Security Guide profile. It gives pass/fail per rule, can remediate, and produces the evidence an auditor wants. It is also much heavier to run and read. Start with Lynis. Move when someone with a clipboard asks.
Common Questions
Is Lynis safe to run on a production server?
Yes. Lynis audits read-only: it inspects configuration files, running services, and kernel settings, and changes no system configuration. It writes only its log, report, PID, and temp files. A full run takes about a minute and uses little CPU. Run it first on a staging copy if your change policy requires proof.
Does Lynis fix anything automatically?
No. Lynis only reports findings and suggests fixes. You apply every change yourself, by editing configs, installing packages, or setting sysctl values. That is deliberate: automatic hardening can lock you out of SSH or break services. Use a config-management tool or a hardening role if you want changes applied automatically.
What is a good Lynis hardening index?
Lynis publishes no pass mark. The index is points earned divided by points possible across the tests that ran, so it varies with what is installed. A fresh Debian 13 container scored 57 to 59 across my runs. Use your own first score as the baseline and watch the trend instead of chasing a target.
How do I make Lynis ignore a test?
Add skip-test=TEST-ID to /etc/lynis/custom.prf, one per line, for example skip-test=BANN-7126. To skip one item inside a multi-check test, use skip-test=SSH-7408:port. Lynis lists the profile files it loaded in the audit header, so check there that your file was read. Put a comment above each skip line that says why.
Where does Lynis store its results?
Lynis writes the full trace to /var/log/lynis.log and the machine-readable result to /var/log/lynis-report.dat. Both are overwritten on each run. The --report-file flag changes the report path, and --no-log skips the log. Copy the report after each run if you want history, or use a trend script.