Pre-release
Kith is under active development. Formats and behavior can change between releases, and mistakes can break your servers. Back up anything you can't afford to lose.
Installation
Requirements
- Docker with the Compose plugin
- Bun 1.4+ (Node.js is not supported), only for the Bun package install. The prebuilt binary needs no runtime.
The user running kith needs permission to use Docker. Kith shells out to docker and docker compose for everything, so add yourself to the docker group (or run rootless Docker) before starting.
Kith is configured entirely through environment variables, and how you install determines which ones you need to set. There are two models:
- Per-user install (default). Kith follows the XDG Base Directory Specification. Everything lives under the invoking user's home directory, with nothing to configure.
- Global install. One shared kith for the whole machine, managed by a Unix group. You point the directories at system locations (
/var/lib,/var/log, and so on) and setKITH_GIDto the managing group.
Install script (recommended)
Every GitHub release carries prebuilt single-file binaries for Linux and macOS (x64 and arm64). The install script downloads the latest one, verifies it against the release's checksums, and installs it:
curl -fsSL https://raw.githubusercontent.com/foopis23/kith/main/scripts/install.sh | shThe binary lands in /usr/local/bin when that's writable, otherwise ~/.local/bin (with a reminder if the chosen spot isn't on your PATH). Two environment variables override the defaults:
KITH_VERSION— a release tag to install instead of the latest, for exampleKITH_VERSION=0.4.1.KITH_INSTALL_DIR— where the binary goes. The script prompts for sudo itself when the target isn't writable.
Upgrading is the same command again. The binary is self-contained, so there's no runtime to install or keep updated.
Bun package install
Kith is also published to GitHub Packages as a Bun package, if you'd rather manage it through Bun. Point Bun at GitHub Packages first. Bun's --registry flag and .npmrc scope mapping are unreliable for global installs, so use Bun's config file instead. Add this to ~/.bunfig.toml, creating the file if it doesn't exist:
[install.scopes]
"@foopis23" = "https://npm.pkg.github.com"Then install and run:
bun install -g @foopis23/kith
kithUpgrading is the same command again. "Global" here means global to your account: the package lands in ~/.bun/install/global and the kith command is linked into ~/.bun/bin, which Bun's installer puts on your PATH.
Per-user install
This is the default model and needs no directory setup. Kith resolves its directories from the XDG environment variables, falling back to the spec's defaults under your home directory:
- Servers and world data:
$XDG_DATA_HOME/kith/servers, default~/.local/share/kith/servers - Logs:
$XDG_STATE_HOME/kith/logs, default~/.local/state/kith/logs - Cache:
$XDG_CACHE_HOME/kith, default~/.cache/kith - Scratch files:
$TMPDIR/kith, wiped on reboot
Files are owned by you (KITH_UID/KITH_GID default to your ids), and files carrying secrets (compose files with backup credentials, password scratch files) default to mode 600, readable by you alone. Nothing to configure.
Global install
For one kith shared by several admins, a dedicated Unix group is the admin boundary. The directories live at system locations, and group membership plus file permissions do the rest.
Recommended layout, following the Filesystem Hierarchy Standard:
| Env var | Path | Used for |
|---|---|---|
KITH_SERVERS_DIR | /var/lib/kith/servers | Server compose projects and world data |
KITH_LOG_DIR | /var/log/kith | Application logs |
KITH_BASE_BACKUP_DEST | /var/backups/kith | Backup repositories |
KITH_CACHE_DIR | /var/cache/kith | Cache (version manifests, Java lookups) |
Leave KITH_TMP_FILE_DIR at its default ($TMPDIR/kith). Scratch files are short-lived, and the OS tmp dir's wipe-on-reboot behavior suits them.
Set it up as root:
# 1. Create the managing group and add your admins. They also need
# permission to use Docker, since kith shells out to docker compose.
sudo groupadd kith
sudo usermod -aG kith alice
sudo usermod -aG kith bob
sudo usermod -aG docker alice
sudo usermod -aG docker bob
# 2. Create the directories, owned by the group. The setgid bit (2770)
# makes anything created inside inherit the kith group. The bin
# directory gets the same treatment, so any member can install or
# upgrade the shared binary.
sudo install -d -o root -g kith -m 2770 \
/var/lib/kith/servers /var/log/kith /var/backups/kith /var/cache/kith
sudo install -d -o root -g kith -m 2770 /usr/local/kith/binBe aware that docker group membership is effectively root on the host, since a docker socket lets you mount anything into a privileged container. Only add people you already trust as admins.
Then configure kith's environment. The variables live in their own file, locked down so only group members can read them. A profile snippet sources it for members only. This keeps the values out of world-readable profile files, which matters once backup credentials join the list.
# 3. Create the environment file, readable only by root and the kith group.
sudo install -d -o root -g kith -m 0750 /etc/kith
sudo install -m 0640 -o root -g kith /dev/null /etc/kith/envPut the configuration in /etc/kith/env:
# /etc/kith/env: kith configuration, sourced for kith group members only.
export KITH_SERVERS_DIR=/var/lib/kith/servers
export KITH_LOG_DIR=/var/log/kith
export KITH_CACHE_DIR=/var/cache/kith
export KITH_GID=$(getent group kith | cut -d: -f3)
export KITH_SECRET_FILE_MODE=660
export PATH="/usr/local/kith/bin:$PATH"Then add the sourcing logic to /etc/profile (or drop a file in /etc/profile.d/, for example /etc/profile.d/kith.sh):
# Only members of the kith group get kith's configuration.
if id -nG | grep -qw kith; then
. /etc/kith/env
fiNon-members can't read /etc/kith/env (mode 0640, group kith), so the paths and any credentials stay invisible to other users on the machine.
After that, any member can install or upgrade the shared binary by pointing the install script at the shared bin directory (writable by every group member, so no sudo needed):
curl -fsSL https://raw.githubusercontent.com/foopis23/kith/main/scripts/install.sh | KITH_INSTALL_DIR=/usr/local/kith/bin shMembers then log in again and run kith. The setgid directories keep new files in the kith group no matter who or which container creates them, and the group-read/write modes let any member manage any server. Each member's files are owned by their own uid (KITH_UID defaults to whoever is running), so world files show alice:kith, bob:kith, and so on.
One caveat: only one kith instance can manage a servers directory at a time. Kith takes a lock ($KITH_SERVERS_DIR/.kith.lock) at startup and refuses to start if another instance holds it, so two admins can't race each other on compose rewrites and port allocation. If kith crashes without releasing the lock, the next start detects the stale lock and reclaims it.
File ownership and permissions
Everything kith creates (server directories, compose files, logs) is owned by KITH_UID:KITH_GID, defaulting to the invoking user, and is group-accessible. Directories are 2770 (setgid, so the group propagates to anything created inside them later) and files are 0660. Files carrying secrets (compose files with backup credentials, password scratch files) are the exception: they default to 0600, owner-only, matching the per-user install model. On a global install, set KITH_SECRET_FILE_MODE to 660 (group read/write) or 640 (group read-only) so every member can manage every server.
The same ids are passed to the containers. The itzg/minecraft-server image re-maps its internal minecraft user to KITH_UID/KITH_GID at startup and chowns /data to match, and the backup sidecar runs as KITH_UID:KITH_GID directly. Files the containers write into the bind-mounted data and backup directories show up on the host with the same ownership as files kith writes itself, so there's no permission drift between the two.