Deploy and remove a Once app at .local
Find a file
28allday 68c64265a6 once-add: deploy + remove Once apps at <name>.local
A small Go tool that wraps the `/etc/hosts` + `once deploy --disable-tls`
recipe needed to host a Once app on a single-label `<name>.local`. Adds
the hosts entry, runs the deploy, polls /up, and on removal cleans the
hosts line that the Once UI orphans (including the orphan-only case,
where the app is already gone).

Bubble Tea TUI for interactive use; non-interactive CLI for scripts.
Curated apps live in /etc/once-add/apps.toml; seeded with Writebook +
Campfire on first run.
2026-05-28 07:23:07 +01:00
cmd/once-add once-add: deploy + remove Once apps at <name>.local 2026-05-28 07:23:07 +01:00
internal once-add: deploy + remove Once apps at <name>.local 2026-05-28 07:23:07 +01:00
.gitignore once-add: deploy + remove Once apps at <name>.local 2026-05-28 07:23:07 +01:00
go.mod once-add: deploy + remove Once apps at <name>.local 2026-05-28 07:23:07 +01:00
go.sum once-add: deploy + remove Once apps at <name>.local 2026-05-28 07:23:07 +01:00
LICENSE once-add: deploy + remove Once apps at <name>.local 2026-05-28 07:23:07 +01:00
README.md once-add: deploy + remove Once apps at <name>.local 2026-05-28 07:23:07 +01:00

once-add

Deploy and remove a Once app at a single-label <name>.local host. Adds the right /etc/hosts entry, runs once deploy --disable-tls, and (on removal) cleans the /etc/hosts line that the Once UI orphans.

Why it's needed

Once's binary is statically linked (pure-Go resolver), so its post-deploy GET /up verify resolves /etc/hosts + real DNS only — never mDNS — and the TUI install forces TLS on (no Let's Encrypt cert for a private .local). Either one makes Once roll back the container. once-add runs the working recipe:

echo "127.0.0.1 <name>.local" | sudo tee -a /etc/hosts   # so Once's verify resolves it
once deploy <image> --host <name>.local --disable-tls    # TLS off; http verify passes

For other machines on your LAN to reach the site you'll also need an mDNS publisher running on the box (something that announces <name>.local over Avahi). Without one, the site is only reachable from the hosting box itself.

Install

curl -fsSL https://github.com/28allday/once-add/releases/latest/download/once-add \
  -o once-add && sudo install -m 755 once-add /usr/local/bin/once-add && rm once-add

Usage

Runs as root (it edits /etc/hosts); self-elevates with sudo if needed.

once-add                                   # interactive wizard (Add / Remove)
once-add book ghcr.io/basecamp/writebook   # add (non-interactive)
once-add remove book                       # remove app + its /etc/hosts entry
once-add remove book --data                # also delete the data volume (irreversible)
once-add remove                            # interactive remove picker

Add wizard: pick a curated app (or a custom image) → name it → deploy → reachable at http://<name>.local.

Remove runs once remove <host> and then clears the /etc/hosts line once-add added (the Once UI removes the container but orphans that line). Data volumes are kept by default — pass --data (or tick the toggle in the wizard) to delete them too. If the app is already gone, the orphaned /etc/hosts line is cleaned up anyway.

Curated apps

Edit /etc/once-add/apps.toml to add entries — no rebuild needed. once-add seeds a default file (Writebook, Campfire) on first run if it's missing.

[[app]]
name        = "Writebook"
image       = "ghcr.io/basecamp/writebook"
description = "Books & documentation"
suggested   = "book"

Build

CGO_ENABLED=0 go build -o once-add ./cmd/once-add   # static binary
go test ./...

Licence

MIT.