What is this?

This portfolio itself. A statically generated website built with Astro that showcases my projects and blog posts. No CMS, no framework overhead — just MDX files compiled to HTML at build time.

The site runs on a Hetzner VPS behind Nginx and WireGuard VPN , with automated deployment via GitHub Actions . During development, it is only accessible via VPN.

Architecture

  • Static Site Generation with Astro 5 — all pages are generated at build time. No Node.js process on the server, no database access, minimal attack surface.
  • Content Collections for blog and projects — type-safe frontmatter with Zod validation, MDX for interactive components like glossary references.
  • Glossary system — centralized definitions in TypeScript, referenced via the <Term> component in every article. Desktop: sidebar, mobile: dialog.
  • Floating toolbar — theme toggle (dark/light), accent color picker (HSL ring), glossary button. Expands on click, closes on click outside.
  • QR code generator — interactive tool at /qr-generator, running entirely client-side.

Infrastructure

Two Hetzner servers connected via a private network (10.0.0.0/16):

  • Portfolio server — Nginx as Reverse Proxy , Let’s Encrypt for TLS, serves static files from /var/www/mathis-adler.dev/dist/
  • WireGuard server — VPN endpoint for split-tunnel access, dnsmasq for DNS overrides within the VPN

The separation into two servers isolates the VPN service from the web server. Clients on the VPN reach services via internal IPs, while public access is controlled by Nginx.

Deployment

Push to main triggers a GitHub Actions workflow:

  1. npm ci && npm run build — Astro compiles all MDX files to static HTML
  2. rsync --delete dist/ — only changed files are transferred
  3. Nginx serves the new files immediately — no restart needed

What I Learned

  • Astro’s Content Collections with Zod validation catch frontmatter errors at build time — no “undefined” in production.
  • MDX allows Astro components in Markdown. The glossary system would be significantly more cumbersome without <Term> as an MDX component.
  • A static portfolio has virtually no attack surface. The security work is in the infrastructure (Nginx hardening, VPN, SSH), not in the application code.

Glossary

Static Site Generation (SSG)
A build process where all HTML pages are generated at build time — not on each request. The web server delivers pre-built files without a database or server-side logic. Result: fast load times, minimal attack surface, and simple hosting.
CI/CD
Continuous Integration and Continuous Deployment — automated processes that test, build, and deploy code after every push. Reduces manual errors and enables fast, reproducible deployments.
WireGuard
A modern VPN protocol focused on minimal code and strong cryptography. Compared to OpenVPN or IPSec, it's significantly leaner (~4,000 lines of kernel code), faster to connect, and easier to configure.
Reverse Proxy
A server that receives incoming requests and forwards them to backend services. Handles tasks like SSL termination, rate limiting, and access control, so the actual applications don't need to be directly accessible from the internet.
Let's Encrypt
A free, automated Certificate Authority (CA) that issues TLS certificates. Certificates are requested via the ACME protocol and automatically renewed every 90 days — no manual effort for HTTPS.
Content Security Policy
An HTTP header that tells the browser which resources a page is allowed to load. script-src 'self' allows only scripts from the same domain, blocking injected code (XSS).