anvilsign in

collin/anvil

RenderedSource

1# Deploying anvil on hagrid
2
3anvil runs as a single Docker container at `anvil.richardscollin.com`, fronted by
4hagrid's Caddy reverse proxy.
5
6- **Web** — anvil listens on `:3000` *inside* the container. Caddy (on the
7 `hagrid` Docker network) reverse-proxies to it and provides HTTPS via Let's
8 Encrypt. The web port is **not** published to the host.
9- **SSH** — Caddy only fronts HTTP(S), so anvil's SSH server is **published
10 directly to the host** on `:2222`. Git-over-SSH connects to
11 `anvil.richardscollin.com:2222`.
12- **Runtime** — fully self-contained: SQLite and the SSH crypto are compiled in,
13 and gix is pure-Rust. No git, OpenSSH, or system sqlite in the image.
14
15## 1. DNS
16
17Add an `A`/`AAAA` (or `CNAME` to hagrid) record:
18
19```
20anvil.richardscollin.com -> <hagrid's public IP>
21```
22
23This one record covers both the web (443, via Caddy) and SSH (2222, direct to
24the host).
25
26## 2. Caddy + index (in the hagrid repo)
27
28In `~/Code/hagrid/Caddyfile`, add:
29
30```
31anvil.richardscollin.com {
32 reverse_proxy anvil:3000
33}
34```
35
36In `~/Code/hagrid/sites.yaml`, add an entry:
37
38```yaml
39- name: anvil
40 host: anvil.richardscollin.com
41```
42
43Then reload Caddy (`./hagrid.sh reload`, or `./hagrid.sh deploy` to push to the
44host). Caddy resolves `anvil:3000` by container name over the `hagrid` network,
45so the anvil container must join that network (the run script does this).
46
47## 3. Build the image on your Mac, ship it to hagrid
48
49**Do not build on the VPS** — a release build needs ~2–4 GB peak and OOMs a
50cheap, swap-less droplet. Instead, cross-compile a static binary on your Mac
51(native speed, no QEMU) and copy it into a thin image.
52
53One-time toolchain setup:
54
55```sh
56brew install zig
57cargo install cargo-zigbuild
58rustup target add x86_64-unknown-linux-musl
59```
60
61Then, from a checkout of this repo on your Mac:
62
63```sh
64./deploy/build.sh
65```
66
67That:
681. `cargo zigbuild --release --target x86_64-unknown-linux-musl` — cross-compiles
69 a fully static `x86_64`-musl binary natively (~2 min, no emulation),
702. stages it at `deploy/anvild` and builds a thin image that just `COPY`s it in
71 (the `Dockerfile` does no compilation — fast),
723. ships it: `docker save | gzip | ssh hagrid 'docker load'`.
73
74The VPS never compiles anything.
75
76## 4. Run the container on hagrid
77
78On the hagrid host (only runs docker — no build):
79
80```sh
81./deploy/run.sh
82```
83
84which does:
85
86```sh
87docker run -d --name anvil --network hagrid --restart unless-stopped \
88 -p 2222:2222 \
89 -v anvil-data:/data \
90 anvil:latest
91```
92
93- `--network hagrid` — so Caddy can reach `anvil:3000`.
94- `-p 2222:2222` — publishes SSH to the host.
95- `-v anvil-data:/data` — a named volume holding the SQLite DB, the bare repos,
96 and the persistent SSH **host key**. Use a named volume (not a host bind
97 mount) so it's owned by the in-container `anvil` user.
98
99The baked config lives at `/etc/anvil/anvil.toml` (see `deploy/anvil.toml`).
100Override it by bind-mounting your own file over that path.
101
102> **If you must build on the VPS anyway** (not recommended): give it swap and
103> cap parallelism, or it will OOM —
104> ```sh
105> sudo fallocate -l 4G /swapfile && sudo chmod 600 /swapfile \
106> && sudo mkswap /swapfile && sudo swapon /swapfile # persist in /etc/fstab
107> # then build with CARGO_BUILD_JOBS=1 (slow, but survives 1 GB RAM)
108> ```
109
110## 5. First run: create your account, key, and the repo
111
112```sh
113# admin user
114docker exec anvil anvild -c /etc/anvil/anvil.toml \
115 user create collin --email you@example.com --password '<password>' --admin
116
117# your SSH public key (so you can push over SSH)
118docker exec -i anvil anvild -c /etc/anvil/anvil.toml \
119 user add-key collin --title laptop --key "$(cat ~/.ssh/id_ed25519.pub)"
120
121# the anvil repo itself
122docker exec anvil anvild -c /etc/anvil/anvil.toml repo create collin/anvil \
123 --description "a minimal git forge in Rust"
124```
125
126(You can also create the user, add keys, and create repos from the web UI once
127signed in — the CLI is just convenient for the first admin.)
128
129## 6. Self-host anvil on anvil
130
131From your local anvil checkout:
132
133```sh
134git remote add origin ssh://git@anvil.richardscollin.com:2222/collin/anvil.git
135git push -u origin main
136```
137
138Then browse it at `https://anvil.richardscollin.com/collin/anvil`. HTTPS clone
139also works: `git clone https://anvil.richardscollin.com/collin/anvil.git`
140(pushes over HTTPS require your account password as the git password).
141
142## Operations
143
144- **Update**: re-run `./deploy/run.sh` (rebuilds the image, recreates the
145 container; the `anvil-data` volume persists). NOTE: adding new DB tables in a
146 future version won't auto-apply to an existing database yet (Toasty migration
147 support is pending) — the git repos on disk are unaffected, but repo metadata
148 in SQLite may need recreating until migrations land.
149- **Backup**: snapshot the `anvil-data` volume, e.g.
150 `docker run --rm -v anvil-data:/data -v "$PWD":/out debian:bookworm-slim \
151 tar czf /out/anvil-data.tgz -C /data .`
152- **Logs**: `docker logs -f anvil`.
153- **System routes** live under `/-/` (e.g. sign in at
154 `https://anvil.richardscollin.com/-/login`); `/{username}` is the user/repo
155 namespace.