Linux runs most of the internet's servers, which makes Linux server security less of a specialty topic and more of a baseline responsibility for anyone who administers one — a single VPS, a fleet of production servers, or anything in between. This is a complete map of that responsibility: permissions, SSH, user management, firewalls, logging, hardening, updates, and the single principle — least privilege — that ties all of it together. Each section links out to a focused, hands-on guide and a free browser-based tool for the parts that benefit from one, so this page works as both a starting point and a place to come back to.
Why Linux security matters
A freshly provisioned Linux server is not a secure one — it's a default one, tuned for compatibility and ease of setup rather than for standing exposed on the internet. Linux hardening is the deliberate, ongoing process of closing that gap. Two ideas do most of the work: attack surface — everything on a system that could potentially be exploited, from a running service to an unused account — and defense in depth — the principle that no single control should be the only thing standing between an attacker and the system, so one misconfiguration doesn't mean total exposure. Neither is a one-time task. New packages get installed, new accounts get added, and without periodic review a hardened server drifts back toward a default one, one convenient exception at a time.
This guide covers eight areas that, together, cover the large majority of what "Linux security" actually means in practice for a server you administer directly — bare metal or a VM, reachable over the network, running for months or years rather than minutes. Containers and managed cloud services share some underlying concerns but have their own separate hardening practices this guide doesn't attempt to cover.
Linux permissions
Every file and directory on a Linux system has an owner, a group, and three permission scopes — owner, group, and others — each independently granted some combination of read, write, and execute. This is the most basic access control layer on the system, and it's also the one most frequently misconfigured, usually in the direction of being too permissive: chmod 777 as a quick fix for a permission error is one of the most common and most damaging Linux mistakes, since it grants write access to every account on the system, not just the one that actually needed it.
The three bits mean something different on a directory than on a file, which trips people up constantly: read on a directory allows listing its contents (ls), write allows creating or deleting entries inside it — notably, deleting a file only requires write access to its directory, not to the file itself, which is a common source of "why can I delete this file I don't own" confusion — and execute allows actually entering it (cd) or accessing anything inside by path. A directory with read but no execute permission is a genuinely common, confusing result in practice: you can list filenames inside it but can't open, stat, or cd into any of them.
Beyond the basic read/write/execute bits, three special permissions carry real security weight: setuid runs an executable with its owner's privileges rather than the caller's (the mechanism passwd relies on to write to a file regular users can't touch directly); setgid does the same for group membership, or, on a directory, makes every new file inside inherit that directory's group automatically; the sticky bit restricts deleting files inside a world-writable directory to each file's own owner — exactly why /tmp is 1777 rather than a plain 777.
Permissions and ownership are also frequently confused with each other: chmod controls what each scope can do to a file that already has an owner; chown controls who that owner and group actually are. Reaching for chmod 777 to fix what's really an ownership mismatch — a file created by root during setup that a service account now needs to write to — is the single most common reason that particular anti-pattern shows up in the wild; the real fix is chown to the correct account, not opening the file to everyone. See chmod vs chown for the full distinction, including the asymmetry that any user can chmod a file they own but only root can give a file away to someone else.
For working out or setting permissions in either notation, without recalling exact syntax from memory: Linux Permissions Calculator converts between checkboxes, octal (755), and symbolic (rwxr-xr-x) notation, including the three special bits, with the exact chmod command ready to copy. When the change is a relative edit rather than a final state — "add execute for the group" rather than "set this to 750" — chmod Command Generator builds the symbolic command (u+rwx, g-w, o=r) from clauses instead, including the recursive flags and the capital-X idiom that avoids making every plain file executable when recursing over a mixed tree.
SSH security
SSH is usually the only remote administrative access point on a server, which makes it both essential and the single highest-value target to secure carefully. SSH key authentication is asymmetric — a challenge-response signature, not a password or a key transmission: the server holds only your public key, sends a random challenge on connect, and your client signs it with the private key that never leaves your machine. A public key leaking is not an incident; a private key leaking is.
The highest-impact configuration changes, in /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
MaxAuthTries 3
AllowUsers deploy alice
ClientAliveInterval 300
ClientAliveCountMax 2PermitRootLogin no forces every administrative action to be tied to a named, identifiable account rather than an anonymous shared root login. PasswordAuthentication no is the single highest-impact change available — it eliminates brute-force password guessing as an attack path entirely, since there's no password to guess. MaxAuthTries limits attempts per connection, and AllowUsers restricts login to specific accounts, useful when a service account exists in /etc/passwd but should never itself be an SSH login target. Always keep a second terminal session open while testing a change like this — a mistake in this file can lock you out entirely, with no way back in except console access.
fail2ban is worth deploying specifically alongside SSH, not just as a general logging tool: its default configuration already watches sshd on most distros, and a minimal local override in /etc/fail2ban/jail.local tunes the response:
[sshd]
enabled = true
maxretry = 5
bantime = 3600That bans an offending address for an hour after five failed attempts, turning a slow, persistent brute-force attempt into a quickly-blocked one. For anything genuinely high-value, a PAM module like Google Authenticator adds a second factor on top of key-based authentication — real additional protection, at the cost of real additional operational complexity most small setups don't need day to day. Changing the default port (22) is a much smaller measure worth a brief mention since it comes up constantly: it reduces automated-scanner log noise and nothing more, doing nothing against a targeted attacker who simply scans the host for open ports.
Three tools cover the practical side of SSH end to end: SSH Key Generator produces a real Ed25519 or RSA key pair, formatted exactly as OpenSSH expects, entirely in your browser. SSH Key Inspector reads an existing public key — RSA, Ed25519, or ECDSA — and reports its type, SHA256 fingerprint, bit length, and algorithm details, matching ssh-keygen -l exactly. SSH Config Generator builds the ~/.ssh/config block that ties a host alias to a specific key, port, and jump host, so you never retype a long ssh command by hand again. For the full mechanics of how key authentication actually works and how RSA, Ed25519, and ECDSA compare, see SSH Keys Explained; for the config file's own syntax, precedence rules, and the directives beyond the basics, see SSH Config File Guide.
User management
Every account on a server should map to exactly one identifiable purpose — a specific person, or a specific service — never a shared login used by more than one person or script. Shared accounts make it impossible to know who actually did what during an incident review, which defeats the entire purpose of logging in with credentials at all rather than anonymously.
The root account deserves particular care: lock its password (sudo passwd -l root) so it can't be used for direct login even if SSH root login were ever accidentally re-enabled, and administer the system through named accounts with sudo instead. Edit sudo's configuration only with visudo, never a plain text editor — it validates syntax before saving, so a typo can't silently leave the file unparsable the next time anyone needs sudo at all. Avoid blanket NOPASSWD: ALL grants; if a specific automated task genuinely needs passwordless sudo, scope the exception to that one command:
# Narrow: only this one command, passwordless
deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp
# Avoid: unrestricted passwordless root
deploy ALL=(ALL) NOPASSWD: ALLFor any account still authenticating with a password, enforce aging (chage -M 90 -W 7 username — expire after 90 days, warn 7 days ahead) and complexity at the PAM layer via pam_pwquality (/etc/security/pwquality.conf, typically minlen and minclass), so a weak password is rejected at creation time rather than relying on people to choose well voluntarily. Service accounts that run one specific daemon and never need an interactive login should be created with a shell that refuses one (/usr/sbin/nologin) rather than a normal shell they'll never legitimately use — a small change that removes an entire login path an attacker might otherwise try.
Group membership is the other half of user management worth reviewing periodically, not just at account creation: groups username shows what a given account can actually reach through group-granted permissions, and membership in the system's administrative group (sudo on Debian/Ubuntu, wheel on Fedora/RHEL) is specifically worth auditing, since it's the difference between an account that can only affect its own files and one that can affect the whole system. Accounts accumulate group memberships the same way permissions and firewall rules accumulate cruft — added for a task that ended months ago and never revoked.
Every sudo invocation is normally logged by default to /var/log/auth.log (Debian, Ubuntu) or /var/log/secure (Fedora, RHEL) — worth confirming rather than assuming, since that log is exactly what answers "who ran what as root, and when" during an incident review, tying user management directly into the logging section below.
Firewalls
A firewall's job is simple to state and easy to get backwards in practice: deny everything incoming by default, then explicitly allow only what this specific server needs to expose. Ubuntu and Debian ship UFW, a friendly frontend over the kernel's packet filter; Fedora and RHEL use firewalld instead — different syntax, identical underlying goal:
# UFW (Ubuntu/Debian)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw enable
# firewalld (Fedora/RHEL)
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --set-default-zone=public
sudo firewall-cmd --reloadNotice the order: SSH is allowed before the firewall is enabled or reloaded with its new default-deny policy. Getting this backwards — enabling the firewall first with no SSH rule in place — cuts off remote access immediately, often with no way to fix it except console access. Beyond SSH, allow only the specific ports the server's actual services need; a database server generally needs nothing open to the public internet at all, only to specific application servers on a private network. Firewall rules also accumulate cruft — a rule for a service decommissioned six months ago doesn't remove itself, so a periodic review belongs on the same schedule as the rest of this list.
A firewall controls what can reach a service from the network; it does nothing about services that shouldn't be reachable in the first place. ss -tulnp shows every port actually listening and which process owns it — the ground truth for what the firewall needs to cover, rather than guessing from a service list. A service that only needs to talk to other processes on the same machine should bind to 127.0.0.1 rather than 0.0.0.0 wherever its own configuration allows it, making it unreachable from the network layer entirely regardless of firewall rules. And if a dual-stack host doesn't use IPv6 at all, either firewall it with the same care as IPv4 or disable it outright — a common real gap is hardening the IPv4 firewall thoroughly while leaving IPv6 wide open on the same machine, effectively an unguarded second front door.
A firewall and a properly configured SSH server address different attack surfaces than the operating system's own mandatory access control layer — SELinux (enforcing by default on Fedora and RHEL) or AppArmor (Ubuntu's default, also supported on Debian) confines what an already-running process is allowed to do on the machine itself, which matters specifically when a network-facing service gets compromised despite the firewall working exactly as configured.
Logging
Hardening reduces the chance of compromise; logging is how you find out one happened anyway — the two aren't substitutes for each other, and a server needs both. At minimum, install and enable an audit framework (packaged as auditd on Debian/Ubuntu, audit on Fedora/RHEL) rather than relying on default system logs alone, and install fail2ban to automatically firewall off source addresses after repeated authentication failures — turning a slow brute-force attempt into a quickly-blocked one:
sudo systemctl enable --now auditd
sudo systemctl enable --now fail2banBeyond the daemons themselves: ship logs off the host when practical — a remote syslog target or a managed logging service means an attacker who fully compromises the machine can't also silently delete the local evidence of it. Set a log rotation policy so logs don't quietly fill the disk (most distros configure logrotate reasonably out of the box, worth confirming for anything that logs heavily). And actually review logs on a schedule — collecting logs nobody looks at catches nothing; even a lightweight weekly skim of authentication failures and sudo usage catches problems a purely automated system might miss.
Concretely, that weekly skim is looking for a small set of specific signals: repeated authentication failures against the same account (a brute-force attempt, or a legitimate user who needs their credentials reset — either way, worth knowing), a successful login at a time or from a location nobody on the team recognizes, sudo usage by an account that doesn't normally need it, and new user or group creation nobody remembers requesting. None of these require sophisticated tooling to notice — they require someone actually looking on a predictable schedule, which is the part automated logging alone doesn't provide.
Hardening
"Hardening" is really the sum of every section on this page applied deliberately and reviewed periodically, rather than a separate, distinct activity — but a few additional layers are worth knowing once the fundamentals above are solidly in place. Kernel-level network hardening via sysctl (net.ipv4.tcp_syncookies=1 for SYN flood protection, net.ipv4.conf.all.rp_filter=1 to reject implausible source addresses) tightens behavior the firewall alone doesn't reach. AIDE (Advanced Intrusion Detection Environment) takes a cryptographic snapshot of important system files and later flags any that changed, catching quiet tampering that wouldn't necessarily show up in a log. Lynis scans a running system end to end and reports specific hardening suggestions tailored to what it actually finds installed, catching anything a generic checklist wouldn't know to mention.
Rather than assembling this checklist by hand for a specific distribution, Linux Hardening Checklist Generator produces a complete, downloadable Markdown checklist covering exactly the areas on this page — system updates, SSH, the firewall, user and permissions review, and logging — with the correct commands for Ubuntu, Debian, Fedora, or RHEL, since the package names and tools genuinely differ (apt vs. dnf, UFW vs. firewalld, auditd vs. audit). For the full reasoning behind every item on that checklist, including a worked example of a realistic multi-service server configuration, see Linux Server Hardening Checklist.
Updates
This is foundational, not one item among equals — every other section on this page reduces what an attacker can do once they're in, or closes off a specific way in, but an actively exploited, unpatched vulnerability in exposed software routes around most of it regardless of how well everything else is configured. Start every hardening pass with a full update, and enable automatic security updates so the next one doesn't depend on someone remembering:
# Debian/Ubuntu
sudo apt update && sudo apt full-upgrade -y
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
# Fedora/RHEL
sudo dnf upgrade --refresh -y
sudo dnf install -y dnf-automatic
sudo systemctl enable --now dnf-automatic.timerAlongside updates, remove what you don't use — every installed package and running service is part of the attack surface whether or not it's ever touched again, and a default install often includes software a given server's actual role never needs. Know the distribution's support lifecycle too: running a release past its end-of-life date means security patches simply stop arriving, no matter how well everything else here is configured.
Least privilege
Every section above is really one principle applied to a different layer of the system: every account, process, and permission should have exactly the access it needs to do its job, and nothing more. A file permission scoped to the one account that needs it, an SSH login restricted to named users, a sudo grant scoped to one command instead of everything, a firewall that only opens the ports a service actually needs, a service account that can't log in interactively at all — these aren't six unrelated best practices. They're the same idea, applied consistently, everywhere it can be applied.
The practical value of naming it explicitly is that it gives a default answer to a question that comes up constantly during configuration: "should this be more permissive?" The least-privilege answer is almost always no — grant the specific access actually needed for the task in front of you, and expand it later, deliberately, if a genuine need shows up. Reversing that default — starting permissive and meaning to lock it down later — is how chmod 777, +all-equivalent firewall rules, and permanently-open NOPASSWD: ALL grants end up in production long after whatever justified them in the moment is forgotten.
A concrete example pulling several sections together: a deploy script needs to restart one application service after pushing new code. The least-privilege version of that setup is a dedicated deploy account (never a shared login), authenticated over SSH with a key restricted to that one account (AllowUsers deploy), granted sudo access scoped to exactly the one systemctl restart command it needs and nothing broader, with file ownership on the application directory set so the deploy account can write there without needing root at all for the actual file changes. Every one of those decisions is small on its own; together, a fully compromised deploy credential in that setup can restart one service and nothing else — a very different incident than a compromised credential with unrestricted sudo and a password-based login that also happened to work for the SSH root account.
Recommended DevTools Hub tools
Every tool referenced throughout this page, in one place:
- Linux Permissions Calculator — convert between octal, symbolic, and checkbox notation for file permissions, including the setuid/setgid/sticky special bits, with the exact
chmodcommand ready to copy. - chmod Command Generator — build a symbolic
chmodcommand from relative clauses (u+rwx,g-w,o=r) rather than an absolute final state, including recursive flags and the capital-Xsafety idiom. - SSH Key Generator — generate a real Ed25519 or RSA SSH key pair, formatted exactly as OpenSSH expects, entirely in your browser.
- SSH Key Inspector — inspect an existing SSH public key (RSA, Ed25519, or ECDSA) for its type, fingerprint, bit length, and algorithm details.
- SSH Config Generator — build a complete
~/.ssh/confighost block (User, Port, IdentityFile, ProxyJump, ForwardAgent) ready to copy or download. - Linux Hardening Checklist Generator — generate a downloadable, distro-specific Markdown checklist covering every section of this page for Ubuntu, Debian, Fedora, or RHEL.
Every one of these runs entirely in your browser — nothing you paste or generate is ever sent anywhere.
Explore more: internal linking map
A map of everything on DevTools Hub related to Linux and server security, organized by what it actually is:
Tools: Linux Permissions Calculator · chmod Command Generator · SSH Key Generator · SSH Key Inspector · SSH Config Generator · Linux Hardening Checklist Generator
In-depth articles: chmod vs chown · SSH Keys Explained · SSH Config File Guide · Linux Server Hardening Checklist
Toolkit hub: Linux Security Toolkit — all Linux-category tools in one place, with a shared workflow guide.
Related security areas: for network-facing security rather than the server itself, see the Network Security Toolkit (CIDR Calculator, SPF Record Parser, Security Headers Analyzer) and the Encryption Toolkit for the cryptographic building blocks (AES, RSA, certificate signing requests) SSH and TLS both rely on.
Common mistakes
- chmod 777 as a quick fix. Almost always papering over an ownership problem
chownshould have solved instead — see chmod vs chown. - Disabling SSH password authentication before confirming key-based login works. Always test the key in a second, still-open session first.
- Enabling the firewall before allowing SSH through it. The single most common way people lock themselves out of a server they administer remotely.
- Shared logins for a team, or a service account with an interactive shell it never needs. Both erase exactly the accountability logging exists to provide.
- Treating hardening as a one-time task done at provisioning. Accounts, packages, and firewall rules accumulate continuously; schedule a periodic review rather than hardening once and never revisiting it.
- Skipping updates because hardening feels more interesting. An unpatched, actively exploited vulnerability bypasses nearly everything else on this page.
FAQ
Where should I actually start if I'm securing a Linux server for the first time?
Updates first, then SSH, then the firewall — in that order. Apply pending patches, switch SSH to key-based authentication only, then set the firewall to default-deny with only the ports you actually need open. Those three prevent the large majority of real-world compromises; permissions review, logging, and the rest matter, but they don't stop an attacker who walks in through an unpatched service or a guessable password.
What's the single most important Linux security practice?
Least privilege, applied consistently — every account, process, and permission granted only what it actually needs, nothing more. Almost every other practice on this page (SSH hardening, user management, firewalls, permissions) is really least privilege applied to one specific area.
Do these practices differ much between Ubuntu, Debian, Fedora, and RHEL?
The underlying goals are identical; the tools and commands differ. Ubuntu and Debian use apt and UFW; Fedora and RHEL use dnf and firewalld. AppArmor is Ubuntu's default mandatory access control system (Debian supports it too, less uniformly enabled); SELinux is enforcing by default on Fedora and RHEL. The Linux Hardening Checklist Generator on this page produces the exact commands for whichever of the four you're running.
How often should Linux server security actually be revisited?
At minimum whenever the server's role changes — a new service gets installed, a new admin gets added — and on a recurring schedule regardless. Quarterly is a reasonable default for a small team. Firewall rules and user accounts in particular accumulate cruft from changes nobody remembered to clean up.
Is Linux more secure than other operating systems by default?
Linux gives an administrator more granular control over permissions, services, and access than many alternatives, but a default Linux install is not a hardened one — it's tuned for compatibility and ease of setup, the same as any other operating system's default install. Everything on this page is work a Linux server specifically still needs, not something the platform does for you automatically.
Do I need SELinux or AppArmor if the firewall and SSH are already hardened?
Yes — they defend against a different failure mode. A firewall controls what can reach the machine over the network; SELinux and AppArmor confine what an already-running process is allowed to do on the machine itself, which is exactly the layer that matters when a network-facing service gets compromised despite the firewall and SSH both being configured correctly.
Can Linux server security be automated instead of done manually?
Partially — configuration management tools (Ansible, Puppet, Chef) can apply and enforce a hardening baseline consistently across many servers, and automatic security updates handle patching without manual intervention. Reviewing what those tools actually configured, and judgment calls like which accounts should exist or which ports should be open, still need a human decision behind them at least once.
Try it yourself
Start with Linux Hardening Checklist Generator for a complete, distro-specific checklist covering every section above, or jump directly to whichever tool matches the task in front of you from the Linux Security Toolkit.