Fall 2026brianmars.comBuilt at EGBT Technologies
ReferenceOS

Base hardening

Updates, baseline tools, key-only SSH, a UTF-8 locale, and automatic security updates on a fresh Debian box.

Names and addresses are placeholders. The list says what each one stands for.

What it does here

The first login does the dull work everything later assumes: current packages, sudo, key-only SSH, and a locale mosh will accept. Find the box's address with ip a on its console and connect with ssh youruser@<box-ip>.

From the manual

Canonical setup

Update everything, put your key on the box, turn off password logins, and let security updates install themselves.

Update
sudo apt update && sudo apt full-upgrade -y
Copy your key (run on your own machine)
ssh-copy-id youruser@<box-ip>
Automatic security updates
sudo dpkg-reconfigure -plow unattended-upgrades
Edited for this build

In this build

  1. Baseline packages include tmux, mosh and partedMinimal Debian ships none of them, and later cards need all three.
  2. SSH hardening in a drop-inA file in sshd_config.d keeps the change apart from the package's own config.
  3. UTF-8 locale confirmedmosh won't start without one.
  4. Timezone setUse your own; timedatectl list-timezones lists them.
  5. Plain OpenSSH as the way inNot Tailscale SSH, which breaks mosh. See the Tailscale card.
Baseline packages
sudo apt install -y sudo curl wget git vim htop ca-certificates \
  gnupg lsb-release unattended-upgrades ufw tmux mosh parted
/etc/ssh/sshd_config.d/10-hardening.conf
sudo tee /etc/ssh/sshd_config.d/10-hardening.conf >/dev/null <<'EOF'
PasswordAuthentication no
PermitRootLogin no
EOF
sudo systemctl restart ssh
Locale and timezone
locale                                 # should show en_US.UTF-8
sudo dpkg-reconfigure locales          # if not: generate and select it
sudo timedatectl set-timezone Region/City   # e.g. Europe/Berlin

Optional: Avahi makes the box answer to devbox.local on the home network. Minimal Debian leaves mDNS out. MagicDNS does the same job from any network, so skip this if you'll always use the tailnet.

Avahi (optional)
sudo apt install -y avahi-daemon libnss-mdns
sudo systemctl enable --now avahi-daemon
Where it breaks

Seams

Where this meets the rest of the build. Each one leads with what you will actually see.

mosh meets the locale

Symptommosh-server needs a UTF-8 native locale to run

This is the most common mosh failure. Generate and select en_US.UTF-8 with dpkg-reconfigure locales, then reconnect.

Ghostty meets a fresh box

Symptomerror opening terminal: xterm-ghostty

The box doesn't have Ghostty's terminfo entry. For this session, export TERM=xterm-256color. To fix it for good, run infocmp -x xterm-ghostty | ssh youruser@devbox -- tic -x - from your own machine, then reconnect.

Avahi meets the firewall

Symptomdevbox.local stops resolving once UFW is on

mDNS uses UDP 5353, which the firewall blocks. Allow it with sudo ufw allow 5353/udp, or use the MagicDNS name. .local only ever works on the same network.

sshd meets your only session

Symptoma fresh login fails, and the session that worked is closed

Keep your current session open after restarting ssh. Test a new login in a second terminal before closing the first.

Verify

Checks
sudo sshd -T | grep -E 'passwordauthentication|permitrootlogin'   # both no
locale | head -1                                                   # LANG=en_US.UTF-8
systemctl is-enabled unattended-upgrades                           # enabled
Corrections and updates
  1. 17 Oct 2026First written. Verified on Debian 13.6.