Security

How to Secure a Linux Server for Business Applications (Ubuntu, Nginx, SSH)

A step-by-step guide to hardening the Ubuntu server behind your website, store or ERP: SSH keys and disabled passwords, UFW firewall, automatic security updates, Nginx with modern TLS and automated certificates, a private database and monitoring.

Illustration of an Ubuntu server rack protected by a firewall, an SSH key and a TLS padlock in front of Nginx

Many Egyptian companies now run their website, online store, ERP or mobile app backend on a Linux server, usually Ubuntu on a cloud or VPS provider, with Nginx in front and a Node.js application and PostgreSQL database behind it. The server is often set up quickly for launch and then left alone. That is risky, because several important defaults are open: OpenSSH accepts password logins unless you turn them off, and Ubuntu's firewall, UFW, is disabled until you enable it.

The short answer: to secure a Linux server for business applications, log in with SSH keys only and disable password and root login, enable a firewall that allows only the ports you need, apply security updates automatically, put the application behind Nginx with modern TLS and automated certificate renewal, keep the database off the internet, run services with least privilege, and monitor logs and backups. This guide gives managers the checklist and gives IT staff the commands, verified against official Ubuntu, OpenSSH, Nginx and PostgreSQL documentation.

Key takeaways

  • OpenSSH's PasswordAuthentication defaults to yes: switch to key-based login and disable passwords explicitly.
  • UFW is disabled by default on Ubuntu. Allow SSH first, then enable it with only the ports you need.
  • Automate security updates, and plan for kernel updates with reboots or Livepatch.
  • Use the TLS "Intermediate" profile and automate certificate renewal, because Let's Encrypt certificate lifetimes are getting shorter.
  • The database and the Node.js app should listen only on localhost or a private network, never on the public internet.
  • Write the hardening steps down and review them; CIS Benchmarks give a detailed reference.

Before you start: inventory and a safe way back in

  • List what runs on the server: which applications, which domains, which ports. sudo ss -tlpn shows listening TCP ports and the processes behind them. Anything you cannot explain should be removed or closed.
  • Keep console access: make sure you can reach the provider's web console or recovery mode in case an SSH change locks you out.
  • Take a snapshot or backup before changing configuration.
  • Agree who has access: one named account per person, never a shared login passed around on WhatsApp.

Step 1: SSH keys, no passwords, no root login

Create a personal admin user and a key

Create a normal user (for example with sudo adduser deploy and sudo usermod -aG sudo deploy). On each administrator's own computer, generate a key and copy it to the server. The Ubuntu Server OpenSSH documentation recommends ssh-keygen -t ed25519 and ssh-copy-id username@target_machine. Protect the private key with a passphrase and never email it.

Disable password and root login

According to the OpenSSH sshd_config manual, PasswordAuthentication "The default is yes", KbdInteractiveAuthentication also defaults to yes, and PermitRootLogin defaults to prohibit-password. So password guessing against your server stays possible until you change it.

Ubuntu's documentation explains that /etc/ssh/sshd_config starts with Include /etc/ssh/sshd_config.d/*.conf, and "because OpenSSH uses the first value set for most directives, any settings defined in files within sshd_config.d/ will override those in the main configuration file". Included files are processed in lexical order, so put your settings in a file that sorts first, such as /etc/ssh/sshd_config.d/00-hardening.conf, and check the other files in that folder for a conflicting PasswordAuthentication yes:

PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
AllowUsers deploy

Then test the configuration, apply it, and confirm the effective values. Keep your current session open and test a new login from a second terminal before you close anything:

sudo sshd -t
sudo systemctl restart ssh.service
sudo sshd -T | grep -Ei 'passwordauthentication|permitrootlogin'

Step 2: turn on the firewall

The Ubuntu firewall documentation notes that UFW is disabled by default and that you should allow SSH before enabling it. For a typical web server:

sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

Do not open the database port (5432) or the Node.js port (for example 3000) to the internet. If your cloud provider offers a network firewall or security group, apply the same rules there too, so there are two layers.

Step 3: security updates and kernel patches

The Ubuntu automatic updates documentation installs the tool with sudo apt install unattended-upgrades, configured in /etc/apt/apt.conf.d/50unattended-upgrades and /etc/apt/apt.conf.d/20auto-upgrades. The allowed origins include Ubuntu's security pocket, so security fixes are applied without waiting for someone to remember.

  • Kernel updates need a reboot to take effect. Schedule a monthly maintenance window, for example Friday early morning when Egyptian offices are closed.
  • Livepatch: according to Ubuntu, Livepatch patches critical and high-severity kernel vulnerabilities while the system runs, enabled with sudo pro attach and sudo pro enable livepatch. It is free for up to 5 machines for personal use or evaluation, and it does not patch userspace libraries such as OpenSSL, which still rely on normal updates.
  • Software outside Ubuntu's repositories, such as a separately installed Node.js runtime and your application's npm dependencies, needs its own update routine.

Step 4: Nginx, TLS and certificates

Let Nginx face the internet and forward requests to the application, which listens only on 127.0.0.1. A few settings worth checking in the server block:

server_tokens off;
ssl_protocols TLSv1.2 TLSv1.3;
add_header Strict-Transport-Security "max-age=63072000" always;
client_max_body_size 10m;
location / { proxy_pass http://127.0.0.1:3000; }

  • server_tokens: the nginx documentation shows the default is on, which emits the nginx version on error pages and in the Server header. Turning it off hides the version.
  • TLS profile: the Mozilla Server Side TLS guidelines now live at TLSRef. The Intermediate profile, recommended for general-purpose servers, supports TLS 1.2 and TLS 1.3 and uses HSTS with max-age=63072000 (two years). Use its configurator to generate the full cipher settings for your Nginx version, and enable HSTS only when every subdomain serves HTTPS.
  • Request size: nginx's default client_max_body_size is 1m; set it deliberately to the largest upload you accept.
  • After any change, run sudo nginx -t before sudo systemctl reload nginx.

Automate certificate renewal

Let's Encrypt announced in December 2025 that its certificates, 90 days today, will drop to 45 days by 2028: the default profile moves to 64-day certificates on 10 February 2027 and to 45 days on 16 February 2028. Manual renewal will not keep up. Use certbot or another ACME client with a scheduled renewal, test it with sudo certbot renew --dry-run, and add an expiry alert. See SSL certificates.

Step 5: least privilege for the app and the database

The application

  • Run the Node.js app as a dedicated non-root user, managed by a process manager such as PM2 configured to start on boot under that user.
  • Keep secrets in an environment file readable only by that user (chmod 600), never in the Git repository.
  • Rotate application logs so the disk does not fill up, a common cause of outages.
  • Remove packages and services you do not use: an old FTP server, a test database, a phpMyAdmin left from a previous project.

The database

The PostgreSQL documentation states that the default listen_addresses value is localhost, "which allows only local TCP/IP loopback connections". Keep it that way when the app and database share a server. If the database has its own server, listen only on the private network, allow only the application server's IP in pg_hba.conf with scram-sha-256 authentication, and restrict the port in the firewall:

sudo ufw allow from 10.0.0.5 to any port 5432 proto tcp

Give the application a database user with only the permissions it needs, not a superuser, and keep database backups off the server; see our backup and disaster recovery guide.

Step 6: monitoring, logs and accountability

  • Watch SSH: sudo journalctl -fu ssh.service shows login attempts live; review failed and successful logins regularly.
  • Uptime and resources: alerts for downtime, disk above 80%, high memory, and certificate expiry.
  • Application logs: logins, permission changes and exports, kept long enough to investigate an incident.
  • Access review: remove SSH keys of staff and vendors who leave, the same day.

This is also a legal matter in Egypt. Under Anti-Cyber and IT Crimes Law 175 of 2018, Article 29 penalises a person managing a website or information system who exposes it to a crime, with a lower penalty tier where this results from negligence in taking the security precautions set by the law's executive regulations. The Personal Data Protection Law 151/2020 requires controllers to take technical and organizational measures to secure personal data. See Law 175 of 2018 for companies and breach notification within 72 hours.

Linux server hardening checklist

ControlHow to verify
Named admin users, SSH keys onlysudo sshd -T shows passwordauthentication no
Root login disabledsudo sshd -T shows permitrootlogin no
Firewall on, only 22, 80, 443 opensudo ufw status verbose
No unexpected listening servicessudo ss -tlpn
Automatic security updatesunattended-upgrades installed and configured
Kernel patchedReboot window or Livepatch status
TLS 1.2/1.3 only, HSTS, version hiddenNginx config reviewed, external TLS test
Certificate renewal automatedsudo certbot renew --dry-run
Database not public, least-privilege userlisten_addresses and pg_hba.conf reviewed
Backups off-server and restore testedLast restore test date recorded

For a deeper standard, the CIS Benchmarks offer prescriptive, consensus-developed configuration recommendations, with free PDF downloads, covering Ubuntu, Nginx and PostgreSQL among others. An external test also helps; see penetration testing vs vulnerability scanning, and our cybersecurity plan for Egyptian SMEs for the wider picture.

How Nilex helps

Nilex deploys its systems on Ubuntu Server with Nginx as a reverse proxy, PM2 for process management and automated SSL certificates, following the steps above: key-only SSH, firewall rules, automatic security updates, private database access and monitoring. We can harden a server you already run, document the setup, and hand your IT team a checklist they can maintain.

Frequently asked questions

Is a new Ubuntu server secure by default?

Not fully. Ubuntu is a solid base, but UFW is disabled by default and OpenSSH allows password authentication unless configured otherwise. You need to harden SSH, enable the firewall and set up updates yourself.

Should I change the SSH port from 22?

Changing the port reduces noise from automated scans but does not add real protection. Key-only login, disabled passwords and a firewall matter much more. If you change it, update the firewall rules first so you do not lock yourself out.

How often should a production server be updated?

Security updates should install automatically every day. Plan a regular maintenance window for reboots after kernel updates, or use Livepatch, and update Node.js and application dependencies on your own schedule after testing.

Can the database be on the same server as the application?

Yes, for small and medium systems it is common and safe if PostgreSQL listens only on localhost and backups are stored elsewhere. Larger systems benefit from a separate database server on a private network.

What happens if Let's Encrypt certificates are not renewed?

Browsers show a security warning and customers cannot use your site or app safely. With lifetimes shrinking toward 45 days by 2028, automated renewal and an expiry alert are essential.

Running your business system on a Linux server that nobody has reviewed since launch? Ask for a server hardening review or a free consultation through our contact page.

LET'S BUILD

YOUR VISION.
OUR TECHNOLOGY.

Tell us what your business needs. We'll build the system around it.

START A CONVERSATION →

or email us at info@nilexdigitalsystems.com