Automated Server Setup
One-command Ubuntu/Debian host bootstrap—restricted service user, key-only SSH, minimal firewall, nginx, hardening drop-ins, and a pass/fail self-check report.
StackBash · Ubuntu · OpenSSH · ufw · nginx · Multipass · Docker/Colima
SignalsIdempotent Bash bootstrap · SSH harden · ufw · nginx · self-check
Requirements outline
Scope this page covers. Each row is an acceptance item the automation targets; delivery is what ships in this revision.
| ID | Requirement | Delivery |
|---|---|---|
| FR1 | Restricted non-root service user | appsvc system account, /var/lib/appsvc (0750), shell nologin, password locked |
| FR2 | SSH hardened (keys only) | sshd drop-in — no password auth, no root login, PubkeyAuthentication yes, AllowUsers admin |
| FR3 | Minimal host firewall | ufw default deny incoming; allow OpenSSH + 80/tcp only |
| FR4 | Real service installed and running | nginx enabled under systemd; static site on port 80 |
| FR5 | Basic system hardening | unattended-upgrades, sysctl drop-in, SSH banner, PermitEmptyPasswords no, sudo membership hygiene |
| FR6 | Automation self-check | PASS/FAIL report per FR; non-zero exit if any check fails |
Evidence




Terminal captures from the lab host. Click an image to expand fullscreen.
Problem
I wanted a single, re-runnable automation for a clean Ubuntu/Debian box: bring the host from a minimal install to a known baseline without mid-run manual steps, and finish with explicit verification rather than treating a quiet command exit as success. The failure modes I designed around include SSH lockout (password auth off without keys) and firewall order (default deny before an SSH allow rule).
Approach
setup.sh sources modular stages (lib/01–06): system user appsvc with nologin and locked password; sshd drop-in (PasswordAuthentication no, PermitRootLogin no, AllowUsers admin); ufw default deny with OpenSSH + port 80 only; nginx serving a static proof page; unattended-upgrades and sysctl hardening; then a self-check that writes PASS/FAIL lines to /var/log/automated-server-setup-selfcheck.txt and exits non-zero on any fail. File writes are cmp-idempotent; packages install only when missing. Validated on Multipass Ubuntu 24.04 (systemd) twice, plus a Docker/Colima privileged smoke run.
Outcomes
- Self-check ALL CHECKS PASSED on Multipass Ubuntu 24.04 (ass-lab), including systemd nginx
- Second run on the same VM also ALL CHECKS PASSED (idempotent path exercised)
- Independent probes recorded (sshd -T, ufw status, getent appsvc, curl localhost)
- Docker/Colima smoke path green for packages/config (no full systemd claim there)
- Mitigations encoded (keys before password-off; allow SSH before ufw enable; sshd -t before reload)