Bootstrap is a declarative package bootstrapper for reproducible system provisioning.
Instead of writing an imperative shell script that decides how to install each package, you write a small manifest that describes the packages a system should have. Bootstrap parses that manifest, plans the requested package operations, resolves them through a native package-manager backend, and then either explains or executes the resulting plan.
The goal is not to replace the operating system package manager. The goal is to make bootstrap intent easier to read, review, test, and repeat. The distribution's package maanger is used on the backend. This is not a replacement, but a wrapper.
- Plain-text package manifests.
- Comments and blank lines for human-readable manifests.
- Package version constraints such as
>=,=,==,<, and>. - Dry-run mode for reviewing planned work before changing a system.
- Explain mode for understanding how manifest entries become planned actions.
- Native package-manager delegation for APT, Alpine APK, and DNF.
- Visible per-package installation progress outside quiet mode.
- Per-package installation timeouts when GNU
timeoutis available. - APT installations that omit automatically recommended packages.
- Conservative diagnostics that stop rather than guessing when input is unclear.
- Three generated standalone release flavors: development, stripped, and minified.
- SHA-256 checksum companions for every executable release flavor.
- Support for multiple system package managers:
- APT: Ubuntu / Debian variants
- DNF: RedHat variants
- APK: Alpine
Bootstrap releases contain three executable representations of the same program:
bootstrap.dev.bashcontains the fully assembled source with comments intact.bootstrap.bashis the ordinary/default artifact with full-line comments removed.bootstrap.min.bashis derived frombootstrap.bashwith the pinned Bash-Minifier build dependency.
All three files are standalone executable Bash scripts and are tested against the
same behavior suite. The conventional bootstrap.bash filename remains the
default for existing consumers and examples.
To download the latest ordinary release artifact with curl:
curl -fsSL \
https://github.com/wesley-dean/bootstrap/releases/latest/download/bootstrap.bash \
-o bootstrap.bash
chmod +x bootstrap.bashEach executable also has a .256 SHA-256 checksum companion in the release:
bootstrap.dev.bash.256
bootstrap.bash.256
bootstrap.min.bash.256
If you use Bootstrap frequently, you may prefer a shell function that keeps the
latest release artifact in ~/.local/bin/bootstrap.bash and lets you provide a
small package manifest directly as command-line arguments.
For example, after defining the function below, this command:
bootstrap git curl jqis equivalent to passing this manifest to Bootstrap through standard input:
git
curl
jq
The function intentionally keeps the wrapper small. If the local copy of the
release artifact is missing, it downloads the latest published bootstrap.bash
artifact and marks it executable. It then writes each argument on its own line
and pipes that generated manifest into Bootstrap.
bootstrap() {
local bootstrap_path="${bootstrap_path:-${HOME}/.local/bin/bootstrap.bash}"
local bootstrap_url="${bootstrap_url:-https://github.com/wesley-dean/bootstrap/releases/latest/download/bootstrap.bash}"
local -a effective_bootstrap_args=()
if declare -p bootstrap_args &>/dev/null; then
effective_bootstrap_args=("${bootstrap_args[@]}")
fi
if [[ ! -x "${bootstrap_path}" ]]; then
mkdir -p "${bootstrap_path%/*}" \
&& curl -fsSL \
"$bootstrap_url" \
-o "${bootstrap_path}" \
&& chmod +x "${bootstrap_path}"
fi
for package in "$@"; do
printf '%s\n' "${package}"
done | "${bootstrap_path}" "${effective_bootstrap_args[@]}" -
}Create a manifest file. This example uses packages.manifest:
#------------------------------------------------------------------------------
# Bootstrap starter manifest
#
# Each logical line describes one package requirement. Blank lines and comments
# are ignored. Inline comments are also supported.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Unconstrained packages
#
# These packages may be installed at the package manager's candidate version.
#------------------------------------------------------------------------------
git
curl
jq
# Inline comments are allowed after package requirements.
ca-certificates # useful for HTTPS package repositories and downloads
#------------------------------------------------------------------------------
# Minimum versions
#
# These entries require the package manager's candidate version to satisfy the
# lower bound before Bootstrap will plan installation.
#------------------------------------------------------------------------------
shellcheck >= 0.10
shfmt >= 3.10
bats >= 1.12
#------------------------------------------------------------------------------
# Exact versions
#
# A single equals sign and a double equals sign are both supported for exact
# version requirements.
#------------------------------------------------------------------------------
yq = 4.45.4
openssl == 3.0
#------------------------------------------------------------------------------
# Upper bounds
#
# Upper bounds can be useful when a later major version is not yet supported by
# the workstation, project, or operational environment being bootstrapped.
#------------------------------------------------------------------------------
python3 < 3.14
nodejs < 23
#------------------------------------------------------------------------------
# Greater-than constraints
#
# This form is available when the candidate version must be strictly newer than
# a known baseline.
#------------------------------------------------------------------------------
make > 4.0
Then run Bootstrap with the manifest:
./bootstrap.bash packages.manifestMultiple manifests may be supplied in one invocation:
./bootstrap.bash core.manifest containers.manifest security.manifest
./bootstrap.bash *.manifestBootstrap parses, plans, and resolves every supplied manifest before executing any package operation. If preflight fails, no package changes are made, and the diagnostic retains the original manifest filename and line number.
To inspect the plan without making package-manager changes:
./bootstrap.bash --dry-run packages.manifestTo include a plain-language explanation of the planning boundary:
./bootstrap.bash --dry-run --explain packages.manifestTo select the package-manager backend explicitly:
./bootstrap.bash --package-manager apt packages.manifest
./bootstrap.bash --package-manager apk packages.manifest
./bootstrap.bash --package-manager dnf packages.manifestBootstrap uses a pinned released bashdeps.bash artifact to manage ordinary
external build/development files declared in dependencies.txt.
The Makefile directly bootstraps only vendor/bashdeps.bash. It verifies the
pinned release bytes against the SHA-256 digest committed in the Makefile before
execution. Current manifest-managed dependencies include the Bash Doxygen filter
used for reference documentation and a commit-pinned copy of Bash-Minifier used
to derive bootstrap.min.bash.
Synchronize dependency state explicitly with:
make depsVerify already-present dependency state without network access or repair with:
make deps-checkBuild all three executable flavors and their checksum companions with:
make buildmake build remains network-free and does not repair dependency state. It does,
however, require an already-prepared vendor/bash-minifier.bash; a fresh checkout
therefore needs make deps before make build.
For a fresh-checkout convenience path that synchronizes dependencies and then builds, use:
make allA successful build creates:
dist/bootstrap.dev.bash
dist/bootstrap.bash
dist/bootstrap.min.bash
dist/bootstrap.dev.bash.256
dist/bootstrap.bash.256
dist/bootstrap.min.bash.256
vendor/ is generated state and is not committed. None of the three released
executables requires bashdeps, Bash-Minifier, dependencies.txt, or the vendor
tree at runtime. See ADR-051, ADR-052, and doc/testing.md for the detailed
boundary and validation workflow.
The Bootstrap tool is distributed as standalone Bash scripts with no external library dependencies at runtime. While the source for the tool is spread across multiple files, each release flavor is a single executable file. Therefore, "installation" in a traditional sense isn't a hard requirement.
vet can be used as a safer replacement for
the common curl | bash pattern. It downloads the release artifact, displays
the script for review, and then runs it with the arguments you provide.
To run Bootstrap with packages.manifest through vet:
vet https://github.com/wesley-dean/bootstrap/releases/latest/download/bootstrap.bash \
packages.manifestThe same Bootstrap arguments can be passed after the release URL:
vet https://github.com/wesley-dean/bootstrap/releases/latest/download/bootstrap.bash \
--dry-run --explain packages.manifestIt is not recommended, but it's also possible to run Bootstrap by fetching it
first using curl (or wget). This is not a recommended approach; it's
included here for completeness. From a security perspective, it's an
anti-pattern to download and run a shell script -- especially one that makes
changes to the underlying system like installing packages -- without inspecting
the script first. The source for Bootstrap is openly available and can be
inspected along with the process that the bootstrap.bash script is generated.
The source is heavily commented so that it ought to be clear what every single
function in the script does, how they work, how they're invoked, and any
assumptions made when running them. The code should be as direct, obvious, and
boring as possible.
If you understand the risks and still prefer this approach, you can invoke Bootstrap directly.
Warning
Running a script directly from the Internet (for example, curl ... | bash)
executes code before you have an opportunity to inspect it. Download the
script first, or use vet, if you want to review the script before
execution.
curl https://github.com/wesley-dean/bootstrap/releases/latest/download/bootstrap.bash \
| bash -s -- --dry-run --explain packages.manifestBootstrap is designed to run either with elevated (root) privileges or as a
normal system user in which case it uses sudo or doas only when needed.
If neither sudo nor doas are available and Bootstrap is run as a normal
user, Bootstrap will not be able to perform operations that modify the
underlying system. That is, it won't be able to install packages.
In this case, all is not lost. Another option would be to use su to run
Bootstrap as a user with elevated privileges:
su -c '/path/to/bash /path/to/bootstrap.bash`The README is intentionally brief. More detailed project documentation is kept in focused files:
doc/motivation.mddiscusses why this project existsdoc/cli.mddescribes the supported command-line interface.doc/manifest-format.mddescribes the manifest grammar and parser behavior.doc/examples/contains small, release-specific starter manifests.doc/adr/contains the project's Architecture Decision Records.testing.mddescribes how to test the tool during development work.CONTRIBUTING.mddescribes contribution expectations.SECURITY.mdexplains how to report security concerns.Reference Documentationdevelopment reference material
Bootstrap is under active development. The current implementation focuses on a small, reviewable package-manifest workflow with APT, Alpine APK, and DNF backends. Additional package-manager backends and richer provisioning features are future work.
See LICENSE for license information.