Server Setup Script — Complete Guide
serversetup.sh is an interactive Bash script that hardens a fresh Ubuntu server in a single run. It collects all configuration up front, shows you a summary, asks for confirmation, then applies every change in order. A post-install verification pass checks every step and only reboots if everything passed.
Requirements
- OS: Ubuntu 22.04 or 24.04 (fresh install recommended)
- Access: Must be run as root (
sudo -ior direct root login) - Network: Outbound internet access (needed to download packages and optionally fetch your SSH key)
How to Use
Log into your server as root and run the following three commands:
curl -fsSL https://nanoapps.ca/scripts/server-setup/serversetup.sh -o serversetup.sh
chmod +x serversetup.sh
./serversetup.sh
The script collects all configuration input first, then shows a summary and asks you to confirm before making any changes. Nothing is modified until you type yes at the confirmation prompt.
Input Prompts
The script asks for five pieces of information before it does anything. All prompts read from /dev/tty directly so they work correctly even when piped.
- Username — The name for the new sudo account. Press Enter to use the default (
admin). - Password — Password for the new account. Entered twice to confirm. Cannot be empty.
- SSH Key URL — A public URL pointing to your SSH public key file (e.g. a raw GitHub Gist or any HTTPS URL). The script downloads it, checks that it looks like a real SSH key, and installs it. Press Enter to skip and use password authentication instead.
- Hostname — The server’s hostname. Press Enter to keep the current one.
- Timezone — A valid tz database name (e.g.
America/New_York). Press Enter to useAmerica/Winnipeg.
After all prompts a configuration summary is shown. Type yes to proceed or anything else to abort with no changes made.
System Update & Package Install
What it does: Runs apt-get dist-upgrade to bring all installed packages to their latest versions, then installs four tools the rest of the script depends on:
- curl — used to fetch your SSH key if you provide one
- fail2ban — brute-force protection for SSH
- unattended-upgrades — automatic security patch application
- ufw — the Uncomplicated Firewall
All apt prompts are suppressed with DEBIAN_FRONTEND=noninteractive and existing config files are kept as-is (--force-confold) so nothing unexpected changes.
Automatic Security Updates
What it does: Configures unattended-upgrades to automatically download and install security patches from the Ubuntu security repository. Non-security updates (feature releases, etc.) are left for you to apply manually.
Key settings applied:
- Only packages from
${distro_codename}-securityare auto-installed Automatic-Rebootis set tofalse— the server will never reboot automatically due to an update
Config is written to /etc/apt/apt.conf.d/50unattended-upgrades.
Sudo User Creation
What it does: Creates a new user account and adds it to the sudo group so it can run commands as root when needed.
If the username you entered already exists on the system, the script updates its password instead of failing.
The password variables are cleared from memory immediately after the account is set up.
SSH Key Setup
What it does: If you provided a key URL during setup, the script downloads the file, validates that it contains a real SSH public key, and installs it as the authorized key for your new user.
Validation checks performed:
- URL must return HTTP 200
- Downloaded file must not be empty
- File must start with a recognized key type (
ssh-rsa,ssh-ed25519,ecdsa-sha2-*, etc.)
If any check fails you are prompted to try a different URL or press Enter to skip. The key is installed to /home/USERNAME/.ssh/authorized_keys with correct ownership and permissions (700 on the directory, 600 on the file).
~/.ssh/id_ed25519.pub file. Paste the raw Gist URL when prompted.
SSH Hardening
What it does: Locks down the SSH daemon configuration using a drop-in file (/etc/ssh/sshd_config.d/99-hardening.conf) that loads last and wins over any conflicting settings.
Settings applied:
- PermitRootLogin no — direct root SSH login is disabled
- PasswordAuthentication — set to
noif you provided an SSH key,yesif using password login - LoginGraceTime 30 — unauthenticated connections are dropped after 30 seconds
- MaxAuthTries 3 — only 3 attempts per connection before it is closed
- AllowUsers — only the user you created can log in over SSH
If a 50-cloud-init.conf drop-in exists (common on cloud VMs), it is overwritten to match the chosen auth mode so it cannot override the hardening settings.
The original /etc/ssh/sshd_config is backed up to /etc/ssh/sshd_config.bak before any changes. The config is validated with sshd -t before restarting the service — if validation fails, the backup is restored automatically.
UFW Firewall
What it does: Enables UFW (Uncomplicated Firewall) with a restrictive default policy and opens only what is needed to keep SSH access.
- Default incoming: DENY — all inbound traffic is blocked unless explicitly allowed
- Default outgoing: ALLOW — the server can make outbound connections freely
- Port 22/tcp: ALLOW — SSH access is preserved
After the script finishes, open additional ports for any services you install (e.g. ufw allow 80/tcp for a web server).
Fail2Ban
What it does: Configures Fail2Ban to watch SSH login attempts and automatically ban IPs that show signs of brute-force behavior.
Settings applied (written to /etc/fail2ban/jail.d/sshd.conf):
- maxretry: 5 — 5 failed attempts triggers a ban
- findtime: 600 — the 5 attempts must occur within a 10-minute window
- bantime: 3600 — banned IPs are blocked for 1 hour
- backend: systemd — reads SSH logs from the systemd journal
Fail2Ban is enabled and started immediately. Current ban status is visible in the MOTD each time you log in.
Hostname
What it does: Sets the server hostname using hostnamectl set-hostname. The hostname you enter is validated — only letters, numbers, and hyphens are accepted (no leading or trailing hyphens, maximum 63 characters).
Press Enter at the hostname prompt to leave the current hostname unchanged.
Timezone
What it does: Sets the system timezone using timedatectl set-timezone. The value you enter is validated against the system’s tz database before it is applied.
The default is America/Winnipeg. Press Enter to accept it, or enter any valid timezone name (e.g. America/New_York, Europe/London, UTC).
IPv6 Disable
What it does: Disables IPv6 at the kernel level by writing to /etc/sysctl.d/99-disable-ipv6.conf and applying it immediately with sysctl -p.
This reduces the server’s attack surface on environments where IPv6 is not needed or used. The setting persists across reboots.
Login Message (MOTD)
What it does: Replaces Ubuntu’s default login message with a clean, informative status dashboard that displays every time you SSH in.
The MOTD shows:
- Hostname and current date/time
- System uptime and load averages (1m / 5m / 15m)
- Memory usage (used / free / total in GB)
- Disk usage for the root partition
- Public and private IP addresses
- Last login IP and time
- Number of active sessions
- Pending security updates (green if up to date, yellow if updates are waiting)
- Status of fail2ban, ufw, and unattended-upgrades (green if all running, red if any are down)
- UFW open ports
- Fail2Ban current ban count and banned IPs
The script is installed to /etc/update-motd.d/99-hardened-motd and all other MOTD scripts are disabled so only this one runs.
Post-Install Verification
What it does: After all configuration is applied, the script runs a series of automated checks to confirm everything worked correctly.
Checks performed:
- User exists and is in the sudo group
- SSH authorized_keys installed (if key mode)
- SSH hardening drop-in config present
- PermitRootLogin is set to no
- PasswordAuthentication is set correctly for the chosen mode
- SSH service is running
- UFW is active
- UFW allows port 22
- Fail2Ban is running
- Timezone is set to the requested value
- IPv6 is disabled
- MOTD script is installed and executable
If all checks pass, the server reboots automatically after a 5-second countdown (press Ctrl+C to cancel). If any check fails, the reboot is cancelled and the specific issues are listed so you can investigate before rebooting manually.
All runs are logged to /var/log/hardening.log.
After the Script
Once the server has rebooted, log in as the new user (not root). A few things to do next depending on what the server will run:
- Open additional firewall ports — e.g.
sudo ufw allow 80/tcpfor HTTP,sudo ufw allow 443/tcpfor HTTPS - Install your software — the server is now at a clean, hardened baseline ready for whatever you are building
- Check the MOTD — the login dashboard will show any pending updates or service issues on every login going forward
Server Setup Script is developed by NanoApps. For support, visit nanoapps.ca.