Sooner or later, something goes wrong, and you want to know what happened. On many servers, the answer is scattered: a log file here, a container log there, and nothing at all from the container that crashed and was replaced an hour ago.
On a Yard host, the answer is in one place: the system journal. This chapter explains what goes into it, and how to read it from your laptop.
Logs and events are different things#
A log is what a program prints. Your app writes “listening on :8000” or a stack trace, and that line is a log line.
An event is something that happened to the host. A container started. A webhook arrived. A deploy began. A setting changed.
Logs answer “what did the app say?” Events answer “what happened, and when?” You need both, and Yard keeps both in the same journal.
Three streams, one journal#
The install script makes journald persistent, so the journal survives a reboot, and caps it at 2 GB. Then three streams flow in:
Container output. Podman’s log driver is journald. Every line a container prints goes to the journal, tagged with the container’s name.
Podman’s events. Podman’s event logger is journald too. Each container that is created, started, stopped, or removed leaves an entry.
Yard’s events. Yard writes its own events to journald’s native socket, tagged
SYSLOG_IDENTIFIER=yard, withYARD_*fields: webhooks, checks, app changes, setting changes, Caddy changes, tokens, forges, repos, and prunes.
The first stream is logs. The other two are events.
Why journald, and not log files or a log service? Because it is already on the machine, it indexes by container and time, and it rotates itself. And because the log lives outside the container, the log of a container that crashed, or that a deploy replaced, is still there to read.
Reading an app’s log#
Inside an app’s checkout, run:
yard app logsIt prints the last 100 lines of the app’s container. A few flags shape the window:
yard app logs -f # keep following new linesyard app logs --tail 500 # read more linesyard app logs --since -15m # from fifteen minutes agoyard app logs --since "2026-11-06 14:00" --until "2026-11-06 14:30"--since and --until take a relative time, such as -30s, -15m, -1h, or -2d, or a date with an optional time.
An app with more than one container picks one with --container, such as --container notes-worker-1. For any container on the host, by name or id, use the plain form:
yard logs yard-caddy-1yard logs deploy-notesReading a deploy#
yard app logs --deployThis prints the log of the app’s last deploy container, from the first == sync line to the outcome. It is the first command to run when a deploy fails. With -f during a deploy, it follows the deploy live, from its first line.
Filtering#
A busy app writes a lot. --hide and --only filter on the host, before the lines travel to your laptop. Each takes an expression, and each can repeat:
yard app logs --hide request.path=/healthyard app logs --only status=erroryard app logs --only "response.status_code>499"yard app logs --only timeoutAn expression of the form key=value, key!=value, key>n, or key<n reads a JSON log line. The key is a top-level field first, then a field under attributes. The value can use * as a wildcard. Anything else is plain text, matched anywhere in the line.
A Stario app writes one JSON line per span, and every span of a request shares a trace_id. The filters keep or drop all lines of one trace together. So --only status=error shows the whole request that failed, not one lonely line out of it.
One detail: --tail counts the lines read before the filter. --tail 100 --only status=error reads 100 lines and shows the errors among them.
Reading events#
yard eventsThis prints the last hour of the host’s history, oldest first, Podman’s events and Yard’s merged into one timeline:
time source event app container detail14:02:03 yard hook.received notes …14:02:03 yard check.moved notes notes commit moved14:02:04 yard deploy.start notes deploy-notes 9c2c7e1a2b3c …14:02:11 podman create notes notes-app-1 …14:02:12 podman start notes notes-app-1 …14:02:19 yard deploy.end notes deploy-notes ok 9c2c7e1a2b3c …Read it top to bottom, and the deploy tells itself. A few flags narrow it:
yard events --app notes # one appyard events --event deploy. # one kind of event, by prefixyard events --source yard # only Yard's eventsyard events --since -1d # the last dayyard events -f # follow, liveA read starts at --since and shows the first 200 rows after it. --lines changes the count. When there are more, the command prints the next command to run, with --after <cursor>. Run it to read the next page.
The deploy.start and deploy.end rows are built from Podman’s events for the deploy container and its labels, not from a record that Yard writes. Deploys explains why.
What events never hold#
No event holds a secret. A setting change records the key names, never the values. A repo token event names the repo, never the token. So an excerpt of yard events is safe to share. App logs are another matter: they hold whatever your app prints.
Scripts can read it too#
Everything above also has a JSON form. --json on yard logs gives the entries with a cursor. yard events -f --json streams one JSON object per line. So a script that watches the host, or a bot that tells you when a deploy fails, reads the same journal you do, through the same API.
By the way#
The 2 GB cap means old entries roll off. A quiet host keeps months. A host with a chatty app keeps less.
yard status --fullshows the journal’s size in itsdiskrow.On the host,
journalctlreads the same journal:sudo journalctl CONTAINER_NAME=notes-app-1 -f.Each command prints plain text, also in a pipe.
yard app logs | grep errorworks as you expect.