
You can harden a server for weeks and still miss the one setting that bites you later. Linux VPS security auditing gives you a repeatable way to catch the gaps that actually matter: sloppy SSH policy, unexpected listeners, stale packages, noisy auth logs, and kernel/sysctl defaults that don’t match how the box is used.
This post sticks to a safe baseline. You’ll run an audit, read the report like an engineer (not a scorekeeper), apply changes that won’t strand you without SSH, and set up simple drift tracking so you notice when risk creeps back in.
Scenario: a small API VPS that needs a defensible baseline
Assume you run one VPS hosting an internal JSON API plus a small admin UI. You’re not chasing “perfect.” You want a baseline you can defend in a review: “We audit on a schedule, we keep the reports, and we fix the highest-risk findings first.”
- Distro: Ubuntu Server 24.04 LTS (similar steps work on Debian 12)
- Services: OpenSSH on port 22, Nginx reverse proxy, app listens on 127.0.0.1:8087
- Goal: audit + baseline fixes + verification + ongoing checks
If you want a clean Linux environment to apply these controls end-to-end, start with a HostMyCode VPS so you own the kernel, firewall, and SSH policy. If you’d rather offload OS upkeep and routine maintenance, managed VPS hosting is the better fit.
What you’ll get out of Linux VPS security auditing (and what you won’t)
An audit tool can’t prove you’re secure. What it can do is give you quick, repeatable signal:
- Surface blind spots (forgotten services, risky permissions, weak crypto policy).
- Prioritize changes so you don’t spend a day polishing low-impact items.
- Create a baseline you can rerun after patches, migrations, or incidents.
For threat modeling and response planning, keep this paired with your runbooks. HostMyCode’s this VPS incident response checklist is a solid practical reference.
Prerequisites
- Root access (or sudo) to a Linux VPS
- Out-of-band access option (cloud console or recovery) if possible
- Basic comfort with SSH and editing files in
/etc - 10–20 minutes of downtime tolerance not required (we’ll avoid disruptive changes)
Before you touch security settings, make sure you can roll back. That usually means a verified backup plus a plan to restore. If you don’t already have that written down, keep this bookmarked: VPS backup strategy with restic + S3.
Step 1: capture a “before” snapshot of your current state
Audits are most useful when you can answer one question later: “What changed?” Start by recording a few basics.
-
Check your OS and kernel:
lsb_release -a uname -aExpected output looks like:
Description: Ubuntu 24.04.2 LTS Linux vps-api-01 6.8.0-xx-generic #... SMP ... x86_64 GNU/Linux -
List listening ports (this is where “surprise services” show up):
ss -lntup | sed -n '1,40p'You want to see only what you expect, for example:
tcp LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:("sshd",pid=1123,fd=3) tcp LISTEN 0 4096 0.0.0.0:80 0.0.0.0:* users:("nginx",pid=1401,fd=6) tcp LISTEN 0 4096 0.0.0.0:443 0.0.0.0:* users:("nginx",pid=1401,fd=7) tcp LISTEN 0 4096 127.0.0.1:8087 0.0.0.0:* users:("api",pid=2310,fd=10) -
Quick look at recent auth events:
sudo journalctl -u ssh --since "24 hours ago" --no-pager | tail -n 30
If disk space is already tight, deal with that first. Audit output and logs need room to breathe, and a full filesystem can turn routine tasks into outages. See VPS disk space troubleshooting.
Step 2: run Lynis and get a real report (not just a score)
Lynis remains one of the most useful “broad sweep” tools for Linux servers in 2026. It checks OS hardening, service exposure, logging, and common security settings. Treat the output like a punch list, not a verdict.
-
Install Lynis from Ubuntu repositories:
sudo apt update sudo apt install -y lynis -
Run an audit:
sudo lynis audit systemDuring the run, you’ll see tests like:
[+] Boot and services [+] Kernel [+] Memory and processes [+] Users, groups and authentication [+] Shells [+] Networking [+] Printers and spools [+] Scheduled tasks [+] Accounting [+] Logging and files -
Open the report and focus on warnings and suggestions:
sudo less /var/log/lynis-report.dat sudo less /var/log/lynis.log
Ignore the “Hardening index” as a goal. What matters is whether the findings map to real risk on your VPS: SSH policy, exposed services, patch status, firewall posture, and whether you can reconstruct events from logs.
Step 3: fix the high-signal items first (SSH, updates, firewall posture)
Lynis can produce a long list. Start with changes that reduce exposure quickly and rarely break production.
3.1 Lock down SSH without locking yourself out
Before you edit SSH, open a second root session and leave it open. That’s your safety line if you make a mistake.
-
Edit your SSH server config:
sudo nano /etc/ssh/sshd_configApply a conservative baseline (adapt to your environment):
# /etc/ssh/sshd_config Port 22 Protocol 2 PermitRootLogin no PasswordAuthentication no KbdInteractiveAuthentication no PubkeyAuthentication yes AuthenticationMethods publickey AllowUsers deploy opsadmin X11Forwarding no AllowAgentForwarding no AllowTcpForwarding no ClientAliveInterval 300 ClientAliveCountMax 2 MaxAuthTries 4 LoginGraceTime 30 LogLevel VERBOSE -
Validate the config before restart:
sudo sshd -t echo $?0means syntax is OK. -
Restart SSH safely:
sudo systemctl restart ssh sudo systemctl status ssh --no-pager -
Verify from a new terminal:
ssh deploy@YOUR_SERVER_IP
If your access model is more complex (bastion, ProxyJump, MFA, audit logging), this guide stays practical: SSH bastion host setup.
3.2 Apply security updates with a controlled approach
On most VPS audits, the loudest finding is simple: pending updates. Patch first, then re-audit, so you’re not fixing issues that disappear after upgrades.
-
See what’s pending:
apt list --upgradable 2>/dev/null | sed -n '1,25p' -
Upgrade:
sudo apt update sudo apt -y full-upgrade -
Remove unused packages:
sudo apt -y autoremove --purge -
Reboot if the kernel or core libraries changed:
sudo reboot
If you want a safer cadence (staging, snapshots, maintenance windows, rollback), follow VPS patch management in 2026 as the operating model.
3.3 Confirm firewall posture (and logging) matches reality
Lynis won’t manage your firewall for you. It only checks whether one exists and whether the setup looks reasonable. If you already run nftables, keep it. If you use UFW, keep it. The non-negotiable part is exposure: only publish what must be public.
Quick verification:
sudo ufw status verbose || true
sudo nft list ruleset | sed -n '1,120p' || true
If you’re on nftables and want audit-friendly rules plus safe rollbacks, use VPS firewall logging with nftables.
Step 4: use systemd-analyze to find risky boot-time and service behavior
This is where a lot of teams miss real issues. Auditing isn’t only crypto and permissions; it’s also “why is that daemon running?” and “what changed since last month?”
-
See slow or unusual boot services:
systemd-analyze blame | head -n 20 -
List enabled units (hunt for things you never meant to run):
systemctl list-unit-files --state=enabled | sed -n '1,80p' -
Inspect a suspicious service:
systemctl cat SERVICE_NAME systemctl status SERVICE_NAME --no-pager
Common surprises on VPS images: remote management agents you didn’t choose, mail daemons you never use, or dev tooling that should have been removed after setup. Disable first and watch for fallout; uninstall later once you’re confident.
Step 5: tighten logging and audit signals (without filling the disk)
An audit only pays off if you can answer “what happened?” later. That requires predictable logging, sane retention, and rotation that won’t eat the filesystem.
5.1 Make journald persistent (but bounded)
Some images keep journald in volatile storage. After a reboot, your evidence disappears, which makes incident review painful.
-
Edit journald config:
sudo nano /etc/systemd/journald.confAdd (or adjust) these values:
# /etc/systemd/journald.conf Storage=persistent SystemMaxUse=600M SystemMaxFileSize=80M MaxRetentionSec=14day -
Apply:
sudo systemctl restart systemd-journald
For a broader setup (journald + Nginx + app logs), this is a good reference: VPS log rotation best practices in 2026.
5.2 Enable unattended audit signals (optional, but useful)
For many VPS workloads, a full auditd rule set is more weight than you need. A solid middle ground is making sure auth, sudo, and SSH events are consistently captured and easy to search. If you do run auditd, keep rules narrow and centered on privileged actions.
At minimum, confirm sudo logging works and is visible:
sudo journalctl -t sudo --since "7 days ago" --no-pager | tail -n 20
Step 6: address common Lynis findings with safe, concrete changes
Every server will produce a slightly different report. The fixes below show up constantly on real VPS hosts and tend to be low-risk if you apply them with care.
6.1 Remove or lock unused accounts
List users with a shell:
awk -F: '($7 ~ /(bash|sh|zsh)$/){print $1":"$6":"$7}' /etc/passwd
If you find an old user you no longer need:
sudo usermod -L olduser
sudo usermod -s /usr/sbin/nologin olduser
On production, don’t delete accounts on impulse. First confirm you’re not about to break cron jobs, systemd services, or deploy keys tied to that user.
6.2 Fix world-writable files and odd permissions
Search common local filesystems (skip virtual mounts):
sudo find / -xdev -type f -perm -0002 2>/dev/null | head -n 50
If you discover something like /opt/api/tmp/debug.log is world-writable, tighten it:
sudo chmod o-w /opt/api/tmp/debug.log
Then confirm the app still writes logs. This is a classic “looks safe, breaks quietly” change when an app logs to the wrong directory.
6.3 Review kernel and network sysctls (don’t cargo-cult)
Lynis will suggest sysctl tweaks. Only apply settings that match your role. For a typical VPS that doesn’t route traffic for other systems, these are usually sensible:
sudo tee /etc/sysctl.d/99-hostmycode-baseline.conf >/dev/null <<'EOF'
# HostMyCode baseline sysctl (review before use)
net.ipv4.ip_forward=0
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.default.send_redirects=0
net.ipv4.conf.all.rp_filter=1
net.ipv4.conf.default.rp_filter=1
net.ipv4.tcp_syncookies=1
kernel.kptr_restrict=2
fs.protected_hardlinks=1
fs.protected_symlinks=1
EOF
sudo sysctl --system
Verification:
sysctl net.ipv4.ip_forward kernel.kptr_restrict
Expected output:
net.ipv4.ip_forward = 0
kernel.kptr_restrict = 2
Pitfall: if you run Docker with custom networking, Kubernetes, or act as a VPN gateway, you may need exceptions. This is the moment to document those choices, not “win” points in the report.
6.4 Confirm TLS endpoints and reverse proxy configuration
Lynis can flag old protocols and weak ciphers, but it doesn’t understand your full routing. If you use Nginx, confirm what’s actually enabled:
sudo nginx -T 2>/dev/null | grep -E "ssl_protocols|ssl_ciphers|listen 443" | head -n 40
Then verify from your workstation:
curl -sI https://your-api.example.com | head -n 10
If you host multiple apps behind one VPS, keep the proxy config boring and consistent. This guide is a strong reference: Nginx reverse proxy on a VPS.
Step 7: re-run the audit and track drift over time
After you make changes, rerun Lynis and save the delta. The simplest workflow is a timestamped archive of the report and a separate findings file.
-
Create an audit directory:
sudo mkdir -p /var/lib/security-audits/lynis -
Run Lynis and archive the report:
ts=$(date -u +%Y%m%dT%H%M%SZ) sudo lynis audit system --quiet sudo cp /var/log/lynis-report.dat /var/lib/security-audits/lynis/lynis-report-${ts}.dat sudo cp /var/log/lynis.log /var/lib/security-audits/lynis/lynis-${ts}.log -
Extract the warnings and suggestions into a greppable file:
sudo lynis show report | sed -n '/Warnings/,/Suggestions/p' | sudo tee /var/lib/security-audits/lynis/findings-${ts}.txt >/dev/null sudo wc -l /var/lib/security-audits/lynis/findings-${ts}.txt
What you’re looking for on the second run: fewer high-risk warnings around SSH, firewall state, and patching. Don’t try to clear every suggestion the same day. Capture it, decide if it’s worth the tradeoff, and move on.
Verification checklist (what “good” looks like)
- SSH: password auth off, root login off, only known users allowed
- Ports:
ss -lntupshows only intended public listeners (typically 22/80/443) - Updates: no large backlog of upgradable security packages
- Logs: journald persistent with bounded retention; auth events visible
- Lynis: warnings reduced; remaining items documented as accepted risk
Common pitfalls you’ll hit (and how to avoid them)
- Breaking SSH access: always keep a second session open; run
sshd -tbefore restarting. - Chasing the hardening score: optimize for risk reduction, not a number. Some “findings” conflict with your workload.
- Applying sysctl changes blindly: VPN gateways, containers, and routing hosts often need exceptions.
- Audit without retention: if you don’t save reports, you can’t prove improvement or spot drift.
- Log growth surprise: set bounds on journald and rotate app logs, or your “security effort” becomes a disk incident.
Rollback strategy (keep it boring and reliable)
Security changes should be easy to undo. Plan the rollback before you edit configs.
- SSH rollback:
- Keep a root console path (cloud console / recovery mode).
- Before edits:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date -u +%Y%m%dT%H%M%SZ) - If locked out via console: restore the backup and restart SSH.
- sysctl rollback:
- Move the baseline file out of the way:
sudo mv /etc/sysctl.d/99-hostmycode-baseline.conf /root/ - Re-apply defaults:
sudo sysctl --system
- Move the baseline file out of the way:
- package rollback:
- If an upgrade breaks a service, pin and downgrade only the affected packages (document the reason).
- For fast recovery, snapshots plus file-level backups are the practical answer. See VPS snapshot backup automation.
Next steps: turn one audit into a habit
Once the baseline is in place, the real win is consistency.
- Schedule monthly audits: run Lynis, archive the report, review findings for real risk.
- Alert on unexpected listeners: a simple daily check of
ss -lntupdiffed against a known-good list catches “mystery ports.” - Add monitoring tied to security signals: auth spikes, repeated SSH failures, disk growth from logs. If you want a low-noise setup, see Linux VPS monitoring with Prometheus and Grafana.
- Write down exceptions: if you keep a risky setting for a reason, document it in
/var/lib/security-audits/README.txt.
Summary
Linux VPS security auditing works best as a loop: measure, fix, verify, repeat. Lynis gives you fast coverage; your job is choosing changes that reduce real exposure without destabilizing the server. Save the reports, track drift, and treat each finding as a tradeoff you can explain.
If you want a VPS environment where you can apply these baselines cleanly (and reuse them across multiple servers), start with HostMyCode VPS or choose managed VPS hosting if you prefer help with ongoing maintenance under the same “Affordable & Reliable Hosting” approach.
Audits go faster when the underlying setup stays consistent: known images, clear network policy, and snapshots you can trust. HostMyCode provides that foundation on HostMyCode VPS. If your team is small, managed VPS hosting can cover the routine upkeep while you focus on the application.
FAQ
How often should I run Lynis on a VPS?
Monthly is a solid baseline for most teams. Also run it after major upgrades, migrations, or any change that adds ports or introduces new services.
Should I fix every Lynis suggestion?
No. Prioritize the findings that reduce risk for your workload: SSH policy, patching, exposed services, logging, and file permissions. For anything you keep, write down the reason.
Can Lynis replace a firewall, IDS, or WAF?
No. Lynis is a local audit tool. It helps you verify configuration and spot misconfigurations; it doesn’t block traffic or provide full real-time detection.
What’s the safest way to change SSH settings remotely?
Keep an active second session, validate with sshd -t, restart the service (don’t reboot), and confirm a new login works before closing your safety session.
What should I do if audits keep flagging “unknown services”?
Start with ss -lntup and systemctl status to identify what’s listening and why. Disable first, watch the system, then uninstall only if it’s clearly unused.