The whole idea

One VM, five tools you already know, and a small program that connects them.

Here is Yard in one sentence: one Linux VM runs every app in a Podman container behind Caddy, and a push to the repo deploys the app.

That's it. The rest of this book explains that sentence. If you already know Podman, Compose, Caddy, and webhooks, you could probably build Yard yourself from that line. I did, and it came out small. This page tells you how the parts fit, so nothing later comes as a surprise.

The picture#

                 git push
                    │
          ┌─────────▼─────────┐
          │  GitHub / Origin  │   the forge: where the code lives
          └─────────┬─────────┘
                    │ webhook: "something moved"
                    ▼
 ┌──────────────── one VM ─────────────────────────────┐
 │                                                     │
 │   :443 ──► Caddy ──┬──► yard-app-1     (Yard)       │
 │            (TLS)   ├──► heldso-app-1   (an app)     │
 │                    └──► honorata-app-1 (an app)     │
 │                                                     │
 │   all containers: rootless Podman, network "yard"   │
 │   all logs: journald                                │
 │   all state: files in /srv/yard                     │
 └─────────────────────────────────────────────────────┘

Read it top to bottom. You push. The forge tells Yard about it. Yard builds a new container and swaps it in. Caddy keeps sending traffic to the container by name, so visitors never notice the swap.

Each tool does one job#

Yard does very little by itself. It borrows each hard problem from a tool that already solves it well.

Podman runs the containers. It runs rootless, as a plain yard user, so a container that escapes lands in an account that owns nothing. Every app gets one container (or a few) on one shared network called yard.

Compose describes each app. An app is a Compose project. If your repo has a compose.yml, Yard runs it. If the repo has only a Containerfile, Yard brings a small default compose file. So the format you already write for your laptop is the format the server runs.

Caddy handles the internet. It holds port 443, gets the TLS certificates by itself, and proxies each domain to a container name like heldso-app-1:8000. Yard talks to Caddy through its admin API. No Caddyfile is involved.

The forge holds the code. GitHub or Cursor Origin. Yard connects to it as an installed app, so it can read private repos and receive push webhooks. Yard never hosts git.

journald keeps the record. Container output, Podman events, and Yard's own events all go to the system journal. A log outlives the container that wrote it.

Yard is the glue. It's one Python program (a Stario app) that receives the webhooks, decides when to deploy, starts the deploy, writes the Caddy routes, and answers the yard command on your laptop.

What one deploy is#

A deploy is four steps, and each step is something you could type yourself:

  1. git fetch the repo at the new commit.

  2. podman build the image, tagged with the commit: localhost/heldso:9c2c7e1a2b3c.

  3. docker-compose up --wait to replace the old container with the new one.

  4. If the new container never gets healthy, start the old image again.

Yard runs these steps in a throwaway container, so the build never happens inside Yard's own process. One push, start to finish follows a real push through them.

What Yard leaves out#

The short list of things Yard does not have is part of the design:

  • No registry. Images are built on the VM and stay on the VM.

  • No CI service. The build is the Containerfile. Run your tests wherever you like before you push.

  • No database. Yard keeps its state in plain files under /srv/yard. You can read them with cat.

  • No dashboard. The yard command prints tables, and every table is also JSON from an HTTP API.

  • No cluster. One VM. When it gets full, get a bigger VM.

One VM is a real limit. Yard is for people who want ten small apps online for the price of one machine, and want to understand every moving part. It's not trying to be Kubernetes, and it's glad about that.

The contract with an app#

An app only has to do four things to live on Yard:

  • Ship a Containerfile (or Dockerfile), or a compose.yml.

  • Listen for HTTP on port 8000.

  • Read its settings from environment variables.

  • Keep anything it must not lose in /data.

Yard was built for Stario apps, but it never looks inside the image. A Go binary, a Node server, or nginx with a static site deploys the same way.

By the way#

  • The command is yard. The package on PyPI is staryard, because someone else got yard first.

  • Yard deploys itself. The yard app is an app like any other, so a push to the Yard repo upgrades Yard. How it works shows how a program replaces itself without falling over.

  • The source is at github.com/Bobowski/yard. It's small enough to read in an afternoon.