Skip to content

Commit

Permalink
Docs corrections
Browse files Browse the repository at this point in the history
  • Loading branch information
Colonial-Dev committed Jan 26, 2025
1 parent 58f1505 commit 5a58d3a
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 13 deletions.
58 changes: 51 additions & 7 deletions DEFINITIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ All definitions can contain metadata as TOML key-value pairs with a special pref

Metadata can be placed anywhere in the file. When Box evaluates a definition, each line of metadata is extracted and concatenated into a single TOML document; any intervening lines are ignored.

Currently, only two keys are recognized:
Currently, only one key is recognized:
- `depends_on` (`[string]`) - a list of definition names that this definition depends on. Defaults to empty.

## Build Laziness

By default, Box only builds new and changed definitions to maximize efficiency, especially for those on slow or data-limited connections. This logic takes into account dependency trees; if `alpha` depends on `beta` and only `beta` is changed, both `alpha` and `beta` will be rebuilt.

To override this behavior, pass the `-f`/`--force` flag to `bx build`.

## Commands

Box provides (approximate) implementations of all OCI Containerfile operations as shell functions, as well as several additional tools.
Expand Down Expand Up @@ -111,14 +117,14 @@ CFG <FUNCTION> [ARGS...]
| `cap-add` | Add a Linux capability to the container. | See `man capabilities`. |
| `cap-drop` | Remove a Linux capability from the container. | See `man capabilities`. |
| `cpus` | Number of CPUs the container can utilize. | Self-explanatory. |
| `memory` | Container memory limit. | Supports `b`, `kb`, `mb`, and `gb` as suffixes. |
| `memory` | Container memory limit. | Supports `b`, `k`, `m`, and `g` as suffixes. |
| `ulimit` | Set the `ulimit` parameters for the container. | See `man ulimit`. |
| `device` | Add a host device to the container. Uses `--volume` syntax. | Self-explanatory. |
| `userns` | Set the user namespace mode for the container. | [Podman docs](https://docs.podman.io/en/v4.4/markdown/options/userns.container.html) |
| `security-opt` | Set a security option for the container. | [Podman docs](https://docs.podman.io/en/v4.6.1/markdown/options/security-opt.html) |
| `mount` | Add a mount (`bind` or otherwise) to the container. Uses `--mount` syntax. | [Podman docs](https://docs.podman.io/en/v5.1.0/markdown/podman-create.1.html#mount-type-type-type-specific-option) |
| `restart` | Set the container restart policy. | [Podman docs](https://docs.podman.io/en/v5.1.0/markdown/podman-create.1.html#restart-policy)
| `secret` | Give the container access to a secret. | [Podman docs](https://docs.podman.io/en/v5.1.0/markdown/podman-create.1.html#secret-secret-opt-opt) |
| `userns` | Set the user namespace mode for the container. | [Podman docs](https://docs.podman.io/en/stable/markdown/podman-create.1.html#userns-mode) |
| `security-opt` | Set a security option for the container. | [Podman docs](https://docs.podman.io/en/stable/markdown/podman-create.1.html#security-opt-option) |
| `mount` | Add a mount (`bind` or otherwise) to the container. Uses `--mount` syntax. | [Podman docs](https://docs.podman.io/en/stable/markdown/podman-create.1.html#mount-type-type-type-specific-option) |
| `restart` | Set the container restart policy. | [Podman docs](https://docs.podman.io/en/stable/markdown/podman-create.1.html#restart-policy)
| `secret` | Give the container access to a secret. | [Podman docs](https://docs.podman.io/en/stable/markdown/podman-create.1.html#secret-secret-opt-opt) |

### `PRESET`

Expand Down Expand Up @@ -172,4 +178,42 @@ trap cp

This is not included in the POSIX harness, which automatically applies `set -eu` to abort on non-zero exit codes or uses of unset variables.

## Additional Pointers

### Persistent Package Manager Cache

One of the big advantages of Containerfiles over `buildah`-based scripts is layer caching. Every statement in a Containerfile writes a new layer that can be cached and reused in future builds. This is *especially* useful when dealing with package manager calls, as it avoids wasting huge amounts of time and bandwidth when iterating on other details in the file.

You can alleviate this by configuring a cache directory on your host that persists between builds. The below demonstrates how to accomplish this with DNF 5, but the same logic should easily transfer to your preferred package manager.

```sh
# Mount ~/.cache/dnf on my host into the standard DNF 5 cache directory in the container.
FROM -v $HOME/.cache/dnf:/var/cache/libdnf5:z fedora-toolbox:latest
# Prevent DNF from clobbering the cache by default.
RUN sh -c "echo keepcache=True >> /etc/dnf/dnf.conf"
# Install all your packages!
```

### Desktop Access

You may want to execute graphical applications or access the system clipboard inside containers. This is actually quite simple to implement!

For Wayland:

```sh
ENV WAYLAND_DISPLAY=wayland-0
# You may want to change this if your user inside the container has a different UID.
ENV XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR
CFG mount type=bind,src=$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY,dst=$XDG_RUNTIME_DIR/wayland-0
```

For X11 - warning, untested as my setup does NOT like X:

```sh
ENV DISPLAY=$DISPLAY
CFG mount type=bind,src=/tmp/.X11-unix,dst=/tmp/.X11-unix
CFG args --ipc=host
CFG device /dev/dri
```

[^1]: If you're wondering "how the hell does it do that" - it saves them as OCI annotations that are read back at creation time. <br> [Did you know you can just use the ASCII separator characters to separate things?](https://github.com/Colonial-Dev/box/blob/0c45cfe2c51a4ff1c3f62b3f753bcfeab882a56b/src/podman.rs#L341-L352) They're right there. Nobody can stop you.
28 changes: 22 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ for my development containers.
# --pull=newer updates my local copy of the fedora-toolbox image if needed.
# -v $HOME/.cache/dnf... mounts a shared, persistent DNF cache into the working container -
# good for recouping most of the speed loss from not using Containerfiles.
FROM fedora-toolbox:latest
FROM --pull=newer -v $HOME/.cache/dnf:/var/cache/libdnf5:z fedora-toolbox:latest

# Set up DNF opts. The 'keepcache=true' in particular is critical for efficiency.
for opt in "keepcache=True" "max_parallel_downloads=8" "fastestMirror=True"
Expand Down Expand Up @@ -139,10 +139,7 @@ USER $USER
# ... and the working directory to my $HOME inside the container.
WORKDIR /home/$USER
# A dummy 'infinite command' like this keeps the container alive so processes on the host
# can spawn 'exec' sessions inside.
#
# You could also use a minimal `init` implementation, such as `tini`,
# to ensure that zombie processes are reaped correctly.
# (e.g. VSCode) can spawn 'exec' sessions inside.
CMD "sleep inf"

# Mount my projects directory.
Expand Down Expand Up @@ -209,17 +206,36 @@ As a Silverblue user, this tight coupling with my "pure" host always left a bad
Box also requires that every container be associated with a "definition," rather than defaulting to a standard "toolbox" image for each container. These use Box's custom shell-based format to declare runtime arguments (like mounts) during build time.
> I find this particularly advantageous for ensuring a consistent environment between my desktop and my laptop. It also makes for a good "lazy man's NixOS" on my Pi-hole server.
> I find this particularly advantageous for ensuring a consistent environment between my desktop and my laptop. It also makes for a good "lazy man's NixOS[^2]" on my Pi-hole server.
So:
- If you don't mind the above caveats and want containerized environments that Just Work with the host, use Toolbx or Distrobox.
- If you *do* mind the above caveats and/or want some declarative-ness in your containers, give Box a try.
> This is also where the name 'Box' came from; it makes boxes without any promises about the contents. You get to decide.
### "Why use shell scripts for definitions?"
Not only is shell a familiar environment that's easily extensible by external programs like Box, it also enables you to sprinkle logic into your definitions if needed.
Consider this snippet that mounts all non-hidden `$HOME` directories into the container:
```sh
for dir in (ls -p $HOME | grep /)
CFG mount type=bind,src=(realpath $dir),dst=/home/$USER/$dir
end
```
As far as I'm aware, doing something like this in the available declarative formats (`compose` et. al.) would be a tedious manual affair duplicated across every container that needs this behavior.
### "Why not just use Kubernetes YAML or `compose`?"
A few reasons:
1. For Box's target use case of "bespoke interactive containers," separating the information on how to *build* the image from information on how to *run* it is [suboptimal](https://htmx.org/essays/locality-of-behaviour/).
2. Kubernetes YAML is massively overcomplicated for what I wanted to do, and the `podman` version of `compose` was somewhat buggy when I tried it.
- I was made aware as I was finishing up Box that `docker-compose` now works "out of the box" with `podman`, so if that sounds like what you want - by all means, use that instead!
3. YAML is... [yeah](https://github.com/Colonial-Dev/satpaper/blob/b2016c63ffeafc70538fd2b02fa60d1c077fd694/.github/workflows/release.yml#L1-L3).
[^1]: Single Rust binary compiled from ~2000 lines of boring plumbing code. Red Hat and the OCI have already done all the heavy lifting here!
[^2]: My apologies to any Nix fans in the audience, but my brain is too smooth to handle it.

0 comments on commit 5a58d3a

Please sign in to comment.