← All labs

F06Lab 01 · November 2026

How this site runs

m-ashraf.com is hand-written HTML and CSS, with no framework and no database, served from a Linux server I run myself. The same server hosts my other projects, each one walled off from the rest.

Server
Oracle Cloud ARM64 VM · 4 cores · 24 GB RAM · Ubuntu 24.04 LTS
Front door
Caddy: automatic HTTPS certificates and routing for every app
Isolation
One Linux user, folder and set of systemd services per app
Backups
Nightly snapshots of every app's data, kept for 14 days

The path of a request

  1. DNSm-ashraf.com points at the server. A wildcard record sends every subdomain there too, so a new app never needs a DNS change.
  2. FirewallOnly SSH, HTTP and HTTPS are reachable from outside; everything else is rejected. SSH accepts keys only, with password login switched off.
  3. One door inCaddy is the only program listening on the internet. It gets and renews HTTPS certificates by itself, redirects HTTP to HTTPS, and routes each address to the right app.
  4. The appFor this site, Caddy serves the files directly. Every other app listens on a private port that can't be reached from outside, and Caddy forwards to it.
How a request reaches this site A visitor looks up m-ashraf.com and connects to the server. The firewall only lets SSH, HTTP and HTTPS through. Caddy handles HTTPS and either serves this site's files directly or forwards to an app on a private port. Each app runs under its own Linux user, and every app's data is snapshotted nightly. Visitor DNS: m-ashraf.com Firewall only 22 · 80 · 443 open Caddy automatic HTTPS · one door in This site static files no app, no port App own Linux user private port App own Linux user private port Nightly snapshots every app's data · 14 days
One public entry point; everything else stays private.

Every app in its own box

The rule on this server: everything an app needs lives in its own folder, and adding, changing or removing one app never touches another.

  • Its own Linux user, so one compromised app can't read another's files or keys.
  • Code it can only read, and one data folder it's allowed to write to.
  • Its own Python environment and secrets file, readable by that app alone.
  • Its own systemd services, restarted automatically if they crash.
  • Its own small Caddy config. The main config only imports them, so it never needs editing.

Security updates install automatically, and a nightly job snapshots every app's data and keeps 14 days of them.

This site

  • Plain HTML, CSS and a little JavaScript. No trackers and no third-party requests: even the fonts are served from here.
  • Strict security headers: a Content-Security-Policy that only allows this site's own files, HSTS, and no framing by other sites.
  • Light and dark themes, readable on a phone, and it still works with JavaScript turned off.

Deploying is one command

From my PC, sh deploy/deploy.sh runs four steps:

  1. CheckEvery internal link and image must resolve, and nothing that looks like a key, a token or a phone number is allowed to ship.
  2. UploadThe site goes to the server as one archive over SSH.
  3. SwapThe new version replaces the old one in a single move, so visitors never see half an update.
  4. VerifyThe script fetches the live page and confirms it answers.

What I'd add next

  • Uptime monitoring that alerts me when something stops answering.
  • IPv6.
  • Privacy-friendly visitor numbers from Caddy's own logs, without cookies or trackers.