Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Ubuntu Server Security Best Practices

By Kokil Thapa | Last reviewed: September 2026

A default Ubuntu VPS is online and reachable within minutes of provisioning. Bots scan it before you finish installing PHP. That is why Ubuntu server security best practices belong on day one, not after a breach. This guide covers the exact hardening stack I apply on production servers running Laravel, WordPress, and custom PHP — whether the box sits in Kathmandu or on AWS. If you need context for local hosting constraints, start with how to secure your website and server in Nepal and treat this page as the technical baseline underneath it.

What Are the Essential Ubuntu Server Security Best Practices for Production?

There is no single setting that makes a server safe. Defense in depth stacks independent controls so one failure does not expose everything. On legal-tech portals and eCommerce stores I maintain, the same five layers appear on every box.

Network filtering comes first. It drops junk traffic before Nginx or Apache sees it. Access control comes second. Compromised credentials bypass most app-level checks. Automated patching closes known CVEs while you ship features. Monitoring and backups let you detect and recover when something slips through.

Ubuntu Server Defense LayersLayer 1: UFW FirewallLayer 2: SSH HardeningLayer 3: Fail2BanLayer 4: Auto Security PatchesLayer 5: File Permissions
Defense-in-depth stack for Ubuntu server security best practices on production web servers

Priority order matters. Patch a leaky SSH config all you want — an open MySQL port still exposes your database. For a deeper checklist aligned with industry standards, see the CIS benchmarks for server hardening and the companion Ubuntu security hardening guide on this site.

Most teams I work with treat this stack as standard operating procedure. If you want someone to apply and maintain it, Linux system administration in Nepal covers exactly that scope.

How Do You Harden SSH Access on Ubuntu 24.04?

SSH is the front door every bot on the internet tries first. Ubuntu 24.04 LTS ships with sensible defaults, but sensible is not hardened. Securing Ubuntu server SSH access is the highest-ROI step you can take in the first hour after provisioning.

Disable root login and password authentication

Root login gives attackers a known username with full privileges. Password auth enables brute-force attempts at scale. Edit /etc/ssh/sshd_config:

# /etc/ssh/sshd_config — production hardened
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
AllowUsers deploy admin
Protocol 2

Validate before restart. A syntax error locks you out:

sudo sshd -t
sudo systemctl restart sshd

Configure key-based authentication correctly

Generate ED25519 keys on your laptop, not the server. ED25519 beats RSA on modern OpenSSH for both speed and key size:

ssh-keygen -t ed25519 -C "deploy@production-2026"
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@your-server-ip

Set private key permissions to 600. Never paste keys into chat or email. For teams running multiple servers, centralise access through SSH certificates rather than scattering public keys. The dedicated walkthrough at SSH key auth, fail2ban, and port hardening covers edge cases I hit on real deployments.

Change the default SSH port (optional)

Moving SSH from port 22 to something like 2222 cuts scanner noise. It does not stop a targeted attacker. Update UFW if you do this. Document the port in your runbook. Never treat port obscurity as a substitute for keys and firewall rules.

How Should You Configure UFW Firewall Rules for Web Servers?

UFW wraps iptables with a readable interface. The rule is simple: deny everything inbound, then allow only what the app needs. A typical Laravel or WordPress server on Nginx needs three ports. Nothing else faces the internet.

UFW Rule FlowDefault DenyAll IncomingAllow 22SSH AccessAllow 80HTTP TrafficAllow 443HTTPS TrafficLimit 22Rate Limit SSHAll Other Ports Blocked
UFW firewall rules that secure Ubuntu server traffic for production web apps
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
sudo ufw limit 22/tcp comment 'SSH rate limit'
sudo ufw enable
sudo ufw status verbose

Official reference: the Ubuntu UFW community documentation. For port-specific guidance on Laravel stacks, read UFW firewall rules for web servers.

Never expose MySQL (3306) or Redis (6379) to the public internet. Bind databases to 127.0.0.1. Use an SSH tunnel for remote admin. Opening database ports is one of the fastest ways to lose customer data.

PortServiceActionWhy
22/tcpSSHAllow + limitAdmin access; rate limit slows brute force
80/tcpHTTPAllowLet's Encrypt validation and HTTP redirects
443/tcpHTTPSAllowEncrypted traffic; mandatory in production
3306/tcpMySQLBlock externalLocalhost only; tunnel for remote access
6379/tcpRedisBlock externalLocalhost unless running a dedicated cluster
8080/tcpDev serverBlock in prodProxy through Nginx; never expose directly

How Do You Set Up Fail2Ban to Block Brute-Force Attacks?

Key-only SSH stops most credential attacks. Fail2ban adds a safety net. It watches log files and bans IPs that show repeated failure patterns. I run it on every production box, including sister sites on shared EC2 infrastructure.

sudo apt update
sudo apt install fail2ban -y
sudo nano /etc/fail2ban/jail.local

Add this to /etc/fail2ban/jail.local. Never edit jail.conf directly — package updates overwrite it:

[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
banaction = ufw

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 86400

[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5

For Laravel apps, add a custom filter matching failed logins in storage/logs/laravel.log. Network-level bans reduce load on PHP-FPM during credential-stuffing waves. Enable and verify:

sudo systemctl enable fail2ban
sudo systemctl start fail2ban
sudo fail2ban-client status sshd

At the edge, compare fail2ban vs Cloudflare for DDoS protection when traffic scales beyond a single VPS.

Why Is Automated Security Patching Critical for Ubuntu Servers?

Manual patching fails in practice. Client deadlines, feature work, and on-call fatigue push security updates to next week. Next week becomes next month. Unattended Upgrades installs security patches automatically, usually within hours of release.

Auto-Patch WorkflowDaily Cron6 AM TriggerCheck ReposSecurity OnlyInstallAuto ApplyRebootIf NeededEmail ReportSuccess or FailureNo Manual Steps Required
Automated patching pipeline supporting Ubuntu server security best practices
sudo apt install unattended-upgrades apt-listchanges -y
sudo dpkg-reconfigure --priority=low unattended-upgrades

Configure /etc/apt/apt.conf.d/50unattended-upgrades:

Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
};

Unattended-Upgrade::Package-Blacklist {
    "php8.3-fpm";
    "nginx";
};

Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
Unattended-Upgrade::Mail "admin@yourdomain.com";

Blacklist services that restart during business hours. Schedule reboots for off-peak windows — 4 AM Nepal Time works well for domestic eCommerce traffic. Test in staging first. More detail lives in the Ubuntu security updates guide.

What File Permissions Protect Laravel and WordPress Applications?

Wrong permissions are among the most common issues I find during server audits. Overly open directories let an attacker who gains limited access rewrite PHP files or read secrets. Tight ownership and mode bits contain the blast radius.

  • Ownership: App files owned by a deploy user; web server group (www-data), never root
  • Directories: 755 — owner writes, others read and traverse
  • Files: 644 — owner writes, others read only
  • Laravel writable dirs: storage/ and bootstrap/cache/ at 775
  • Environment file: .env at 600 — never world-readable
  • Upload dirs: No execute bit; validate MIME types in application code
sudo chown -R deploy:www-data /var/www/html/myapp
sudo find /var/www/html/myapp -type d -exec chmod 755 {} \;
sudo find /var/www/html/myapp -type f -exec chmod 644 {} \;
sudo chmod -R 775 /var/www/html/myapp/storage
sudo chmod -R 775 /var/www/html/myapp/bootstrap/cache
sudo chmod 600 /var/www/html/myapp/.env

Enforce permissions through deployment scripts, not manual fixes after each release. Deployer 7 handles writable_dirs automatically. Pair this with the CI/CD pipeline setup guide for production servers and Laravel on Ubuntu VPS with Nginx for a full deploy workflow.

When coordinating with Laravel developers in Nepal, bake permission checks into the pipeline. Manual drift between releases is how production .env files end up readable by the web server group.

How Do You Secure an Ubuntu Server from Hackers After Initial Hardening?

The first-hour checklist stops automated scanners. Staying secure means closing gaps attackers probe after the basics. These steps turn a hardened box into a maintainable production system.

Enable TLS everywhere

HTTP sends credentials and session cookies in plain text. Issue free certificates with Certbot and force HTTPS redirects. Follow the Let's Encrypt and Certbot setup guide for the exact Nginx config blocks.

Automate off-site backups

Ransomware and operator error both destroy local snapshots. Push encrypted backups to S3 or another region nightly. The Ubuntu server backup strategies and rsync and cron backup guide cover tested patterns I use on client infrastructure.

Remove unused packages and services

Every installed daemon is a potential CVE. Audit with sudo ss -tulpn and disable anything not required. A web server does not need a mail transfer agent unless it sends mail directly.

Generate strong credentials with a proper tool

Database passwords, API keys, and admin tokens belong in a secrets manager or at minimum a 600-permissioned .env file. Use the password generator for initial entropy, then store values outside git.

Post-Hardening ChecklistTLS on All EndpointsOff-Site BackupsRemove Unused ServicesMonitor Auth LogsQuarterly AuditApp Security LayerLayers beyond SSH and UFW to secure Ubuntu from targeted attacks
Extended steps to secure Ubuntu server from hackers after baseline hardening

Application security still matters. Server hardening does not fix SQL injection or XSS. Read server hardening for Ubuntu web servers and Ubuntu server setup for PHP apps for the full stack picture. Projects like Nepal Gift Card run on exactly this baseline — Laravel app layer on top, hardened OS underneath.

For ongoing monitoring and patch management after launch, support and maintenance services and domain registration and hosting cover the operational side so you can focus on product work.

Key Takeaways

  • Disable root SSH and password auth; use ED25519 keys before opening the server to traffic.
  • Run UFW with default deny inbound; allow only ports 22, 80, and 443 on web servers.
  • Install fail2ban with SSH and web jails to block brute-force IPs automatically.
  • Enable unattended security upgrades; blacklist services that restart during peak hours.
  • Set Laravel and WordPress file permissions through deploy scripts, not manual chmod after each release.
  • Add TLS, off-site backups, and quarterly audits to secure Ubuntu from hackers long term.

People Also Ask

How do I make Ubuntu more secure on a fresh VPS?

Create a sudo user, deploy SSH keys, disable password and root login, enable UFW, install fail2ban, and turn on unattended security upgrades. Do all six before installing your web stack. The full sequence takes under an hour on Ubuntu 24.04 LTS.

What is the difference between securing Ubuntu and securing the application?

Server hardening controls network access, OS patches, and file permissions. Application security covers input validation, authentication, and dependency updates. You need both. A hardened server with a vulnerable Laravel plugin still gets compromised.

Should I change the SSH port to secure Ubuntu server access?

Changing from port 22 reduces scanner noise but does not stop targeted attacks. Key-only auth plus UFW rate limiting on port 22 is the real fix. If you change the port, update firewall rules and document it in your runbook.

How often should I patch an Ubuntu production server?

Security patches should apply within 24 hours of release. Unattended Upgrades handles this automatically. Test kernel reboots in staging first. Schedule automatic reboots for off-peak hours to avoid downtime during business traffic.

Build Ubuntu Server Security Into Every Deployment

Ubuntu server security best practices are not a one-time checkbox. They are standard operating procedure for every server you provision in 2026. Start with SSH and UFW today — thirty minutes eliminates entire attack categories. Add fail2ban and auto-patching this week. Wire permissions into your deploy pipeline next release.

These layers compound. A bot that cannot reach SSH, cannot brute-force a login, and cannot read your .env file simply moves on to the next target. That is the outcome you want.

Need a production audit or hands-on hardening for your stack? Contact us to discuss your server security requirements, or reach out directly about your infrastructure. For reference, the OpenSSH manual documents every directive mentioned above.

Frequently Asked Questions

Create a non-root sudo user, disable root SSH login, configure UFW to allow only ports 22, 80, and 443, enable automatic security updates via unattended-upgrades, and set up SSH key authentication before exposing the server to the internet.

Initial hardening typically costs NPR 15,000–30,000 (USD 110–225) for standard web servers. Ongoing maintenance runs NPR 5,000–10,000 monthly depending on complexity and monitoring requirements.

Use both. Fail2ban blocks attacks at the server level before they consume resources, while Cloudflare stops them at the edge. For Nepal-hosted servers with limited bandwidth, fail2ban is essential even behind Cloudflare.

Enable UFW with default deny incoming, allow outgoing. Open port 22 for SSH, 80 and 443 for HTTP/HTTPS. If using Redis or MySQL externally, restrict those ports to specific IPs only. Never expose database ports to 0.0.0.0. Test with ufw status verbose after every change. On production Laravel servers I maintain, I also rate-limit SSH with ufw limit ssh/tcp to slow automated attacks without blocking legitimate admin access during deployments.

Edit /etc/ssh/sshd_config to set PermitRootLogin no, PasswordAuthentication no, PubkeyAuthentication yes, and MaxAuthTries 3. Change the default port from 22 to reduce noise from automated scanners. Use AllowUsers or AllowGroups to restrict SSH access to specific accounts. Restart sshd after changes. I have seen countless compromised servers where root login with password was left enabled. Always deploy SSH keys via Deployer or manually before disabling password auth to avoid locking yourself out during initial setup.

Install unattended-upgrades package and configure /etc/apt/apt.conf.d/50unattended-upgrades to enable automatic installation of security updates. Set Automatic-Reboot-Time to schedule reboots during low-traffic hours like 4 AM NPT. Configure mail notifications for update results. This prevents known vulnerabilities from lingering weeks between manual maintenance windows. On client projects, I pair this with canonical-livepatch for kernel patches without rebooting, which matters for eCommerce sites where downtime directly loses revenue during peak Nepali business hours.

Incorrect ownership lets attackers modify application files or read sensitive configs. Set www-data as owner for storage and cache directories only. Keep application code owned by deploy user with 755 for directories and 644 for files. Never set 777 permissions. Configure PHP-FPM pools to run as dedicated users per site. In my experience maintaining legal-tech portals handling sensitive documents, proper permissions prevent one compromised site from accessing another's data on shared infrastructure. Always audit permissions after deployments using find commands to catch drift.

Bind database to 127.0.0.1 unless remote access is required. Use strong passwords and remove anonymous users and test databases. Create application-specific database users with minimal privileges. Enable SSL for connections if remote access is necessary. Regularly audit user grants. For Laravel applications, store credentials in .env outside web root with 600 permissions. On production systems I manage, I also configure AppArmor profiles to restrict database processes from accessing unrelated filesystem paths, adding defense-in-depth beyond network-level restrictions.

Configure rsyslog to forward logs to centralized storage. Install auditd for file integrity monitoring on critical paths like /etc and application configs. Set up logwatch for daily email summaries. Monitor auth.log for failed SSH attempts and sudo usage. Use netdata or similar for real-time resource alerts. On servers running client applications, I configure alerts for unusual outbound connections that indicate compromise. Logs alone are insufficient without alerting; reviewing terabytes manually is impossible. Pair logging with automated anomaly detection for practical security monitoring.

Disable dangerous functions like exec, shell_exec, and system in php.ini unless absolutely required. Set open_basedir to restrict file access per pool. Configure separate FPM pools with dedicated users for each application. Limit max_children based on available RAM to prevent DoS through resource exhaustion. Enable slowlog to identify problematic scripts. On Laravel applications I deploy, I also set expose_php Off and disable URL wrappers when not needed. These settings prevent many automated exploitation attempts that target misconfigured PHP installations across Nepali hosting environments.

Use Certbot with Let's Encrypt for certificates. Configure Apache or Nginx with TLS 1.2 and 1.3 only, disabling older protocols. Enable HSTS headers with long max-age. Use Mozilla's intermediate cipher suite configuration. Implement OCSP stapling for performance. Test with SSL Labs after configuration. On client projects, I automate certificate renewal via systemd timers rather than cron for reliability. Remember that SSL configuration protects data in transit but does nothing for application-layer vulnerabilities; it is one layer in comprehensive server security, not a complete solution.

Store secrets in .env files outside web root with 600 permissions owned by application user. Never commit secrets to Git. Use deployment tools like Deployer to inject environment-specific configs during release. Consider HashiCorp Vault or SOPS for complex multi-environment setups. Rotate credentials regularly and after personnel changes. On Laravel projects I maintain, I validate .env presence in deployment scripts to prevent accidental exposure. Avoid storing secrets in database or config caches. For team environments, use encrypted secret sharing rather than Slack or email transmission of credentials.

Enable SYN cookies to mitigate SYN flood attacks. Disable IP source routing and ICMP redirects. Enable reverse path filtering. Configure TCP connection timeouts to free resources faster during attacks. Harden IPv6 settings even if unused. Apply changes via /etc/sysctl.d/99-security.conf and reload with sysctl -p. These network stack hardening measures complement application-level security. On production servers, I test these settings in staging first as aggressive tuning can break legitimate traffic patterns. Document all changes since kernel parameter issues are difficult to diagnose months later during incident response.

Use separate PHP-FPM pools with unique users per site. Configure open_basedir restrictions in each pool. Set proper directory permissions preventing cross-site access. Use Apache virtual hosts or Nginx server blocks with isolated document roots. Consider systemd-nspawn or LXC containers for stronger isolation when budget prevents separate VPS instances. On shared infrastructure I manage for sister sites like translation and notary services, this isolation prevents one compromised WordPress site from affecting Laravel applications. True multi-tenancy security requires accepting that shared servers carry inherent risk regardless of configuration quality.

Neglecting application-layer security while perfecting OS hardening. Using outdated PHP versions because upgrading seems risky. Skipping backups or never testing restores. Applying security guides without understanding trade-offs for your workload. Ignoring dependency vulnerabilities in Composer or npm packages. Over-restricting permissions then loosening them ad-hoc during troubleshooting without reverting. In my experience, the biggest gap is treating security as a one-time setup rather than continuous practice. Schedule quarterly audits, test incident response procedures, and accept that perfect security is impossible while striving for resilient, recoverable systems.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: