Files
bytegeist-portfolio/index.html
T

237 lines
9.7 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="ByteGeist is Keith Casko's self-hosted infrastructure lab and engineering case study." />
<title>ByteGeist — Self-hosted infrastructure</title>
<link rel="icon" href="assets/images/bytegeist-logo.png" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<header class="site-header">
<a class="brand" href="#top" aria-label="ByteGeist home">
<img src="assets/images/bytegeist-logo.png" alt="" />
<span>ByteGeist</span>
</a>
<nav aria-label="Primary navigation">
<a href="#architecture">Architecture</a>
<a href="#systems">Systems</a>
<a href="#lessons">Lessons</a>
</nav>
<a class="text-link header-link" href="https://wiki.casko.dev">Read the docs <span>↗</span></a>
</header>
<main id="top">
<section class="hero">
<div class="hero-copy">
<p class="kicker"><span></span> Infrastructure lab / ongoing project</p>
<h1>A production-minded cloud, running on Hetzner.</h1>
<p class="hero-intro">
ByteGeist is where I test infrastructure decisions for real: identity, observability,
deployments, security, and recovery. It currently runs 26 Docker containers on Ubuntu at scale.
</p>
<div class="actions">
<a href="#architecture" class="button primary">See how it works</a>
<a href="https://git.casko.dev" class="text-link">Browse the Git server <span>↗</span></a>
</div>
</div>
<figure class="hero-visual">
<img src="assets/images/portfolio-grafana-operations.png" alt="ByteGeist operations dashboard in Grafana" />
<figcaption>
<span>Live operations view</span>
Grafana / Prometheus / cAdvisor
</figcaption>
</figure>
</section>
<section class="project-facts" aria-label="Project facts">
<div><span>01</span><strong>26 containers</strong><small>deployed with Compose</small></div>
<div><span>02</span><strong>Central identity</strong><small>SSO and 2FA via Authentik</small></div>
<div><span>03</span><strong>Full observability</strong><small>metrics, logs, and uptime</small></div>
<div><span>04</span><strong>Documented recovery</strong><small>tested restore procedures</small></div>
</section>
<section id="architecture" class="architecture section-rule">
<div class="section-heading">
<p class="section-index">01 / Architecture</p>
<div>
<h2>One server, treated like a platform.</h2>
<p>
Public traffic terminates at Nginx Proxy Manager. Authentik handles identity before
requests reach protected services. Metrics, logs, CI, and backups remain separate
concerns, so each can fail or change without taking the whole stack with it.
</p>
</div>
</div>
<figure class="architecture-figure">
<img src="assets/images/bytegeist-architecture.png" alt="Detailed ByteGeist infrastructure architecture diagram" />
<figcaption>Current architecture — updated as services are added or retired.</figcaption>
</figure>
<div class="principles">
<article>
<span>Access</span>
<h3>Authenticate once</h3>
<p>OIDC where supported; forward auth for services that do not speak it natively.</p>
</article>
<article>
<span>Operations</span>
<h3>Make failures visible</h3>
<p>Host and container metrics, centralized logs, and independent uptime checks.</p>
</article>
<article>
<span>Recovery</span>
<h3>Assume the host is replaceable</h3>
<p>Configuration lives in Git. Persistent data is backed up with rebuild notes.</p>
</article>
</div>
</section>
<section id="systems" class="systems section-rule">
<div class="section-heading compact">
<p class="section-index">02 / Selected systems</p>
<div>
<h2>The parts I spend time on.</h2>
<p>Not a catalog of logos—a record of what is running, why it exists, and what I learned building it.</p>
</div>
</div>
<article class="system-story">
<div class="story-copy">
<p class="story-number">01</p>
<p class="story-label">Observability</p>
<h3>Useful signals before pretty dashboards.</h3>
<p>
Prometheus collects host and container metrics while Loki receives logs through
Grafana Alloy. The dashboards answer practical questions: what is down, what changed,
and which container is consuming the host.
</p>
<ul>
<li>Grafana</li><li>Prometheus</li><li>Loki</li><li>Alloy</li>
</ul>
</div>
<div class="story-images">
<img src="assets/images/portfolio-grafana-operations.png" alt="Grafana infrastructure metrics dashboard" />
<img src="assets/images/portfolio-grafana-logs.png" alt="Centralized container logs in Grafana" />
</div>
</article>
<article class="system-story reverse">
<div class="story-copy">
<p class="story-number">02</p>
<p class="story-label">Identity & security</p>
<h3>One identity layer, fewer exposed edges.</h3>
<p>
Authentik provides SSO, OIDC, forward auth, and 2FA across eleven applications.
CrowdSec watches hostile traffic and creates firewall decisions alongside key-only SSH,
UFW, and TLS.
</p>
<ul>
<li>Authentik</li><li>CrowdSec</li><li>UFW</li><li>Let's Encrypt</li>
</ul>
</div>
<div class="story-images">
<img src="assets/images/portfolio-authentik-applications.png" alt="Applications protected by Authentik" />
<img src="assets/images/portfolio-crowdsec-decisions.png" alt="CrowdSec security decisions" />
</div>
</article>
<article class="system-story">
<div class="story-copy">
<p class="story-number">03</p>
<p class="story-label">Development</p>
<h3>Code and delivery stay in-house.</h3>
<p>
Gitea stores infrastructure code and documentation. Woodpecker runs repository-linked
pipelines, including the build and deployment path for this site.
</p>
<ul>
<li>Gitea</li><li>Woodpecker CI</li><li>Docker</li><li>Git</li>
</ul>
</div>
<div class="story-images">
<img src="assets/images/portfolio-gitea-dashboard.png" alt="Self-hosted Gitea dashboard" />
<img src="assets/images/portfolio-woodpecker-pipeline.png" alt="Successful Woodpecker CI pipeline" />
</div>
</article>
<article class="system-story reverse">
<div class="story-copy">
<p class="story-number">04</p>
<p class="story-label">Platform & recovery</p>
<h3>The boring work is part of the system.</h3>
<p>
Dashy keeps service access legible. The wiki records inventory, DNS, restore order,
backup validation, and the steps required to rebuild the environment from a clean host.
</p>
<ul>
<li>Dashy</li><li>Wiki.js</li><li>Restic</li><li>Runbooks</li>
</ul>
</div>
<div class="story-images">
<img src="assets/images/portfolio-cloud-dashboard-overview.png" alt="ByteGeist internal service portal" />
<img src="assets/images/portfolio-wiki-disaster-recovery.png" alt="ByteGeist disaster recovery runbook" />
</div>
</article>
</section>
<section id="lessons" class="lessons section-rule">
<div class="section-heading compact">
<p class="section-index">03 / Lessons</p>
<div>
<h2>What actually took work.</h2>
</div>
</div>
<div class="lesson-list">
<article>
<span>01</span>
<h3>OIDC is mostly careful bookkeeping.</h3>
<p>Redirect URIs, claims, group mapping, and user provisioning all need to agree across both ends.</p>
</article>
<article>
<span>02</span>
<h3>Backups are unfinished until restore is documented.</h3>
<p>I track data location, dependencies, restore order, DNS, certificates, and validation—not just archive jobs.</p>
</article>
<article>
<span>03</span>
<h3>Observability has to support a decision.</h3>
<p>A smaller dashboard that explains an incident is more useful than a wall of metrics nobody checks.</p>
</article>
<article>
<span>04</span>
<h3>Convenience can weaken the boundary.</h3>
<p>Every exposed service has a cost. SSO, least privilege, and fewer public endpoints keep that cost explicit.</p>
</article>
</div>
</section>
<section class="closing">
<p class="section-index">Project notes</p>
<h2>This environment is still changing.</h2>
<p>
I use ByteGeist to practice the parts of infrastructure work that tutorials tend to skip:
upgrades, failure modes, access policy, noisy logs, and recovery.
</p>
<a class="button primary" href="https://wiki.casko.dev">Open the documentation <span>↗</span></a>
</section>
</main>
<footer>
<a class="brand" href="#top">
<img src="assets/images/bytegeist-logo.png" alt="" />
<span>ByteGeist</span>
</a>
<p>An infrastructure lab by Keith Casko, running on Hetzner.</p>
<p>Built with plain HTML and CSS.</p>
</footer>
</body>
</html>