Skip to content

Install script

Install Kiln with one command, including mirrors, silent installs, systemd, and uninstall.

Updated View as Markdown

Run one command to install Kiln on Linux or macOS, and run it again to upgrade. The script detects the platform, selects a working download source, verifies SHA256SUMS, and replaces the binary atomically. It does not require sudo by default, and it displays the complete plan before writing any files.

One-line install

curl -fsSL https://raw.githubusercontent.com/babywbx/Kiln/main/install.sh | sh

Windows is out of scope for the script; it exits with a pointer to the release archives. See Manual install and Windows service.

What it does

Detect platform and tooling

Map uname -s and uname -m onto a release artifact, then confirm curl or wget, plus tar, mktemp, and sha256sum or shasum. Anything missing produces an install command for the local package manager and an early exit.

Choose an install directory

/usr/local/bin when it is writable, otherwise $HOME/.local/bin.

Read the current state

If an executable kiln is already there, the script runs kiln -version and parses version= and variant= to decide whether this run is an install, an upgrade, a downgrade, or a variant switch.

Resolve the version and the source

Resolve the latest release unless --version pins one, then probe every candidate download source in parallel.

Print the plan and ask

Version, variant, platform, location, source, and decoder status are shown as a table. Nothing is written before you confirm.

Download, verify, swap

Fetch the archive, compare it against SHA256SUMS, unpack into a staged file next to the target, smoke-test it, and only then rename it into place.

The plan looks like this:

  About to run:
     Version     vX.Y.Z (latest)
     Variant     full
     Platform    linux/amd64
     Location    /usr/local/bin/kiln
     Source      github.com (direct)
     Decoder     ffmpeg not found (native engine, nothing to install)

The decoder line is an environment check, not a dependency: the native engine needs no FFmpeg, and the result never changes what gets installed. See Media engine.

State-aware by design

The same command behaves differently depending on what is already installed, so there is no separate upgrade command to remember.

Current state What happens
Nothing installed Plain install, the confirmation defaults to Y
Older version installed Upgrade, the confirmation defaults to Y
Same version, same variant Reported as already current; the interactive menu defaults to cancel, and --yes exits 0 immediately
Same version, different variant Switches variants, for example full to lite
Newer version installed Downgrade, the confirmation defaults to N

When an existing install is found in an interactive session, the script offers three choices: upgrade or reinstall, uninstall, or cancel. Picking uninstall runs the same flow as --uninstall.

Options

Option Description Default
--yes, -y Non-interactive, accept every default Interactive
--version <v> Pin a version, with or without the leading v; supports the SemVer core and prerelease identifiers, but not +build metadata Latest release
--lite Install the lite variant, built for Linux only full
--dir <path> Explicit install directory, with no fallback once given See Install location
--mirror <base> Use a specific GitHub proxy and skip probing; must start with https://, contain no whitespace, and trailing slashes are stripped Auto-probe
--no-mirror Direct connection only; mutually exclusive with --mirror Off
--lang Force the output language, accepts zh or en only From LC_ALL, LANG
--service Install the systemd unit and enable it once configured; needs Linux, systemd, and root Off
--uninstall Remove the installed binary, and the service when present
--dry-run Show and simulate every step, write nothing Off
--help, -h Print usage and exit

Unknown options, options missing their value, an invalid --lang value, and --mirror combined with --no-mirror all exit 1 before anything else runs.

The following installation options can also be set with environment variables. Command-line flags take precedence.

Variable Equivalent option
KILN_YES --yes
KILN_VERSION --version
KILN_VARIANT --lite (accepts full or lite only)
KILN_INSTALL_DIR --dir
KILN_MIRROR --mirror
KILN_NO_MIRROR --no-mirror
KILN_LANG --lang
KILN_DRY_RUN --dry-run

Mirrors and download sources

The installer can fall back to built-in GitHub proxies when a direct connection is unavailable.

Without --mirror, the script sends HEAD probes to the direct URL and all three mirrors at once, each with a 3-second timeout, then takes the first reachable candidate in priority order. Direct comes first, mirrors follow in their built-in order. A reachable github.com therefore always wins; mirrors only come into play when the direct connection fails, and the output states plainly when a mirror is in use.

--mirror skips probing entirely and uses the given base. --no-mirror shrinks the candidate set to the direct connection alone and fails outright rather than falling back.

Version resolution follows the same idea: the release redirect first, the GitHub API second, and finally each built-in mirror in turn when mirrors are allowed.

Install location and PATH

Without --dir:

  • /usr/local/bin is used when it exists and is writable, which is where a root run normally lands;
  • otherwise the install falls back to $HOME/.local/bin, so an unprivileged user never needs sudo;
  • if HOME is unset too, the script stops with an error.

--dir is an explicit choice and never falls back. The path must be a directory if it already exists, and the nearest existing parent must be writable.

When the install directory is not on PATH, the script prints a ready-to-paste line for the current shell: ~/.zshrc for zsh, fish_add_path for fish, ~/.bashrc otherwise.

Versions and upgrades

Re-running the script is the upgrade path. Comparison covers the SemVer core and prerelease identifiers, so 1.0.0-rc.1 correctly sorts before 1.0.0.

curl -fsSL https://raw.githubusercontent.com/babywbx/Kiln/main/install.sh | sh -s -- --version 1.0.0

A malformed version string exits 1. A well-formed version with no matching release gets as far as source probing and exits 3.

For unattended upgrades, add --yes: when the installed build is already current, the script prints one line and exits 0, which makes it safe to run from a timer.

curl -fsSL https://raw.githubusercontent.com/babywbx/Kiln/main/install.sh | sh -s -- --yes

The lite variant is built for Linux only, so --lite on macOS exits 2. See Lite, Core and Full.

systemd service

--service registers a system service that starts at boot, right after installing the binary. It needs root, and elevating a piped command is awkward, so download first and run second:

curl -fsSL https://raw.githubusercontent.com/babywbx/Kiln/main/install.sh -o /tmp/kiln-install.sh
sudo sh /tmp/kiln-install.sh --yes --service

Three preconditions are checked before anything is written:

  • Linux with systemctl available;
  • running as root;
  • an absolute install directory, free of whitespace, and not under /home, /root, /tmp, or /var/tmp. The default /usr/local/bin qualifies.

Then the script:

Prepares the service account

Creates a kiln system user and matching group with useradd -r -U -d /var/lib/kiln when the user does not exist, using the first available login shell among /usr/sbin/nologin, /sbin/nologin, and /bin/false.

Creates the directories

Creates /etc/kiln and /var/lib/kiln, with the latter owned by the service account.

Writes and loads the unit

Writes /etc/systemd/system/kiln.service, then runs systemctl daemon-reload.

Enables it only when configured

Runs systemctl enable --now kiln when /etc/kiln/kiln.toml exists. Otherwise the unit stays disabled and the script tells you to write the config first.

The unit ships hardened:

/etc/systemd/system/kiln.serviceini
[Unit]
Description=Kiln
After=network-online.target
Wants=network-online.target

[Service]
User=kiln
Group=kiln
ExecStart=/usr/local/bin/kiln -config /etc/kiln/kiln.toml
WorkingDirectory=/var/lib/kiln
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/lib/kiln

[Install]
WantedBy=multi-user.target

ProtectSystem=strict makes the whole filesystem read-only for the service, and ReadWritePaths carves out /var/lib/kiln as the single writable location. Pointing data_dir somewhere else means adding that path here as well. See Configuration.

A first install usually continues like this: write /etc/kiln/kiln.toml, enable the service, then watch it.

sudo systemctl enable --now kiln
systemctl status kiln
journalctl -u kiln -f

Uninstall

curl -fsSL https://raw.githubusercontent.com/babywbx/Kiln/main/install.sh | sh -s -- --uninstall

Discovery order: with --dir, only that directory is checked; otherwise /usr/local/bin/kiln then $HOME/.local/bin/kiln, and only regular files count, so symlinks are skipped. A custom install directory therefore has to be named with --dir.

If neither is found and no systemd unit exists, the script reports nothing to remove and exits 0. Otherwise it confirms first, defaulting to N unless --yes is passed.

When the systemd service is present, uninstalling requires root: the script runs systemctl disable --now kiln, deletes the unit, reloads the daemon, and only then removes the binary. Without root it removes nothing and prints the manual command:

sudo systemctl disable --now kiln && sudo rm /etc/systemd/system/kiln.service && sudo systemctl daemon-reload

Dry run

--dry-run walks the full decision path and prints every step marked as simulated, but creates no temporary directory, downloads nothing, writes nothing, and skips the dependency check.

curl -fsSL https://raw.githubusercontent.com/babywbx/Kiln/main/install.sh | sh -s -- --dry-run --service

It is the fastest way to confirm the chosen location, the selected source, and whether the systemd preconditions hold. Note that a dry run stays offline: without --version, the version shown in the plan is a placeholder rather than the real latest release.

Checksums and the atomic swap

After the download, the script fetches SHA256SUMS (direct with a 5-second timeout, falling back to the active source with a 10-second timeout) and compares the line matching this artifact. A mismatch discards the downloaded file and exits 4 without touching the existing install.

Only then is the archive unpacked, and the target file is never written in place:

  1. a .kiln.new.XXXXXX staging file is created in the target directory, written, and chmod 0755;
  2. it is smoke-tested by running -version;
  3. it is renamed over the final path atomically.

Any failure deletes the staging file and leaves the previous install working. A failing smoke test usually means the target directory is mounted noexec, or the wrong architecture was downloaded. Interrupting the script cleans up the staging file and the temporary directory as well.

Supported platforms

System Architectures Variants
Linux amd64, arm64, armv7, armv6 full, lite
macOS amd64, arm64 full

uname -m is mapped as follows: x86_64 and amd64 become amd64, aarch64 and arm64 become arm64, armv7l and armv8l become armv7, and armv6l becomes armv6. Anything else exits 2.

macOS gets one extra check. When uname -m reports x86_64, the script reads sysctl hw.optional.arm64; a value of 1 means the shell is merely running under Rosetta on Apple Silicon, so the arm64 artifact is installed instead of a translated x86 build.

Windows and every other system exit 2 with a pointer to the release archives.

Exit codes

Code Meaning
0 Success, including a deliberate cancel, --help, an already current install, and nothing found to uninstall
1 Bad arguments, missing tools, a directory that is unwritable or not a directory, unset HOME, a malformed version, a failed write, a binary that will not execute, or a systemd setup or removal failure
2 Unsupported platform or architecture, including Windows, and --lite outside Linux
3 Network trouble: the version could not be resolved, or no download source was reachable
4 SHA256 mismatch; the downloaded file was discarded
130 Interrupted

Next steps

Navigation

Type to search…

↑↓ navigate↵ selectEsc close