Open-source project
buildplan/du_setup avatar
buildplan/du_setup

du_setup: a Bash script that hardens a fresh Debian or Ubuntu server

A Bash script to automate the initial configuration and security hardening of Debian and Ubuntu servers.

813 stars74 forksShellMIT

At a glance

What is it?
du_setup is an MIT licensed Bash script that takes a fresh Debian or Ubuntu machine through user creation, SSH hardening, UFW, Fail2Ban, unattended upgrades, sysctl settings, rsync backups and optional Tailscale, and the README goes out of its way to tell you to checksum the script before you run it as root. It suits a sysadmin or a self-hoster who provisions servers by hand, and it is a poor fit if you want configuration as code you can review and reapply.
Who is it for?
du_setup suits someone who provisions a Debian or Ubuntu VPS by hand and wants a hardened baseline in one sitting, since it covers the long checklist of user management, SSH, firewall, intrusion prevention, kernel settings, updates, time sync, backups and VPN, and it pauses for the one step that can lock you out.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 71 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A fresh image in, a hardened host out

du_setup is described as a Bash script to automate the initial configuration and security hardening of Debian and Ubuntu servers, and the README states its own scope plainly: it automates the initial setup and security hardening of a fresh Debian or Ubuntu server, it is idempotent, safe and suitable for production environments, providing a secure baseline for further customization.

The script runs interactively, guiding users through critical choices while automating the essential security and setup tasks. That combination is the design in one sentence. The things that need a human decision, which user, which SSH port, whether to install Docker, get asked, and the things that are the same on every machine get applied.

The feature list is a checklist of the standard first hour of owning a VPS, and it is worth reading as a description of what the script does rather than as marketing.

Secure user management creates a new sudo user and disables root SSH access, with an option to install a custom .bashrc. SSH hardening configures a custom port, enforces key based authentication and applies best practices. The firewall is set up with UFW using secure defaults and customisable rules. 2FA is optional and shows a QR code for any TOTP authenticator. Fail2Ban is installed and configured to block malicious IPs. Kernel hardening applies a set of recommended sysctl settings against common network and memory threats. unattended-upgrades is enabled for automatic security patches. chrony is configured for NTP time synchronisation, with an optional swap file for low RAM systems.

The project is MIT licensed, the primary language is Shell, the homepage is buildplan.org/du_setup/getting-started, the last push to the repository was on 2026-08-13, and the current version is v0.81.4, last updated on 2026-08-12, with v0.81.3 and v0.81.2 following days earlier in the same month.

The SSH change is the step that can lock you out

The README carries a warning in a block quote, and it is the most important thing in the document.

The script pauses to verify SSH access on the new port before disabling old access methods, and you are told to test the new SSH connection from a separate terminal before proceeding.

That pause is the whole safety mechanism, and it is worth understanding why it exists. Changing the SSH port and switching to key based authentication both have the same failure mode: if the new configuration does not work, you are locked out of a machine you may only have console access to. The script therefore applies the change, tests it, and only then removes the old way in. The failure mode it is designed to avoid is the one where the old access is disabled first and the new access turns out not to work.

The related warning is about the network perimeter. You are told to ensure your VPS provider's firewall allows the custom SSH port, the backup server's SSH port, for example 23 for Hetzner Storage Box, and Tailscale traffic, with UDP 41641 used for direct connections.

That is a limitation the script cannot engineer around. A host firewall rule inside the machine is irrelevant if the provider's own firewall drops the packets before they arrive, and the script cannot add a rule to a firewall it does not control. For a provider with a control panel firewall, that is a manual step you take before you run the script, not after.

The same reasoning appears in the feature table, where the SSH task is described as disabling root login, configuring key based authentication, setting a custom SSH port, and supporting rollback of the SSH configuration if connectivity fails. The rollback path is a safety net, not the primary defence, and it needs the script to still be reachable, which is exactly the situation the pause prevents.

Check the checksum before you run it as root

The README does something that few setup scripts ask for, and it is the first thing you should do.

You can download the official checksum file and automatically compare it against your downloaded script.

bash
# Download the official checksum file
wget https://raw.githubusercontent.com/buildplan/du_setup/refs/heads/main/du_setup.sh.sha256

# Run the check (it should output: du_setup.sh: OK)
sha256sum -c du_setup.sh.sha256

The manual route is to generate the hash of the downloaded script and compare it to the published one.

bash
# Generate the hash of your downloaded script
sha256sum du_setup.sh

Or, in a single line, which is the version to put in your own provisioning notes.

bash
echo 87b55982d3eb81ac94fe3bb41749b55af08680a0920d2053bd7bbd08ce232313 du_setup.sh | sha256sum --check

The published hash is for a specific version, and the README is explicit that the outputs must match exactly. That means the check is tied to the release you downloaded, so a fresh pull of main will not match a hash published for an earlier version, and the right workflow is to download and verify in one step.

The download itself is two commands.

bash
wget https://raw.githubusercontent.com/buildplan/du_setup/refs/heads/main/du_setup.sh
chmod +x du_setup.sh

This is a reasonable posture for a script that asks for root, writes to the SSH daemon configuration, changes the firewall, installs packages and can reboot a machine. The risk profile is not that the script is hostile, it is that a truncated download or a substituted file produces a broken or unwanted machine, and the checksum turns that into a detectable condition.

What the run actually changes on the box

The What It Does table is a task list, and it is more precise than the feature list above it.

Provider package cleanup detects and optionally removes cloud provider packages, monitoring agents and default provisioning users, to reduce attack surface and unnecessary services. That is a real hardening step, because a default provisioning user is a login that nobody remembers disabling.

System compatibility checks verify OS compatibility, root privileges and internet connectivity, and the package management task repeats that verification and prevents running on unsupported environments. So the script refuses to start on a distribution it does not know about rather than applying a hardening profile to the wrong machine.

Setup user creation and management creates or uses an existing admin user, with optional SSH key setup and strong password enforcement, and includes a marker file for cleanup exclusion. That marker matters for the cleanup task above, so that removing a provider package does not remove the account you are standing in as.

SSH hardening and rollback disables root login, configures key based authentication, sets a custom SSH port and supports rollback of the SSH configuration if connectivity fails. Firewall setup configures UFW to deny incoming traffic by default and allow specific user defined ports. The intrusion prevention setup configures Fail2Ban or CrowdSec to monitor SSH and UFW logs and block suspicious addresses, and the README is truncated at that point, so the rest of the table is not visible in the copy available here.

Behind these, two safety nets run constantly. Critical configuration files are backed up before modification, and the backups are stored in /root/setup_harden_backup_*, so a bad change can be undone by hand. And all actions are logged to /var/log/du_setup_*.log, which is where you look when a service stopped working after a run.

Backups over rsync, and a VPN option

Two features go beyond the usual hardening checklist, and they are the ones that change how you operate the machine afterwards.

The first is remote rsync backups. The script configures automated rsync backups over SSH to any compatible server, with the Hetzner Storage Box given as the example, with SSH key automation using sshpass or a manual key, cron scheduling, ntfy or Discord notifications, and a customisable exclude file. There is also a backup testing option, an optional test backup to verify the rsync configuration before the schedule is set up.

The backup test is the part that matters. A backup that has never been restored is a hypothesis, and the test run is what turns it into evidence, and doing it before scheduling means the first time you discover a broken exclude pattern is not the morning you need the data.

The second feature is Tailscale. The script installs Tailscale and connects it to the standard Tailscale network, which needs a pre-auth key, or to a custom server, which needs a URL and a key. It configures the optional flags, including --ssh, --advertise-exit-node, --accept-dns and --accept-routes.

The pre-auth key requirement is worth noting for automation. The README explains that the standard key comes from the Tailscale admin console and starts with tskey-auth-, and a custom server might be something like https://ts.mydomain.cloud. Both need a credential created out of band before the script runs, which is the same pattern as the rsync backup credentials and the SSH keys.

One more thing the script offers is security auditing after the fact, running Lynis for system hardening audits and debsecan for package vulnerability checks, with the results logged for review. It is a second opinion on the hardening you just applied, and the logging means you can look at the findings later rather than watching them scroll past.

Idempotence, quiet mode and a documented walk through

Three things make this usable in an automated provisioning pipeline rather than only by hand.

The first is idempotence. The README describes the script as idempotent, which for a setup script means running it twice does not break anything, which is the difference between a tool you can re-run after a failed run and a tool you run exactly once and hope for.

The second is the quiet mode, which exists for automated provisioning.

bash
sudo -E ./du_setup.sh --quiet

The third is the environment preservation. The README says to run the script ideally as root, and that if you are a sudo user you can switch to root with sudo su, or alternatively run it with sudo -E, where the -E flag preserves the environment variables.

bash
./du_setup
bash
sudo -E ./du_setup.sh

The -E flag detail is not cosmetic. A script that reads configuration from the environment, which this one does for the Tailscale key and the backup credentials among other things, will not see your variables if sudo resets the environment, and it will fail or fall back to defaults in a way that is annoying to diagnose.

The prerequisites are modest and worth checking before you start: a fresh installation of a compatible OS, root or sudo privileges, internet access for package downloads, and a minimum of 2GB of free disk for the swap file and temporary files. For remote backups you need an SSH accessible server with credentials or key access, and for Hetzner the SSH port used for rsync is 23. For Tailscale you need a pre-auth key.

The repository also contains a walk-through.md document alongside the script, the checksum file and a renovate.json for dependency updates, and the badges at the top of the README show a shell script linter and a Codacy security scan, which tells you the script is itself linted and scanned in CI.

Editorial conclusion

du_setup suits someone who provisions a Debian or Ubuntu VPS by hand and wants a hardened baseline in one sitting, since it covers the long checklist of user management, SSH, firewall, intrusion prevention, kernel settings, updates, time sync, backups and VPN, and it pauses for the one step that can lock you out. It does not suit a team that manages infrastructure as code, because the result is a mutated machine rather than a declared one, and it does not suit a host where you cannot afford the SSH change window. Before you run it, verify the script against the published SHA256 checksum, read the walk-through document in the repository, and confirm your provider firewall allows the custom SSH port, the backup server port and the Tailscale traffic, because the script cannot open a firewall it does not control.

Frequently asked questions

What does du_setup do on a fresh server?

It creates or reuses a sudo user, disables root SSH login, configures key based authentication on a custom SSH port with rollback support, sets up UFW with deny by default, installs Fail2Ban or CrowdSec, applies recommended sysctl settings, enables unattended-upgrades, configures chrony time sync, and can set up remote rsync backups, Tailscale, Docker and Lynis and debsecan audits, logging everything to /var/log/du_setup_*.log.

How do I install du_setup safely?

Download du_setup.sh with wget and make it executable, then verify it. The README gives an automatic check using sha256sum -c against the published du_setup.sh.sha256 file, and a manual alternative comparing sha256sum du_setup.sh with the published hash, or piping that hash into sha256sum --check.

Which operating systems does du_setup support?

The README lists Debian 12 and 13, and Ubuntu 20.04, 22.04, 24.04 and 26.04, with Ubuntu 24.10, 25.04 and 25.10 marked as experimental. The script also checks OS compatibility, root privileges and internet connectivity, and prevents running on unsupported environments.

What stops du_setup from locking me out of my server?

The script pauses to verify SSH access on the new port before disabling the old access methods, and the README tells you to test the new connection from a separate terminal first. It also backs up critical configuration files to /root/setup_harden_backup_* before modifying them, and supports rolling back the SSH configuration if connectivity fails.

Can du_setup run unattended?

Yes. The script supports a quiet mode for automated provisioning with sudo -E ./du_setup.sh --quiet, and the README notes that the -E flag preserves environment variables, which matters for the Tailscale key and the backup credentials.

Official sources

  1. buildplan/du_setup on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/buildplan-du-setup.svg)](https://hysenlabs.com/projects/buildplan-du-setup)