Daniel Meier

Infrastructure, Linux, automation, and operational engineering

Onion Lab: Serve from Home and Route Outbound Traffic Through Tor

I think Tor is incredibly cool for a very practical reason: I can make a web service available from a machine at home without configuring port forwarding on my router and without registering a domain anywhere.

Run the service locally, let Tor create an Onion Service, and share the .onion address. Someone using Tor Browser can reach it even though I have not exposed an HTTP port to the internet. That is a fascinating capability to fit into such a small setup.

I built Onion Lab to make that idea easy to try with Docker Compose. It publishes a small website and an SSH service through Tor, with no web or SSH ports published on the Docker host.

Then I wanted to push the demo further. What if the applications also had to send their outgoing traffic back through the Tor network, even when they knew nothing about proxies?

That became the second half of the lab: Onion Services for incoming connections, transparent Tor routing for outgoing application TCP connections, and everything defined in a small Compose stack.

Read more

I Run Kubernetes at Work. I Still Use Docker Compose at Home.

I run Kubernetes professionally. I still deliberately use Docker Compose at home.

That can sound like a contradiction. If Kubernetes is the right platform for serious production systems, why would I not use it for my own infrastructure? If I already know how to operate it, surely the learning curve is no longer an argument.

The answer is that knowing how to run a complex platform is not the same as needing one.

Kubernetes is excellent. It solves difficult scheduling, availability, reconciliation, policy, isolation, and organizational problems through a consistent API. I value those properties at work because the requirements justify the machinery.

My homelab has different requirements. I want reliable containers, reproducible configuration, automated deployment, controlled updates, centralized identity, sensible storage, and a system I can still understand when something fails on a quiet Sunday evening.

Docker Compose, Git, CI, and Renovate already give me most of the operational properties I care about there. Adding Kubernetes would give me more capability, but it would also give me more platform to own. Capability is useful only when it answers a requirement.

Read more

Git Worktrees Keep Parallel Tasks in Their Own Directories

Git branches isolate history, but a normal clone gives us only one working directory.

That mismatch creates friction as soon as more than one task matters. I may have an unfinished feature in my editor when a production hotfix arrives. I may want to run a pull request beside main, compare two implementations, or let separate automation processes work on independent branches. With one directory, every interruption begins by changing the state underneath my tools.

The usual responses are stashing, temporary commits, repeated branch switching, or cloning the repository again. They work, but they make context switching more expensive than it needs to be.

Git worktrees are one of my favorite Git features because they solve the problem at the correct level. Instead of repeatedly changing which branch one directory represents, I give each active branch its own directory.

Read more

Give Your Repository a Front Door with Make

Every repository develops a collection of commands.

There is a command to install dependencies, another to run tests, one for formatting, one for building an artifact, and several more for local development, generated code, documentation, containers, or deployment validation. Without a common interface, those commands spread across README files, CI YAML, shell history, package-manager scripts, and team memory.

I recommend putting a Makefile in almost every repository.

Not because every project compiles C, and not because GNU Make is a modern workflow language. I recommend it because make provides a small, familiar, versioned entry point for repository operations. A developer can run make, discover the available workflows, and use the same targets that CI uses.

A good repository Makefile is not a second application hidden beside the real one. It is a thin, documented interface over commands the project already trusts.

Read more

Pin Project Tools with asdf and .tool-versions

Every software repository depends on tools.

Even a small project may require one particular version of Go, Node.js, Python, or another runtime. Larger repositories often add Terraform, kubectl, documentation generators, and code-generation tools. Without an explicit mechanism, developers install whatever their operating system provides, CI uses a different version, and the production build quietly depends on another one again.

These differences often remain invisible until a command changes behavior, a formatter rewrites files differently, or an upgrade works on one machine and fails everywhere else.

asdf is the tool version manager I recommend for this problem. It gives a repository one version-selection interface backed by one small file: .tool-versions. The file can be committed to Git, reviewed like any other dependency change, and used by developers and CI to install the same declared versions.

asdf does not make an environment perfectly reproducible, and it is not a replacement for a package manager or a container image. It solves a narrower problem extremely well: selecting and installing the versions of project tools that should be active in a directory.

Read more