DEV Community

Cover image for Hardening Your Deploynix Server: A Step-by-Step Security Audit
Deploynix
Deploynix

Posted on • Originally published at deploynix.io

Hardening Your Deploynix Server: A Step-by-Step Security Audit

Every server connected to the internet is a target. Within minutes of provisioning, automated bots will begin probing your server for open ports, default credentials, and known vulnerabilities. While Deploynix applies a strong set of security defaults during provisioning, understanding what those defaults are and verifying them gives you confidence that your infrastructure is genuinely secure.

This guide walks you through a comprehensive security audit of your Deploynix server. Whether you've just provisioned a fresh server or inherited one that's been running for months, these steps will help you verify your security posture and identify any gaps.

Step 1: Verify Root Login Is Disabled

Root is the most powerful account on any Linux system. If an attacker gains root access, they have unrestricted control over your server. Disabling direct root login forces attackers to compromise both a user account and the sudo password, adding a critical layer of defense.

What Deploynix does by default:

Deploynix creates a deploynix user during provisioning and disables root login via SSH. All administrative tasks are performed through this user with sudo.

How to verify:

SSH into your server and check the SSH daemon configuration:

grep -i "PermitRootLogin" /etc/ssh/sshd_config
Enter fullscreen mode Exit fullscreen mode

You should see:

PermitRootLogin prohibit-password
Enter fullscreen mode Exit fullscreen mode

This prevents root login with passwords while still allowing key-based root access if needed. For maximum security, you can change it to no to block all root login entirely:

sudo sed -i 's/^PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo systemctl restart sshd
Enter fullscreen mode Exit fullscreen mode

Also verify that the root account has no usable password:

sudo passwd -S root
Enter fullscreen mode Exit fullscreen mode

The output should show L (locked) or NP (no password).

Step 2: Enforce SSH Key-Only Authentication

Password-based SSH authentication is vulnerable to brute-force attacks. Even with strong passwords, the sheer volume of automated attacks makes key-based authentication the only sensible choice.

What Deploynix does by default:

Deploynix disables password authentication during provisioning. All SSH access uses key-based authentication, with keys managed per-server through the Deploynix dashboard.

How to verify:

grep -i "PasswordAuthentication" /etc/ssh/sshd_config
Enter fullscreen mode Exit fullscreen mode

Expected output:

PasswordAuthentication no
Enter fullscreen mode Exit fullscreen mode

Also check that challenge-response authentication is disabled:

grep -i "ChallengeResponseAuthentication\|KbdInteractiveAuthentication" /etc/ssh/sshd_config
Enter fullscreen mode Exit fullscreen mode

Both should be set to no. If either is set to yes, update the configuration and restart SSH.

Audit authorized keys:

Review which SSH keys have access to your server:

cat ~/.ssh/authorized_keys
Enter fullscreen mode Exit fullscreen mode

Remove any keys you don't recognize. Each key should correspond to a team member or service that legitimately needs access. Compare this list against the SSH keys shown in your Deploynix dashboard.

Step 3: Review and Configure the Firewall

A firewall is your server's bouncer. It decides which traffic gets in and which gets turned away. The principle of least privilege applies: only open the ports your application actually needs.

What Deploynix does by default:

Deploynix enables ufw (Uncomplicated Firewall) during provisioning with a default deny policy for incoming traffic. It opens ports 22 (SSH), 80 (HTTP), and 443 (HTTPS).

How to verify:

sudo ufw status verbose
Enter fullscreen mode Exit fullscreen mode

You should see something like:

Status: active
Default: deny (incoming), allow (outgoing), disabled (routed)

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere
443/tcp                    ALLOW IN    Anywhere
Enter fullscreen mode Exit fullscreen mode

What to look for:

  • Are there any unexpected ports open? Database ports (3306, 5432) should never be open to the public unless your architecture specifically requires it.
  • Is the default incoming policy set to deny?
  • If you're running Valkey, is port 6379 restricted to localhost or internal IPs only?

You can manage firewall rules directly from the Deploynix dashboard. Any rules you add there are pushed to the server's ufw configuration immediately.

For dedicated database servers:

If you've provisioned a separate database server through Deploynix, verify that the database port is only accessible from your application server's IP:

sudo ufw status | grep 3306
Enter fullscreen mode Exit fullscreen mode

You should see the port allowed only from specific IPs, not Anywhere.

Step 4: Install and Configure fail2ban

fail2ban monitors log files for suspicious activity and automatically bans IP addresses that show malicious behavior, such as repeated failed SSH login attempts.

Installation:

sudo apt update && sudo apt install -y fail2ban
Enter fullscreen mode Exit fullscreen mode

Configuration:

Create a local configuration file (never edit the main config, as it gets overwritten during updates):

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Enter fullscreen mode Exit fullscreen mode

Edit the local file to configure the SSH jail:

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
Enter fullscreen mode Exit fullscreen mode

This configuration bans an IP for one hour after three failed SSH login attempts within ten minutes.

For additional protection, consider adding jails for Nginx:

[nginx-http-auth]
enabled = true
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 3600

[nginx-limit-req]
enabled = true
filter = nginx-limit-req
logpath = /var/log/nginx/error.log
maxretry = 10
bantime = 3600
Enter fullscreen mode Exit fullscreen mode

Start and enable fail2ban:

sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Enter fullscreen mode Exit fullscreen mode

Verify it's running:

sudo fail2ban-client status
sudo fail2ban-client status sshd
Enter fullscreen mode Exit fullscreen mode

Step 5: Enable Unattended Security Updates

Security patches for the operating system and installed packages are released regularly. Unattended upgrades ensure critical security fixes are applied automatically without waiting for manual intervention.

Installation and configuration:

sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Enter fullscreen mode Exit fullscreen mode

Verify the configuration:

cat /etc/apt/apt.conf.d/50unattended-upgrades
Enter fullscreen mode Exit fullscreen mode

Ensure the Unattended-Upgrade::Allowed-Origins section includes security updates:

Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
};
Enter fullscreen mode Exit fullscreen mode

Optional but recommended: Enable automatic reboots for kernel updates (schedule them during low-traffic hours):

Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
Enter fullscreen mode Exit fullscreen mode

Check that the service is running:

sudo systemctl status unattended-upgrades
Enter fullscreen mode Exit fullscreen mode

Step 6: Remove Unused Services and Packages

Every running service is a potential attack surface. If you don't need it, remove it.

Audit running services:

sudo systemctl list-units --type=service --state=running
Enter fullscreen mode Exit fullscreen mode

Common services to review and potentially disable:

  • postfix/sendmail: If you're not sending email directly from the server (you probably shouldn't be in production), disable it.
  • avahi-daemon: Used for mDNS/DNS-SD, rarely needed on a server.
  • cups: Print service, definitely not needed on a web server.
  • bluetooth: Should never be running on a server.

To disable an unnecessary service:

sudo systemctl stop service-name
sudo systemctl disable service-name
Enter fullscreen mode Exit fullscreen mode

Audit listening ports:

sudo ss -tlnp
Enter fullscreen mode Exit fullscreen mode

This shows all TCP ports that are listening and which process owns them. Every open port should be accounted for. If you see a port you don't recognize, investigate the owning process.

Remove unused packages:

sudo apt autoremove -y
Enter fullscreen mode Exit fullscreen mode

Step 7: Verify File Permissions

Incorrect file permissions can expose sensitive data or allow unauthorized modifications.

Critical files to check:

# SSH configuration should be owned by root, readable only by root
ls -la /etc/ssh/sshd_config
# Expected: -rw------- 1 root root

# Your application's .env file
ls -la /home/deploynix/your-site/.env
# Expected: -rw------- 1 deploynix deploynix

# SSH authorized keys
ls -la ~/.ssh/authorized_keys
# Expected: -rw------- 1 deploynix deploynix

# SSH directory
ls -la -d ~/.ssh
# Expected: drwx------ 2 deploynix deploynix
Enter fullscreen mode Exit fullscreen mode

Fix permissions if they're too open:

chmod 600 /home/deploynix/your-site/.env
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Enter fullscreen mode Exit fullscreen mode

Step 8: Verify Nginx Security Configuration

Your web server is the front door to your application. Its configuration should block malicious requests before they reach your Laravel application.

Check that dotfiles are blocked:

# In your Nginx site configuration, look for:
grep -A2 "location.*\\." /etc/nginx/sites-enabled/*
Enter fullscreen mode Exit fullscreen mode

You should find a block like:

location ~ /\. {
    deny all;
}
Enter fullscreen mode Exit fullscreen mode

This prevents direct access to .env, .git, .htaccess, and other dotfiles.

Check that sensitive paths are blocked:

Your Nginx config should also block access to directories like vendor/, storage/, and node_modules/.

Verify security headers:

Test your site's headers using curl:

curl -I https://your-domain.com
Enter fullscreen mode Exit fullscreen mode

Look for the presence of security headers like X-Content-Type-Options, X-Frame-Options, and Strict-Transport-Security.

Step 9: Audit User Accounts

Review all user accounts on your server to ensure there are no unauthorized users.

# List all users with login shells
grep -v '/nologin\|/false' /etc/passwd

# List all users with sudo privileges
grep -Po '^sudo.+:\K.*$' /etc/group
Enter fullscreen mode Exit fullscreen mode

Every user with a login shell should be accounted for. Every sudo user should be authorized. Remove any accounts that are no longer needed:

sudo userdel -r username
Enter fullscreen mode Exit fullscreen mode

Step 10: Set Up Log Monitoring

Logs are your audit trail. Without them, you're flying blind.

Key log files to monitor:

  • /var/log/auth.log - Authentication attempts (SSH logins, sudo usage).
  • /var/log/syslog - General system messages.
  • /var/log/nginx/access.log - Web traffic.
  • /var/log/nginx/error.log - Web server errors.
  • /var/log/fail2ban.log - fail2ban actions.

Set up log rotation:

Verify that logrotate is configured to prevent logs from consuming all disk space:

cat /etc/logrotate.d/nginx
Enter fullscreen mode Exit fullscreen mode

On paid plans, Deploynix's real-time monitoring dashboard tracks CPU, memory, disk usage, and load average. Critical alerts are sent via email to the server owner when thresholds are exceeded. Combine this with application-level logging (Laravel's built-in logging to a service like Sentry or Flare) for comprehensive visibility.

Creating a Regular Audit Schedule

Security is not a one-time task. Schedule regular audits:

  • Weekly: Check fail2ban status, review auth logs for anomalies, verify disk space.
  • Monthly: Review firewall rules, audit user accounts, check for pending system updates.
  • Quarterly: Full security audit using this checklist, review and rotate credentials, test backup restoration.

Conclusion

Deploynix gives you a strong security foundation out of the box. Root login is disabled, SSH keys are enforced, firewalls are configured, and SSL certificates are automated. But security is a shared responsibility. The steps in this guide help you verify those defaults, add additional layers of protection like fail2ban and unattended upgrades, and remove unnecessary attack surface.

A hardened server is not impenetrable, but it raises the cost of an attack to the point where automated scanners and opportunistic attackers move on to easier targets. Combined with secure application code and regular audits, these measures provide robust protection for your Laravel applications on Deploynix.

Run through this checklist today. Your future self, and your users, will thank you.

Top comments (0)