█
LastWrite
  • > Curriculum
  • > Pricing
  • > For Educators
  • > About
  • > Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
LastWrite

Structured computer science lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 LastWrite. All rights reserved.
Curriculum/DevOps and Infrastructure/Linux and the Command Line/Passion Project: Hardened VPS Deploy
180 minIntermediate

Passion Project: Hardened VPS Deploy

After this lesson, you will be able to: Set up a fresh Ubuntu VPS, secure it (SSH keys, no root, UFW firewall), deploy a real web app to it manually, then automate the deploy with a bash script.

This is the passion project for the Linux sub-track per Curriculum-Upgrade.md. Spend ~$5 (DigitalOcean / Hetzner / Vultr / Linode all work) and a Saturday afternoon. The result: a public URL with your name on it that proves you can run a Linux server.

Prerequisites:vim Basics

Milestone 1 — Spin up the VPS

Cheapest provider that fits is fine. DigitalOcean is the canonical learning choice.

  1. 1

    Sign up at digitalocean.com (or hetzner.com, vultr.com, linode.com)

  2. 2

    Create a $5/mo droplet: Ubuntu 22.04 LTS, smallest size, region near you

  3. 3

    When prompted for authentication: paste your SSH public key (cat ~/.ssh/id_ed25519.pub). DO NOT pick password.

  4. 4

    Note the public IP

  5. 5

    From your laptop: ssh root@<ip>

  6. 6

    If that works, you have a server. If not, double-check the SSH key.

Milestone 2 — Harden the server

Every box that touches the internet should pass this list.

  1. 1

    Create a non-root user: adduser deploy ; usermod -aG sudo deploy

  2. 2

    Copy your SSH key to the deploy user: mkdir /home/deploy/.ssh ; cp /root/.ssh/authorized_keys /home/deploy/.ssh/ ; chown -R deploy:deploy /home/deploy/.ssh ; chmod 700 /home/deploy/.ssh ; chmod 600 /home/deploy/.ssh/authorized_keys

  3. 3

    From your laptop: ssh deploy@<ip>, must work before continuing

  4. 4

    Edit /etc/ssh/sshd_config: PermitRootLogin no, PasswordAuthentication no, save, sudo systemctl restart sshd

  5. 5

    VERIFY in a parallel terminal that deploy@<ip> still works BEFORE closing your other session

  6. 6

    Install UFW firewall: sudo apt install ufw ; sudo ufw allow 22 ; sudo ufw allow 80 ; sudo ufw allow 443 ; sudo ufw enable

  7. 7

    Install fail2ban: sudo apt install fail2ban (default config bans IPs after 5 SSH failures)

  8. 8

    Enable unattended-upgrades: sudo apt install unattended-upgrades ; sudo dpkg-reconfigure -plow unattended-upgrades

Milestone 3 — Deploy a web app (manually first)

Pick any app you've built. Or use a 'Hello World' Express / Flask app to start.

  1. 1

    On the VPS as deploy: sudo apt install nginx nodejs npm git

  2. 2

    Clone your repo: git clone https://github.com/<you>/<repo>.git /srv/myapp

  3. 3

    cd /srv/myapp ; npm ci ; npm run build (if applicable)

  4. 4

    Create a systemd unit (see do-linux-04 lesson) at /etc/systemd/system/myapp.service

  5. 5

    sudo systemctl daemon-reload ; sudo systemctl enable --now myapp

  6. 6

    Configure nginx as a reverse proxy: copy a sample config to /etc/nginx/sites-available/myapp; symlink to sites-enabled; reload nginx

  7. 7

    Point a domain (or use the IP directly) at the box

  8. 8

    Use certbot to install a free Let's Encrypt cert: sudo apt install certbot python3-certbot-nginx ; sudo certbot --nginx -d yourdomain.com

Milestone 4 — The deploy script

Replace manual deploy with a one-command script. Put it in /usr/local/bin/deploy.sh.

bash
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
APP_DIR=/srv/myapp
LOG=/var/log/myapp/deploy.log
log() { echo "[$(date '+%F %T')] $*" | tee -a "$LOG"; }
log 'Starting deploy'
cd "$APP_DIR"
log 'Pulling latest'
git fetch --all --quiet
git reset --hard origin/main
log 'Installing deps'
npm ci --omit=dev
log 'Building'
npm run build
log 'Restarting service'
sudo systemctl restart myapp
log 'Verifying'
sleep 3
if curl -sf http://localhost/health > /dev/null; then
log 'Deploy OK'
else
log 'HEALTH CHECK FAILED, rolling back'
git reset --hard HEAD~1
sudo systemctl restart myapp
exit 1
fi

💡 How to talk about this in an interview

Walk the interviewer through the architecture: nginx → systemd service → Node app + DB. Show the deploy script and explain the health check + rollback. Show the hardened sshd_config and UFW rules. Mention the unattended-upgrades + fail2ban for ongoing security. Ten minutes of demo proves you can run a production-shape Linux box.

Common mistakes only candidates with offers avoid

Skipping the deploy script, manual deploys don't impress. Forgetting HTTPS. Free Let's Encrypt is 10 lines; no excuse. Storing secrets in the deploy script. Put them in /etc/myapp/env (root:deploy 640). Leaving the IP exposed without UFW + fail2ban. Skipping the health check + rollback. The script that catches its own failure is the script that proves you've done this before.

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←vim Basics
Back to Linux and the Command Line
Linux + CLI Job Readiness→