Layup

Apps and builds

An app is one or more services, each a container, deployed together. An app's code comes from a Git repository, an uploaded archive, or a published image.

Builders

Builder Use it for It needs
Dockerfile Anything with a Dockerfile A Dockerfile in the repository
Railpack Node, Python, Go, Ruby, PHP and more without a Dockerfile Nothing; it reads the project
Static site Front ends that build to plain files An install command, a build command and the output folder

Static sites are served by a small web server on port 8080. With single-page app on, unknown paths serve index.html.

Variables

Variables reach every service at runtime. Builds see them too:

  • Railpack and static sites see every variable, which front ends need for values like VITE_API_URL.
  • Dockerfile builds see a variable only if the Dockerfile declares it with ARG.

A secret variable never becomes a build argument, because build arguments stay readable in the image's history. A Dockerfile reads it with a build secret instead:

RUN --mount=type=secret,id=NPM_TOKEN \
    NPM_TOKEN=$(cat /run/secrets/NPM_TOKEN) npm ci

Variables marked secret are hidden from viewers.

Deploys

A deploy builds, starts the new containers next to the old ones, waits until they're healthy, switches traffic, then stops the old ones. Nobody sees an error in between.

A service is healthy when its health check path answers with a 2xx or 3xx status. A service without one must stay running, without restarting, for 10 seconds. After two minutes without passing, the deploy fails and the old containers keep serving.

Services with a volume are the exception: two copies of a database can't share one data folder, so the old container stops first and the service is briefly down.

Deploying a commit that was already built with the same settings and variables reuses its image instead of building again.

Compose files

An app can follow a compose.yml in its repository. Every deploy reads the file at the deployed commit. Changes made in Layup are kept as overrides on top of it.

These keys are used:

image build command entrypoint environment env_file volumes healthcheck deploy depends_on user working_dir ports expose restart labels container_name logging

These are refused, since they reach outside the app's own containers:

privileged network_mode pid ipc cap_add devices userns_mode security_opt cgroup_parent volumes_from runtime

Anything else is ignored with a warning. Paths, such as env_file and relative volumes, must stay inside the repository.

Layup reads a few keys of its own:

yaml
services:
  web:
    build: .
    x-layup-builder: railpack
    x-layup-healthcheck:
      path: /health
      port: 3000
  site:
    build: ./site
    x-layup-static:
      install: npm ci
      command: npm run build
      output: dist
      spa: true