PROJECT 01 / HOMELAB
Odysseus
My self-hosted Debian server, built as a practical environment for learning Linux, networking, Docker, monitoring, automation and security.
01 / BACKGROUND
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.
02 / HOST
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.
03 / NETWORKING
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.
04 / SERVICES
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.
05 / SECURITY
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.
06 / VERSION CONTROL
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.
07 / DEPLOYMENT
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.
08 / TROUBLESHOOTING
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.
09 / WHAT I LEARNED
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.