- Go 92.9%
- HTML 5.5%
- CSS 1.6%
| cmd/hosty | ||
| internal | ||
| planning | ||
| CLAUDE.md | ||
| go.mod | ||
| go.sum | ||
| idea.md | ||
| README.md | ||
hosty
hosty is a self-hosted service management tool. It runs each service inside an Incus container, routes traffic through Caddy, and describes everything about a service — its image, ports, environment, and lifecycle scripts — in a single Basefile.
🏗️ Prerequisites
- Go 1.24+ — for building the binary inside hosty-dev
- Incus — hosty-dev must have Incus installed and running
- Caddy — two instances: one on the host (terminates TLS, proxies the hosty UI), one on hosty-dev (managed automatically by hosty for per-service routing)
- SQLite — no setup needed; hosty creates the database file on first run
⚙️ Config file
hosty reads a YAML config file on startup. Three fields are required:
domain: example.com
db: /var/lib/hosty/hosty.db
listen: :8090
domain— root domain for services (e.g. a service namedwikigets subdomainwiki.example.com)db— path to the SQLite database file (created automatically)listen— address hosty's HTTP server binds to
🔨 Building
hosty must be built inside the hosty-dev Incus VM, where the source is live-mounted at /home/hosty/src:
cd /home/hosty/src
go build -buildvcs=false -o /usr/local/bin/hosty ./cmd/hosty/
🚀 Running hosty
Start the web server:
hosty /etc/hosty.yaml serve
hosty binds to the listen address from the config and serves the management UI. Keep it running in the background (e.g. with nohup or a service manager).
📄 Writing a Basefile
A Basefile is a YAML file that lives in your service's source directory. It tells hosty everything it needs to deploy and run the service.
name: wiki
description: A simple markdown wiki
image: ubuntu:24.04
port: 3000
auth: none
data:
- data
env:
DOCS_DIR: /var/lib/hosty/wiki/data
PORT: "3000"
tasks:
install: |
apt-get update -y && apt-get install -y curl
curl -fsSL https://example.com/wiki-install.sh | sh
start: |
wiki --docs /var/lib/hosty/wiki/data --port 3000
Key fields:
| Field | Description |
|---|---|
name |
Unique service name. Also used as the container name and subdomain prefix. |
image |
Incus image reference (e.g. ubuntu:24.04). |
port |
Port the service listens on inside the container. |
auth |
Authentication mode. none means publicly accessible. |
data |
List of data subdirectories created under /var/lib/hosty/<name>/. |
env |
Environment variables injected into the container. |
tasks.install |
Shell commands run once at install time. |
tasks.start |
Shell commands run at start time (fire-and-forget via nohup). |
📦 Installing a service
Point hosty at a directory that contains a Basefile:
hosty /etc/hosty.yaml install /path/to/service
hosty will prompt you to confirm the subdomain (defaulting to <name>.<domain>), then:
- Create an Incus container from the specified image
- Inject environment variables
- Create data directories
- Run the
installtask - Add a Caddy route so the service is immediately reachable
- Run the
starttask in the background
▶️ Start, stop, destroy
After a service is installed, use these commands to manage its lifecycle:
hosty /etc/hosty.yaml start <name>
hosty /etc/hosty.yaml stop <name>
hosty /etc/hosty.yaml destroy <name>
- start — starts the container, waits for an IP, re-adds the Caddy route, and runs the
starttask - stop — gracefully stops the container and removes the Caddy route
- destroy — stops and deletes the container, removes the Caddy route, and deletes the DB record
⚠️
destroydoes not remove data directories under/var/lib/hosty/<name>/. Clean those up manually when you're sure you no longer need them.
🌐 Caddy setup
hosty uses two Caddy instances:
On the host (e.g. servo) — add two entries to your Caddyfile: one for the hosty management UI and one wildcard entry to forward all service subdomain traffic to hosty-dev's Caddy:
hosty.example.com {
reverse_proxy <hosty-dev-ip>:8090
}
*.hosty.example.com {
reverse_proxy <hosty-dev-ip>:8080
}
Replace <hosty-dev-ip> with hosty-dev's IP on your network. Port 8090 should match your hosty listen config. Port 8080 is where hosty-dev's Caddy instance listens for per-service routing.
⚠️ Both entries are required. Without the
*.hosty.example.comwildcard, service subdomain requests (e.g.https://marki.hosty.example.com) will not reach hosty-dev's Caddy and will show the wrong page.
On hosty-dev — you don't need to touch this. hosty drives Caddy's admin API (localhost:2019) automatically when services are installed, started, stopped, or destroyed. Per-service routing is fully managed.