Syntax
Options
Either
--box or --ip is required. If both are provided, the command exits with an error.
The Box Image
The slowest part of an install is the box’s Docker image. A cold build on the box takes roughly 14 minutes. When a pre-built image is available, the deployment pulls it before it stops the running containers. The box keeps its services up during the download. The old containers stop only after the pull succeeds. The deployment then clears the Docker build cache, because the pulled image needs no build. When the deployment builds on the box, it keeps the build cache. A later build on the same box reuses the layers that did not change.lager uninstall clears
that cache.
Every release tag publishes a pre-built image, and lager install uses it by
default: about 2 minutes instead of 14.
This only applies when --version names a release tag. The default
--version main has no published image, and neither does any other branch, so
those always build on the box. Pinning a release tag is the difference between
a two-minute install and a fourteen-minute one.
A full 40-character commit SHA also builds on the box, and the box must be able
to fetch that commit from the remote. A shorter hex value is read as a branch
name.
The image is verified before it is used:
- The computer that runs
lager installresolves the tag to an immutable image digest. This step needscurland access toghcr.io. - The box pulls that digest, pinned to its own architecture.
- The box checks the version label of the image. The label must name the exact version that you requested.
curl, an unreachable registry, or a missing or
mismatched label. A slow install that works beats a fast one that does not.
After a pull, /etc/lager/image-source records ghcr:<digest>. A build on the
box removes that file.
The image carries its license notices in /usr/share/licenses/lager/. That
directory holds the LICENSE and NOTICE of Lager, and THIRD_PARTY.md, which
lists the third-party software in the image. The pip/ directory holds the
notice files of each Python package. To list them, run
docker exec lager ls /usr/share/licenses/lager on the box.
Pass --no-pull to skip the pre-built image and always build. To turn the
install pull off for a whole shell, set LAGER_BOX_IMAGE_PULL to 0, false,
no or off. The variable turns the pull on only for 1, true or yes, in
any letter case, and every other value turns it off. --pull overrides the
variable. lager update reads the same words in the same way. Only the default
differs: install uses a pre-built image for a release tag, and update does not.
--pull together with --no-pull is a usage error.
Deploy Timeout
--timeout bounds the deploy step, which includes the container build. The
default is 1800 seconds. Without the flag, LAGER_INSTALL_TIMEOUT sets the
value. An unparseable or negative LAGER_INSTALL_TIMEOUT gives the default.
--timeout 0 removes the bound, and a negative --timeout is refused.
When the budget runs out, install prints Deployment timed out after .... It
also says that the build can still be healthy, and it prints retry commands with
twice the budget. It then exits 1. A re-run is safe.
The install lock stays valid for the larger of 3600 seconds and twice the
timeout. The lock cannot be renewed while the container that serves it is down.
With --timeout 0 the lock never expires, because no fixed time outlasts an
unbounded deploy. An install that ends normally releases the lock. After a hard
kill, such as a power loss, the lock has no expiry time to clear it, and
lager boxes unlock releases it.
What Gets Installed
The firewall script sets a default deny policy for incoming traffic and allows
SSH from anywhere. It allows the Lager ports on
lo, docker0, tailscale0
(if Tailscale is up) and the --corporate-vpn interface. It denies them on
every other interface. These rules govern the Lager ports only on a box set to
lager box-config network-mode host, where the container does not publish
ports.
Each run of the script resets ufw first. A later lager install without
--skip-firewall therefore removes firewall rules that you added by hand.
lager update does not run the script.
Installation Flow
- Resolve target - Looks up box IP from
--boxname or uses--ipdirectly - Verify SSH - Tests key-based authentication and settles which identity the rest of the command offers
- Show summary - Displays what will be installed and asks for confirmation
- Deploy - Runs the deployment script. About 2 minutes when the pre-built
image is used; a cold build on ordinary box hardware is roughly 14 minutes
(see below). The step is bounded by
--timeout, 30 minutes by default — raise it on slower hardware, where a healthy build can legitimately exceed the default - Store version - Writes the deployed version and the CLI version to
/etc/lager/version, and the deployed ref and commit to/etc/lager/ref. It also writes the build hash to/etc/lager/build-hashand reads the version file back. If a write fails, install names the file, prints the manual fix, and exits1. The deployment is complete at that point - Check passwordless sudo - Confirms the sudoers grant that
lager box-config applyneeds. The deploy step normally writes it, and this step then asks for nothing. If the box does not have the grant, this step writes it and asks for the box’s sudo password. A failure here is a warning, not an install failure - Add to config - Asks whether to add the box to
~/.lager. Install skips this step with--yes, and with--box(the box is already stored)
Examples
Passwordless sudo
Installation configures passwordlesssudo for the box login user. The CLI drives
the box over non-interactive SSH, where sudo has no terminal to prompt
against. The grants must be in place before provisioning runs.
Lager writes these files, and only these files, under /etc/sudoers.d/:
Lager regenerates each file in full when it writes that file. That keeps a
box on the current grant shape. A grant added inside one of them is lost the
next time it is written, so each file opens with a header saying so.
Sudo password prompts
lager install writes both of its files in one sudo session, early in the deploy
step. It asks for the box’s sudo password in that session and nowhere else:
Before the session, install checks the box without a terminal. It compares
/etc/lager/.deploy-sudoers-v1 with a digest of the grants that this run writes,
and it runs one granted command with sudo -n. If both checks pass, install
skips the session. A different --user or --corporate-vpn value changes the
grants, and a newer CLI can change them too. Install then runs the session one
time and asks for the password one time.
To make the next install write the files again, delete
/etc/lager/.deploy-sudoers-v1 on the box. lager update does not use this
file, and it asks for no password.
The same session installs /usr/local/lib/lager/etc_lager_perms.sh. This
root-owned script creates /etc/lager and sets its owner. It does not change
/etc/lager/authorized_keys.d. The grant names the script by its exact path,
and the script refuses all arguments. Lager grants no find command and no
recursive chown.
Lager never reads, edits, or removes any other file in /etc/sudoers.d/. If you
or a box-management platform need additional grants, put them in a separate file
there. For example, /etc/sudoers.d/zz-local sorts after Lager’s files, so its
rules win. Lager will leave that file alone, including during lager uninstall.
SSH Authentication
Install requires key-based SSH authentication. It offers~/.ssh/lager_box —
the key lager ssh-setup and lager install generate — explicitly, because
that is not a filename ssh tries on its own. If the box does not accept it,
install falls back to your own default identities, so a box you authorized with
ssh-copy-id works unchanged.
If neither authenticates, install offers to set the key up for you:
--yes accepts it without asking.
Declining stops the install and points you at lager ssh-setup --box <name-or-ip>, which does the same thing as a separate step.
Install does not offer a password fallback for the deployment itself. A box
configured with PasswordAuthentication no never receives the password. The
resulting “password failed” message therefore described something that never
happened.
For new hosts, the SSH host key is accepted automatically. If the host key
changed since a previous connection, the command asks you to verify the change
manually first.
Install does not write to ~/.ssh/config. Earlier versions added a per-IP
Host block naming the key. Another tool that regenerated the file deleted
that block, and the block also disabled host-key verification for the box.
The identity is passed per command instead.
Notes
- For a step-by-step walkthrough, see Setting Up a Lager Box
- Requires SSH client tools (
ssh,ssh-keygen) to be installed locally - The deployment script is bundled with the
lager-clipackage - After installation, verify connectivity with
lager hello --box <name> - Use
lager updateto deploy code updates to an already-installed box - Use
lager uninstallto remove Lager software from a box

