← Back to Projects

PROJECT 01 / HOMELAB

Odysseus

My self-hosted Debian server, built as a practical environment for learning Linux, networking, Docker, monitoring, automation and security.

Debian Docker Caddy Cloudflare WireGuard Git Bash

Why I built it

I wanted somewhere I could work with infrastructure myself instead of only learning concepts in theory.

Odysseus started as a Debian installation on a mini PC and gradually became the main environment I use for experimenting with Linux, Docker, networking and self-hosted services.

I have tried to add services when they give me something useful or give me a reason to learn a new part of the system, rather than just installing applications for the sake of it.

The server itself

Odysseus runs Debian Linux directly on the mini PC. Docker is used for most application services, while Debian handles the underlying host, networking, storage, services and access to the machine.

Debian

The base operating system. I use it to manage the filesystem, users, permissions, networking, system services and the Docker runtime.

Docker

Most applications run in containers. Docker Compose keeps each service configuration separate and makes deployments easier to reproduce and maintain.

Persistent storage

Applications that need to retain data use Docker volumes or bind-mounted directories so that recreating a container does not remove its data.

Docker networking

Containers communicate over defined Docker networks rather than exposing every application directly to the host or the internet.

How traffic reaches the server

I treat the public portfolio differently from the services I use to administer the homelab.

Public portfolio

Visitors connect to Cloudflare first. Cloudflare Tunnel then carries the request back to the cloudflared container running on Odysseus.

Caddy receives the request and serves the static portfolio files.

Browser
  -> Cloudflare
  -> Cloudflare Tunnel
  -> cloudflared
  -> Caddy
  -> /srv/www/portfolio

Private services

Administrative services are not published in the same way as the portfolio.

My Homepage dashboard and management interfaces are accessed through trusted network connections or WireGuard.

This keeps the public-facing side separate from the services I use to administer Odysseus.

What runs on Odysseus

Caddy

Handles HTTPS and reverse proxying for web services and manages certificates for the domains I host.

Cloudflared

Maintains the Cloudflare Tunnel used by the public portfolio. The connection is established outbound from Odysseus, so I do not need to forward web ports from the router.

WireGuard

Provides VPN access when I need to reach private homelab services remotely.

Vaultwarden

My self-hosted password manager. Because it contains important data, I treat backups, authentication and updates more carefully than less critical services.

AdGuard Home

Provides DNS filtering for the network and has given me a practical reason to learn more about DNS and name resolution.

Homepage

A private dashboard used to access services and view basic container resource information.

Grafana

Used to visualise server and container metrics and understand how Odysseus behaves over time.

Uptime Kuma

Monitors availability of services and lets me see when something has become unavailable.

Diun

Watches the Docker images I use and sends notifications when updated images become available.

Security choices

Most of the security decisions around Odysseus have focused on reducing unnecessary exposure and keeping credentials and administration separate from public services.

No public web port forwarding

The public portfolio uses Cloudflare Tunnel rather than forwarding ports 80 or 443 from my router directly to the server.

Public and private separation

The portfolio is intentionally public, while Homepage and other administrative interfaces remain accessible only from trusted network or VPN connections.

Read-only website files

Caddy receives the deployed portfolio through a read-only mount of /srv/www/portfolio.

The live site is not served directly from the development Git repository.

Secrets outside Git

API tokens, private keys and environment files are kept out of the public repository.

I review changes before committing and pushing them to GitHub.

Repository-specific access

Odysseus uses a dedicated SSH deploy key for the portfolio repository instead of a credential with broad access to my GitHub account.

Browser security headers

Caddy adds security headers to the public site, including a restrictive Content Security Policy and protection against framing and content-type sniffing.

Managing changes with Git

The development copy of the portfolio lives in a Git repository on Odysseus.

Working copy

Changes are made under /home/beynz/projects/portfolio.

I use Git to review what has changed before creating a commit.

GitHub

Once a change is ready, I commit it and push the repository to GitHub.

This gives me a visible history of how the portfolio has developed rather than only keeping the finished version.

Publishing changes

GitHub stores the source and version history, but the live website itself is hosted on Odysseus.

Bash deployment

I use a Bash script to deploy the site instead of manually copying files into the production directory each time.

The script checks the repository state and creates a copy of the currently deployed version before making changes.

Controlled deployment

Only files intended to be public are copied into /srv/www/portfolio.

Once deployment finishes, the script checks the live site. If that validation fails, the previous version can be restored.

Things that didn't work first time

Some of the most useful parts of the build came from having to work out why something wasn't behaving as expected.

Cloudflare 522

My first attempt at publishing the portfolio resulted in a Cloudflare 522 timeout.

The browser could reach Cloudflare, but Cloudflare could not establish the connection back to Caddy.

Instead of opening HTTPS on the router, I changed the design to use Cloudflare Tunnel.

Caddy certificate failure

Caddy initially attempted the normal HTTP and TLS certificate challenges for the new site and failed.

The Caddy logs showed that the new site was missing the Cloudflare DNS challenge configuration already used elsewhere in the homelab.

Once that configuration was added, certificate issuance worked correctly.

Container updates

I initially handled container updates without much of a standard process.

I now check service state first, keep rollback options where appropriate, pull the new image and verify the service after recreation.

Development vs production

The portfolio originally existed only in the Git working directory.

I moved the production version into /srv/www/portfolio so Caddy does not expose Git metadata or development files.

What I've taken from the project

The main benefit of building Odysseus has been seeing how systems that I previously thought about separately actually interact.

DNS determines how a request finds a service. Cloudflare sits in front of the public site. The tunnel brings traffic back to Odysseus. Caddy handles the web request, Docker networking connects the containers, and Linux underneath everything provides the host, networking, processes and storage.

Troubleshooting the environment has also made me more comfortable checking logs and working backwards through a problem instead of immediately changing configuration.

My next step is to build a disposable Linux environment where I can deliberately break and troubleshoot these components without affecting the services running on Odysseus.