collin/anvil
RenderedSource
Deploying anvil on hagrid
anvil runs as a single Docker container at anvil.richardscollin.com, fronted by
hagrid's Caddy reverse proxy.
- Web — anvil listens on
:3000inside the container. Caddy (on thehagridDocker network) reverse-proxies to it and provides HTTPS via Let's Encrypt. The web port is not published to the host. - SSH — Caddy only fronts HTTP(S), so anvil's SSH server is published
directly to the host on
:2222. Git-over-SSH connects toanvil.richardscollin.com:2222. - Runtime — fully self-contained: SQLite and the SSH crypto are compiled in, and gix is pure-Rust. No git, OpenSSH, or system sqlite in the image.
1. DNS
Add an A/AAAA (or CNAME to hagrid) record:
anvil.richardscollin.com -> <hagrid's public IP>
This one record covers both the web (443, via Caddy) and SSH (2222, direct to the host).
2. Caddy + index (in the hagrid repo)
In ~/Code/hagrid/Caddyfile, add:
anvil.richardscollin.com {
reverse_proxy anvil:3000
}
In ~/Code/hagrid/sites.yaml, add an entry:
- name: anvil
host: anvil.richardscollin.com
Then reload Caddy (./hagrid.sh reload, or ./hagrid.sh deploy to push to the
host). Caddy resolves anvil:3000 by container name over the hagrid network,
so the anvil container must join that network (the run script does this).
3. Build & run the container
On the hagrid host, from a checkout of this repo:
./deploy/run.sh
That builds anvil:latest and runs:
docker run -d --name anvil --network hagrid --restart unless-stopped \
-p 2222:2222 \
-v anvil-data:/data \
anvil:latest
--network hagrid— so Caddy can reachanvil:3000.-p 2222:2222— publishes SSH to the host.-v anvil-data:/data— a named volume holding the SQLite DB, the bare repos, and the persistent SSH host key. Use a named volume (not a host bind mount) so it's owned by the in-containeranviluser.
The baked config lives at /etc/anvil/anvil.toml (see deploy/anvil.toml).
Override it by bind-mounting your own file over that path.
4. First run: create your account, key, and the repo
# admin user
docker exec anvil anvild -c /etc/anvil/anvil.toml \
user create collin --email you@example.com --password '<password>' --admin
# your SSH public key (so you can push over SSH)
docker exec -i anvil anvild -c /etc/anvil/anvil.toml \
user add-key collin --title laptop --key "$(cat ~/.ssh/id_ed25519.pub)"
# the anvil repo itself
docker exec anvil anvild -c /etc/anvil/anvil.toml repo create collin/anvil \
--description "a minimal git forge in Rust"
(You can also create the user, add keys, and create repos from the web UI once signed in — the CLI is just convenient for the first admin.)
5. Self-host anvil on anvil
From your local anvil checkout:
git remote add origin ssh://git@anvil.richardscollin.com:2222/collin/anvil.git
git push -u origin main
Then browse it at https://anvil.richardscollin.com/collin/anvil. HTTPS clone
also works: git clone https://anvil.richardscollin.com/collin/anvil.git
(pushes over HTTPS require your account password as the git password).
Operations
- Update: re-run
./deploy/run.sh(rebuilds the image, recreates the container; theanvil-datavolume persists). NOTE: adding new DB tables in a future version won't auto-apply to an existing database yet (Toasty migration support is pending) — the git repos on disk are unaffected, but repo metadata in SQLite may need recreating until migrations land. - Backup: snapshot the
anvil-datavolume, e.g.docker run --rm -v anvil-data:/data -v "$PWD":/out debian:bookworm-slim \ tar czf /out/anvil-data.tgz -C /data . - Logs:
docker logs -f anvil. - System routes live under
/-/(e.g. sign in athttps://anvil.richardscollin.com/-/login);/{username}is the user/repo namespace.