281 lines
12 KiB
HTML
281 lines
12 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>
|
|
|
|
<div class="logo-divider" aria-hidden="true">
|
|
<img src="assets/images/bytegeist-logo.png" alt="" />
|
|
</div>
|
|
|
|
<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">
|
|
<figure class="screenshot-exhibit">
|
|
<figcaption><span>Infrastructure monitoring</span><small>Grafana operations dashboard</small></figcaption>
|
|
<a href="assets/images/portfolio-grafana-operations.png" target="_blank">
|
|
<img src="assets/images/portfolio-grafana-operations.png" alt="Grafana infrastructure metrics dashboard" />
|
|
</a>
|
|
</figure>
|
|
<figure class="screenshot-exhibit">
|
|
<figcaption><span>Centralized logging</span><small>Container logs collected in Loki</small></figcaption>
|
|
<a href="assets/images/portfolio-grafana-logs.png" target="_blank">
|
|
<img src="assets/images/portfolio-grafana-logs.png" alt="Centralized container logs in Grafana" />
|
|
</a>
|
|
</figure>
|
|
</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">
|
|
<figure class="screenshot-exhibit">
|
|
<figcaption><span>Identity portal</span><small>Applications protected by Authentik</small></figcaption>
|
|
<a href="assets/images/portfolio-authentik-applications.png" target="_blank">
|
|
<img src="assets/images/portfolio-authentik-applications.png" alt="Applications protected by Authentik" />
|
|
</a>
|
|
</figure>
|
|
<figure class="screenshot-exhibit">
|
|
<figcaption><span>Active firewall decisions</span><small>CrowdSec command-line output</small></figcaption>
|
|
<a href="assets/images/portfolio-crowdsec-decisions.png" target="_blank">
|
|
<img class="crowdsec-capture" src="assets/images/portfolio-crowdsec-decisions.png" alt="CrowdSec security decisions" />
|
|
</a>
|
|
</figure>
|
|
</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">
|
|
<figure class="screenshot-exhibit">
|
|
<figcaption><span>Source control</span><small>Self-hosted repositories in Gitea</small></figcaption>
|
|
<a href="assets/images/portfolio-gitea-dashboard.png" target="_blank">
|
|
<img src="assets/images/portfolio-gitea-dashboard.png" alt="Self-hosted Gitea dashboard" />
|
|
</a>
|
|
</figure>
|
|
<figure class="screenshot-exhibit">
|
|
<figcaption><span>Continuous delivery</span><small>Successful Woodpecker pipeline</small></figcaption>
|
|
<a href="assets/images/portfolio-woodpecker-pipeline.png" target="_blank">
|
|
<img src="assets/images/portfolio-woodpecker-pipeline.png" alt="Successful Woodpecker CI pipeline" />
|
|
</a>
|
|
</figure>
|
|
</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">
|
|
<figure class="screenshot-exhibit">
|
|
<figcaption><span>Service portal</span><small>Internal tools organized in Dashy</small></figcaption>
|
|
<a href="assets/images/portfolio-cloud-dashboard-overview.png" target="_blank">
|
|
<img src="assets/images/portfolio-cloud-dashboard-overview.png" alt="ByteGeist internal service portal" />
|
|
</a>
|
|
</figure>
|
|
<figure class="screenshot-exhibit">
|
|
<figcaption><span>Recovery runbook</span><small>Documented rebuild and restore process</small></figcaption>
|
|
<a href="assets/images/portfolio-wiki-disaster-recovery.png" target="_blank">
|
|
<img src="assets/images/portfolio-wiki-disaster-recovery.png" alt="ByteGeist disaster recovery runbook" />
|
|
</a>
|
|
</figure>
|
|
</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>
|