{"id":245,"date":"2025-12-11T00:17:50","date_gmt":"2025-12-11T00:17:50","guid":{"rendered":"https:\/\/www.vps.tc\/blog\/10-essential-steps-to-secure-and-harden-your-linux-server\/"},"modified":"2026-09-09T09:22:29","modified_gmt":"2026-09-09T09:22:29","slug":"10-essential-steps-to-secure-and-harden-your-linux-server","status":"publish","type":"post","link":"https:\/\/www.vps.tc\/blog\/en\/10-essential-steps-to-secure-and-harden-your-linux-server\/","title":{"rendered":"10 Essential Steps to Secure and Harden Your Linux Server"},"content":{"rendered":"<div class=\"aiw-toc\" style=\"border:1px solid #dbe3ea;border-radius:8px;padding:16px 20px;margin:0 0 28px\"><strong>Table of Contents<\/strong><\/p>\n<ol style=\"margin:10px 0 0;padding-left:22px\">\n<li><a href=\"#why-linux-servers-stay-on-attackers-radar\">Why Linux servers stay on attackers&#039; radar<\/a><\/li>\n<li><a href=\"#step-1-start-with-a-minimal-fully-patched-system\">Step 1: Start with a minimal, fully patched system<\/a><\/li>\n<li><a href=\"#step-2-lock-down-ssh-your-primary-entry-point\">Step 2: Lock down SSH, your primary entry point<\/a><\/li>\n<li><a href=\"#step-3-enforce-strong-authentication-and-authorization\">Step 3: Enforce strong authentication and authorization<\/a><\/li>\n<li><a href=\"#step-4-put-a-real-firewall-in-front-of-every-service\">Step 4: Put a real firewall in front of every service<\/a><\/li>\n<li><a href=\"#step-5-reduce-attack-surface-services-ports-and-software\">Step 5: Reduce attack surface: services, ports and software<\/a><\/li>\n<li><a href=\"#step-6-harden-the-os-and-kernel\">Step 6: Harden the OS and kernel<\/a><\/li>\n<li><a href=\"#step-7-protect-data-with-sane-permissions-and-encryption\">Step 7: Protect data with sane permissions and encryption<\/a><\/li>\n<li><a href=\"#step-8-logging-auditing-and-knowing-what-changed\">Step 8: Logging, auditing and knowing what changed<\/a><\/li>\n<li><a href=\"#step-9-backups-and-disaster-recovery-that-actually-work\">Step 9: Backups and disaster recovery that actually work<\/a><\/li>\n<li><a href=\"#step-10-continuous-monitoring-and-basic-incident-response\">Step 10: Continuous monitoring and basic incident response<\/a><\/li>\n<li><a href=\"#pulling-your-linux-security-plan-together\">Pulling your Linux security plan together<\/a><\/li>\n<li><a href=\"#frequently-asked-questions\">Frequently Asked Questions<\/a><\/li>\n<\/ol>\n<\/div>\n<h2 id=\"why-linux-servers-stay-on-attackers-radar\">Why Linux servers stay on attackers&#039; radar<\/h2>\n<p>Public Linux servers are scanned almost immediately after they become reachable. SSH password attempts, web application probes and leaked keys tested against your address range are routine background noise. If you run production workloads, server security cannot be an afterthought.<\/p>\n<p>There is no magic hardening script. A safer host comes from a baseline that you can apply, verify and maintain across every machine. I have learned this the unglamorous way: a system can look quiet while an important control is missing.<\/p>\n<p>These are ten practical areas I check on internet-facing Linux servers. They will not make a host invulnerable, but they remove many easy paths and make the remaining ones easier to detect.<\/p>\n<h2 id=\"step-1-start-with-a-minimal-fully-patched-system\">Step 1: Start with a minimal, fully patched system<\/h2>\n<p>If the operating system is outdated, later configuration work has a weak foundation. On a fresh VPS or dedicated server, patch it first, then remove services and packages you do not need.<\/p>\n<h3>Use a minimal base image<\/h3>\n<p>Choose a maintained, minimal distribution image rather than one carrying a collection of services you may never use. With providers like <a href=\"https:\/\/www.vps.tc\/en\/vps\">VPS.TC VPS<\/a> or <a href=\"https:\/\/www.vps.tc\/en\/turkey-dedicated-server\">dedicated servers<\/a>, start with the leanest suitable Linux template and add software deliberately.<\/p>\n<h3>Apply security updates immediately<\/h3>\n<p>After confirming that you can log in through the provider console, update the system before installing your application stack. On Debian and Ubuntu:<\/p>\n<pre><code>sudo apt update &amp;&amp; sudo apt full-upgrade -y<\/code><\/pre>\n<p>On RHEL, Rocky or AlmaLinux:<\/p>\n<pre><code>sudo dnf upgrade --refresh -y<\/code><\/pre>\n<p>Reboot when a kernel, systemd, glibc or another component requiring a restart was upgraded. Check before and after instead of treating every package update as proof that a reboot is necessary:<\/p>\n<pre><code>sudo needs-restarting -r<\/code><\/pre>\n<p>That command is available on RHEL-family systems with <code>dnf-utils<\/code> or the current <code>dnf-plugins-core<\/code> package. On Debian and Ubuntu, review <code>\/var\/run\/reboot-required<\/code> when it exists.<\/p>\n<h3>Automate future security updates<\/h3>\n<p>For Debian and Ubuntu, install and configure <code>unattended-upgrades<\/code> if automatic security updates fit your release and change-control policy:<\/p>\n<pre><code>sudo apt install unattended-upgrades\nsudo dpkg-reconfigure unattended-upgrades<\/code><\/pre>\n<p>On RHEL-like systems, <code>dnf-automatic<\/code> can apply or report updates according to its configuration. Automatic patching still needs monitoring, exclusions for special workloads and a recovery plan. Test kernel updates on a disposable or staging host first.<\/p>\n<h2 id=\"step-2-lock-down-ssh-your-primary-entry-point\">Step 2: Lock down SSH, your primary entry point<\/h2>\n<p>SSH is still the service I see probed most often. Changing its port may reduce log noise, but it is not a security control by itself. Authentication, authorization and network restrictions matter more.<\/p>\n<h3>Disable direct root login<\/h3>\n<p>Create an individual administrator account and give it only the sudo access it needs.<\/p>\n<pre><code>sudo adduser deploy\nsudo usermod -aG sudo deploy<\/code><\/pre>\n<p>On RHEL-based systems, use the <code>wheel<\/code> group instead:<\/p>\n<pre><code>sudo usermod -aG wheel deploy<\/code><\/pre>\n<p>Before changing the daemon configuration, validate it with <code>sshd -t<\/code>. In <code>\/etc\/ssh\/sshd_config<\/code> or a file under <code>\/etc\/ssh\/sshd_config.d\/<\/code>, use settings similar to:<\/p>\n<pre><code>PermitRootLogin no\nPasswordAuthentication no\nKbdInteractiveAuthentication no\nPermitEmptyPasswords no\nPubkeyAuthentication yes<\/code><\/pre>\n<p>Reload the service name used by your distribution. Debian and Ubuntu commonly use <code>ssh<\/code>; RHEL-family systems commonly use <code>sshd<\/code>:<\/p>\n<pre><code>sudo sshd -t &amp;&amp; sudo systemctl reload ssh<\/code><\/pre>\n<p>On RHEL-family systems, replace <code>ssh<\/code> with <code>sshd<\/code>. Keep an existing session open and test a second key-based session before disabling passwords. I once came close to locking myself out because I changed access rules before testing the new account; the provider console saved me, but it was not a clever recovery plan.<\/p>\n<h3>Use key-based authentication, not passwords<\/h3>\n<p>Generate a key on your workstation, protect it with a passphrase and keep the private half off the server.<\/p>\n<pre><code>ssh-keygen -t ed25519<\/code><\/pre>\n<p>Use <code>ssh-copy-id<\/code> where available, or append the public key to <code>~\/.ssh\/authorized_keys<\/code>. For automation, use a separate restricted key rather than your personal administrator key.<\/p>\n<h3>Limit who can log in via SSH<\/h3>\n<p>Define the permitted accounts explicitly when that matches your access model:<\/p>\n<pre><code>AllowUsers deploy<\/code><\/pre>\n<p>For a bastion host, restrict access further with source addresses, a VPN or an upstream firewall. If you use an MFA-capable SSH setup, test the emergency access path before you need it.<\/p>\n<h2 id=\"step-3-enforce-strong-authentication-and-authorization\">Step 3: Enforce strong authentication and authorization<\/h2>\n<p>SSH hardening answers who may enter. Sudo policy answers what they can do after entering.<\/p>\n<h3>Use sudo with least privilege<\/h3>\n<p>Give each administrator a personal account and record privilege changes in version control or configuration management. Use <code>\/etc\/sudoers.d\/<\/code> and validate edits with <code>visudo<\/code>; do not share a root password.<\/p>\n<p>When someone leaves the team, remove the account or disable it, revoke its keys and review any service credentials it owned. Access that exists only in somebody&#039;s memory is not an access-control system.<\/p>\n<h3>Consider multi-factor authentication for critical access<\/h3>\n<p>MFA can protect SSH, VPN and management panels when it is deployed carefully. TOTP through a PAM module is one option; FIDO2 or hardware-backed keys can provide stronger phishing resistance where your SSH clients and policy support them. Keep a documented, tightly controlled break-glass method, and test it without weakening normal access.<\/p>\n<h2 id=\"step-4-put-a-real-firewall-in-front-of-every-service\">Step 4: Put a real firewall in front of every service<\/h2>\n<p>A provider firewall and a host firewall solve different problems. I use both when possible, with the host rules kept simple enough to audit.<\/p>\n<h3>Allow only what you actually use<\/h3>\n<p>List the required ports before writing rules. Usually that means SSH and, for a web server, ports 80 and 443. Database ports should normally be reachable only from the application network or trusted administration addresses.<\/p>\n<p>On Ubuntu, <code>ufw<\/code> is convenient for a small host:<\/p>\n<pre><code>sudo ufw default deny incoming\nsudo ufw default allow outgoing\nsudo ufw allow 22\/tcp\nsudo ufw allow 80\/tcp\nsudo ufw allow 443\/tcp\nsudo ufw enable<\/code><\/pre>\n<p>On RHEL-based systems with firewalld:<\/p>\n<pre><code>sudo firewall-cmd --permanent --set-default-zone=public\nsudo firewall-cmd --permanent --add-service=ssh\nsudo firewall-cmd --permanent --add-service=http\nsudo firewall-cmd --permanent --add-service=https\nsudo firewall-cmd --reload<\/code><\/pre>\n<p>For more explicit rule sets, nftables is the current Linux packet-filtering framework behind many distributions&#039; tooling. Whichever layer you choose, confirm the SSH rule and keep a console or active session available before enabling it. The <code>-a<\/code> flag is not the thing to remember here; the thing to remember is how you will recover from a bad rule.<\/p>\n<h2 id=\"step-5-reduce-attack-surface-services-ports-and-software\">Step 5: Reduce attack surface: services, ports and software<\/h2>\n<p>Every listening daemon expands the amount of code and configuration you must maintain. If a service is unnecessary, remove it or prevent it from starting.<\/p>\n<h3>Inventory running services<\/h3>\n<p>Start with sockets:<\/p>\n<pre><code>sudo ss -tulpn<\/code><\/pre>\n<p>Then inspect enabled services:<\/p>\n<pre><code>systemctl list-unit-files --type=service --state=enabled<\/code><\/pre>\n<p>Check each unfamiliar listener with its package and service documentation. Do not disable something merely because its name is unfamiliar; identify it first.<\/p>\n<h3>Disable and remove what you do not need<\/h3>\n<pre><code>sudo systemctl disable --now avahi-daemon\nsudo systemctl disable --now cups<\/code><\/pre>\n<p>Those examples are appropriate only when you do not need discovery or printing. Remove unused packages through the distribution package manager and review dependencies afterward. A service that is disabled but never patched can still become a problem if somebody starts it later.<\/p>\n<h2 id=\"step-6-harden-the-os-and-kernel\">Step 6: Harden the OS and kernel<\/h2>\n<p>Kernel settings can reduce exposure, but they are not a substitute for patching, firewall rules or application security. Test them against the way the host actually routes traffic.<\/p>\n<h3>Apply secure sysctl settings<\/h3>\n<p>For a host that is not a router, a starting point may include:<\/p>\n<pre><code>net.ipv4.ip_forward = 0\nnet.ipv4.conf.all.accept_source_route = 0\nnet.ipv4.conf.default.accept_source_route = 0\nnet.ipv4.conf.all.accept_redirects = 0\nnet.ipv4.conf.default.accept_redirects = 0<\/code><\/pre>\n<p>Save this in a reviewed file such as <code>\/etc\/sysctl.d\/99-custom.conf<\/code> and apply it with:<\/p>\n<pre><code>sudo sysctl --system<\/code><\/pre>\n<p>Be careful with reverse-path filtering. Strict <code>rp_filter=1<\/code> can break asymmetric routing, policy routing, VPNs or some cloud network designs. Use the distribution default or a tested loose mode where the network requires it; do not paste a large hardening template into production. I have seen a supposedly harmless sysctl change turn a routing problem into a longer investigation because nobody had written down the expected traffic paths.<\/p>\n<h3>Use mandatory access control where feasible<\/h3>\n<p>SELinux and AppArmor restrict what processes can do even when a process is compromised or running with high privileges. Keep SELinux enforcing or AppArmor enabled where the distribution and application support it. Fix policy denials deliberately; disabling the control to make an application start is usually only hiding the real configuration problem.<\/p>\n<p>For multiple servers, put the chosen MAC policy into images and automation. Verify the policy after application upgrades, since a package update can change required paths or permissions.<\/p>\n<h2 id=\"step-7-protect-data-with-sane-permissions-and-encryption\">Step 7: Protect data with sane permissions and encryption<\/h2>\n<p>A network compromise is not the only route to data loss. A stolen credential, an over-permissive service account or an exposed backup can be just as damaging.<\/p>\n<h3>Fix ownership and permissions<\/h3>\n<p>World-writable files should be unusual and explainable. Check them periodically:<\/p>\n<pre><code>sudo find \/ -xdev -type d -perm -0002 -print\nsudo find \/ -xdev -type f -perm -0002 -print<\/code><\/pre>\n<p>Review the results rather than blindly running <code>chmod 777<\/code>. That changes the symptom, not the ownership or application design that caused it. Protect <code>\/etc\/shadow<\/code>, SSH keys, TLS private keys and application secrets with correct owners and the narrowest practical modes.<\/p>\n<h3>Encrypt data at rest and in transit<\/h3>\n<p>LUKS full-disk encryption can protect data on stolen physical media, but it also changes how a remote server boots and how keys are made available. Plan console access and key handling before deploying it. For databases and application data, field-level or volume encryption may be more appropriate.<\/p>\n<p>For public services, use current TLS configurations supplied by the web server and certificate tooling, and remove obsolete protocols only after checking client compatibility. Encryption does not help if private keys are stored in a world-readable backup.<\/p>\n<h2 id=\"step-8-logging-auditing-and-knowing-what-changed\">Step 8: Logging, auditing and knowing what changed<\/h2>\n<p>When a server behaves strangely, I want logs before I want a reboot. Restarting first can erase volatile evidence and rarely explains the original fault.<\/p>\n<h3>Centralize and retain logs<\/h3>\n<p>Forward important journald or syslog records to a separate collector with restricted access and a retention policy. Include authentication events, sudo activity, firewall events, service failures and application logs. A remote copy is useful because an attacker with root can alter local evidence.<\/p>\n<h3>Audit critical actions<\/h3>\n<p>For high-value systems, use <code>auditd<\/code> or an equivalent auditing pipeline for changes to identity files, SSH configuration, sudo policy and other sensitive paths. Tune rules to the risks you can investigate; collecting everything without enough storage or alerting creates noise rather than visibility.<\/p>\n<p>Also record configuration changes through Ansible, packages or a Git-backed process. Knowing who changed a file and when is often as useful as knowing that it changed.<\/p>\n<h2 id=\"step-9-backups-and-disaster-recovery-that-actually-work\">Step 9: Backups and disaster recovery that actually work<\/h2>\n<p>Security includes recovery. Ransomware, accidental deletion, failed updates and provider incidents all test the same question: can you restore the service and the data?<\/p>\n<h3>Follow a practical backup strategy<\/h3>\n<p>The 3-2-1 rule remains a useful baseline:<\/p>\n<ul>\n<li>Keep at least three copies of important data<\/li>\n<li>Use two different storage systems or media<\/li>\n<li>Keep at least one copy offsite, and preferably offline or otherwise immutable<\/li>\n<\/ul>\n<p>Combine filesystem backups with application-consistent database dumps. On <a href=\"https:\/\/www.vps.tc\/en\/cloud-server\">cloud servers<\/a> or <a href=\"https:\/\/www.vps.tc\/en\/virtual-datacenter\">virtual datacenter<\/a> infrastructure, snapshots are useful for short-term recovery but are not a complete backup strategy. Protect backup credentials separately from production credentials.<\/p>\n<h3>Test restores, not just backup jobs<\/h3>\n<p>A successful backup job proves that a process ran. It does not prove that the files are usable, the database is consistent or the service can start. Restore into an isolated environment, check application behavior and record the recovery time.<\/p>\n<p>I use <code>rsync<\/code> for straightforward copies and Borg for deduplicated, encrypted repositories, but the tool is less important than the restore test. A backup I have never restored is an assumption. Schedule the test and document where the keys, repositories and recovery instructions live.<\/p>\n<h2 id=\"step-10-continuous-monitoring-and-basic-incident-response\">Step 10: Continuous monitoring and basic incident response<\/h2>\n<p>Hardening is maintenance, not a one-time ceremony. Packages change, users come and go and a previously internal service may become public after one deployment.<\/p>\n<h3>Monitor resources, services and security signals<\/h3>\n<p>Watch at least:<\/p>\n<ul>\n<li>CPU, memory, swap and disk trends<\/li>\n<li>Network traffic and unusual outbound volume<\/li>\n<li>Service availability and response time<\/li>\n<li>Authentication failures, sudo use and new accounts<\/li>\n<li>Certificate expiry and backup freshness<\/li>\n<\/ul>\n<p>Use alerts that result in an action, not only dashboards. In my homelab, Prometheus and Grafana show trends while Uptime Kuma catches availability failures; I still inspect the underlying logs when an alert fires. A pretty graph is not an incident response plan.<\/p>\n<h3>Plan how you respond to incidents<\/h3>\n<p>Write down:<\/p>\n<ul>\n<li>Who is notified and who can make isolation decisions<\/li>\n<li>How to restrict network access without destroying evidence<\/li>\n<li>Which logs, process data and disk images should be preserved<\/li>\n<li>How to rebuild from a known-good image<\/li>\n<li>How credentials, tokens and keys will be revoked and replaced<\/li>\n<\/ul>\n<p>Do not reboot automatically just because a process looks suspicious. Capture what you can, isolate the host, then rebuild when you have enough information and a clean recovery path.<\/p>\n<h2 id=\"pulling-your-linux-security-plan-together\">Pulling your Linux security plan together<\/h2>\n<p>These ten areas will put an internet-facing host in a much better position than an unpatched, over-permissive default installation. They do not replace application security, provider controls or incident response, and they do not guarantee that an attacker will fail.<\/p>\n<p>From SSH and firewalls to backups and monitoring, the hard part is keeping the baseline true after the first deployment. Standardize the controls with images or Ansible once you have repeated the work twice (although I still do not open a YAML file for a one-line emergency fix). Then every new server from providers like <a href=\"https:\/\/www.vps.tc\/en\/vds-server\">VPS.TC VDS<\/a> can begin from a reviewed baseline instead of a remembered checklist.<\/p>\n<p>If you manage one server, check its exposed ports, privileged accounts and most recent restore test today. If you manage dozens, measure configuration drift and make the repair repeatable. That is usually where security stops being a document and becomes part of operating the system.<\/p>\n<p>When I harden a Linux server, I also check whether memory pressure is being masked by swap; my guide, <a href=\"https:\/\/www.vps.tc\/blog\/en\/how-to-create-swap-space-on-a-vps-for-linux-memory-management\/\">How to Create Swap Space on a VPS for Linux Memory Management<\/a>, explains how to configure it safely and investigate the underlying cause.<\/p>\n<p>When I add a database service, I treat it as another security boundary; my guide on <a href=\"https:\/\/www.vps.tc\/blog\/en\/how-to-install-and-secure-mysql-on-a-vps\/\">How to Install and Secure MySQL on a VPS<\/a> walks through binding, authentication, TLS, and restore-tested backups.<\/p>\n<p>When I&#039;m hardening a Linux VPS that will host a game, I also apply these practical steps from <a href=\"https:\/\/www.vps.tc\/blog\/en\/how-to-set-up-minecraft-server-on-vps\/\">How to Set Up a Minecraft Server on a VPS<\/a>, including running the service as a dedicated user and opening only the required ports.<\/p>\n<p>I also recommend reading our <a href=\"https:\/\/www.vps.tc\/blog\/en\/what-is-a-ddos-attack-vps-protection-guide\/\">practical DDoS protection guide for your VPS<\/a>, which explains how to identify an attack, collect evidence, and choose the right defense before restarting or resizing your server.<\/p>\n<h2 id=\"frequently-asked-questions\">Frequently Asked Questions<\/h2>\n<h3>What is the first thing to do on a new Linux server for security?<\/h3>\n<p>Confirm console access, update the operating system and apply pending security patches. Reboot when the update requires it, then configure SSH, the firewall and the application services.<\/p>\n<h3>How can I harden SSH access on my Linux server?<\/h3>\n<p>Use passphrase-protected keys, disable direct root and password login after testing key access, restrict allowed users and limit SSH at the firewall or VPN boundary. Changing the port can reduce noise, but it is not a substitute for these controls. Fail2ban can help with repeated authentication abuse, while key and network policy remain the main defenses.<\/p>\n<h3>Do I really need a host-based firewall if I already have a perimeter firewall?<\/h3>\n<p>Usually, yes. A host firewall provides a second policy boundary and can limit accidental exposure, internal traffic and lateral movement when an upstream rule is wrong or another system is compromised.<\/p>\n<h3>How often should I review my Linux server security configuration?<\/h3>\n<p>Review it at least quarterly and after major changes, staff changes or high-impact vulnerabilities. Check users, SSH keys, listening sockets, firewall rules, MAC status, logs, patch status and restore tests. A smaller review after every deployment catches drift earlier than a large annual audit.<\/h3>\n","protected":false},"excerpt":{"rendered":"<p>Linux servers are scanned within minutes of going online. These 10 essential hardening steps give you a practical, production-ready baseline for SSH, firewall, backups and monitoring.<\/p>\n","protected":false},"author":1,"featured_media":244,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-245","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-linux"],"lang":"en","translations":{"en":245,"tr":230},"pll_sync_post":[],"_links":{"self":[{"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/posts\/245","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/comments?post=245"}],"version-history":[{"count":6,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/posts\/245\/revisions"}],"predecessor-version":[{"id":574,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/posts\/245\/revisions\/574"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/media\/244"}],"wp:attachment":[{"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/media?parent=245"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/categories?post=245"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/tags?post=245"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}