Every app on Yard has an answer to one question: what runs? There are four possible answers. This chapter shows each one, how Yard picks the answer the first time, and how you change it later.
Four answers#
A Containerfile. Yard builds your code into an image. The Containerfile is in your repo, or Yard writes one for your language.
A compose file. Your own, in your repo, for an app with several containers, such as the app and a database. Your own compose file covers it.
An image. Something already built, from a public registry. No repo.
A recipe. A compose file at a URL, for a ready-made service. No repo.
The first two build from your repo. The last two run something that someone else built.
yard app status shows the answer on one line:
containerfile yardThe first deploy decides#
You do not have to say anything when you create an app from a repo. On the first deploy, Yard looks at the commit and decides, in this order:
A compose file:
compose.ymlorcompose.yaml, at the root or inops/.A
Containerfileor aDockerfile, at the root or inops/.A language that Yard knows. Yard writes a Containerfile for it.
Yard saves the answer on the app record. The output of that deploy, which yard app new and yard app deploy print, starts with what it chose:
== runs containerfile yard (stario: pyproject.toml declares stario)From then on, Yard keeps that answer. A later commit does not change it, even when the commit adds a compose file or a Containerfile. Changing what an app runs is your decision, in the same way that changing its settings is.
The languages Yard knows#
When a repo has no compose file and no Containerfile, Yard reads its files:
Stario:
pyproject.tomldeclaresstario, anduv.lockis committed. The Containerfile is the one thatstario-createwrites.Python:
pyproject.tomlwithuv.lock, or arequirements.txt. The image runsapp/main.py,main.py, orapp.py, the first one it finds.Go:
go.modand one main package, at the root or incmd/. The binary runs on a distroless image.Node:
package.jsonwith astartscript. The image installs with npm, runsnpm run buildwhen there is abuildscript, and starts withnpm start.
Yard does not guess. Two languages in one repo, a Go repo with several main packages, or a Node repo that uses pnpm stop the first deploy with a message that says what Yard found. Add a Containerfile, and deploy again.
Every Containerfile that Yard writes sets PORT=8000, and the app must listen on that port. The file has no healthcheck, because Yard cannot know where your app answers. Add a HEALTHCHECK when you take the file into your repo. Healthchecks explains why it matters.
Look at the file#
yard app cat containerfileyard app cat composecat prints the file that the app runs: the file in your repo at the deployed commit, the file that Yard wrote, or Yard's own compose file. A Containerfile that Yard wrote starts with a line that says where it came from:
# Written by Yard for a stario app (pyproject.toml declares stario). To change it, copy it into your repo: yard app cat containerfile --app my-app > ContainerfileChange it#
Two commands change the answer. Neither one deploys. Each one prints the command that does.
To use a file in your repo, name it:
yard app set --containerfile Containerfileyard app set --compose deploy/stack.ymlThe path can be any file in the repo. It does not need one of the standard names. If a later commit removes the file, that deploy fails and says so, and the running app stays up.
To use the Containerfile that Yard writes, even when the repo has its own, name yard:
yard app set --containerfile yardThe next deploy writes it for the repo's language, and later deploys keep it.
To let Yard decide again, as on the first deploy:
yard app detectmy-app: the next deploy decides what to run again (was: containerfile yard).Deploy now: yard app deploy --app my-appdetect is also how a written Containerfile gets newer. Yard keeps the file it wrote, even after a Yard upgrade brings a better template. The next deploy after detect writes the file again from the current template.
The written file is a starting point. When you want to change it, take it into your repo:
yard app cat containerfile > Containerfilegit add Containerfile && git commit -m "Own the Containerfile" && git pushyard app set --containerfile ContainerfileAn image#
An image app has no repo. It runs an image that a public registry already holds:
yard app new --image ghcr.io/you/api:1.4 --public api.example.comThe app's name comes from the image: api. Its container is api-app-1. It is on the yard network, with its own env file and /data, like any other app.
The tag decides how far the app follows:
postgres:17follows every 17.x release, and never moves to 18.postgres:17.2gets only the patch releases of 17.2.postgres:17.2.1stays on that release, plus any rebuild that the publisher pushes under the same tag.latestfollows everything, including the next major version.
A tag is required. Yard refuses postgres alone, so an app never follows latest by accident.
At every check, Yard asks the registry which image the tag points to. When the tag moves, a push app deploys the new image, exactly as it deploys a new commit. A manual app shows the new image in yard status and waits for yard app deploy. Yard always runs the exact image, ghcr.io/you/api:1.4@sha256:…, so a restart never changes what runs.
To roll back, name an older tag and deploy:
yard app set --app api --image ghcr.io/you/api:1.3yard app deploy --app apiYard runs the image as its publisher built it. It does not set PORT or any other variable that changes how the image listens. So the bind must dial the port the image already uses. That is 8000 by default; --port names another one:
yard app new --image docker.io/library/nginx:1.27 --port 80 --public www.example.comThe image gets the app env, /data, and three variables: PROJECT_NAME, PROJECT_DATA_DIR=/data, and SHA, the digest. An image that keeps its files somewhere else than /data needs a compose file, or a recipe.
An image that needs settings before it can start, such as a database password, is created with --no-deploy. Set its env, then deploy:
yard app new --image docker.io/library/postgres:17 --no-deployyard env set --app postgres POSTGRES_PASSWORD=…yard app deploy --app postgresA recipe#
A recipe is a compose file at a URL, for a ready-made service. Yard's own recipes live in the recipes folder of the Yard repo.
yard app new --recipe uptime-kuma --public status.example.comA name means a recipe from that folder. A full https:// URL means a recipe from anywhere:
yard app new --app analytics --recipe https://example.com/recipes/umami.ymlA recipe is one compose file, and it follows the same rules as your own: a service app on the yard network, with ${APP_ENV} and ${DATA_DIR}. A comment line such as # port: 3001 tells --public which port to bind.
A recipe app follows its URL the way a code app follows its branch. At every check, Yard fetches the file. When its content changes, a push app deploys it. A manual app shows the change in yard status and waits. Yard keeps a copy of the file it runs, so a restart or a revert needs no network.
Yard's own recipes pin every image by digest. A new version of a service arrives as a commit to its recipe. A new major version gets a new recipe name, so the push trigger never moves a database to a new major version by surprise.
To stay on one version of a recipe, use the raw URL of one commit instead of main. To roll back, point --recipe at the raw URL of an older commit.
When the source fails#
An image app and a recipe app depend on something outside the VM. The rule is simple: the running app keeps running.
The registry or the URL does not answer. Yard tries again at the next check, and
yard status --fullshows asource <name>warning until it works.The recipe is not a valid compose file, or it breaks the rules. The deploy fails before anything starts, and the old version keeps running.
The new version never gets healthy. Yard reverts to the one before, as it does for code.
By the way#
Following an image or a recipe means that its publisher's changes deploy to your host. For a source that you do not control, create the app with
--trigger manual, and look at each update before you deploy it.yard app deploy --shais for code apps. An image app rolls back by tag, and a recipe app by URL.Prune removes only the images that Yard built. Pulled images stay on the disk until you remove them with Podman.