Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 21m ago 4b8c9e1d
source preview history blame raw 5.1 KiB
1# Vole on Hetzner Cloud
2
3One server, configured on its first boot by a file you paste into the console. No
4API token, nothing to install locally: a rendered file, a server, a firewall, and
5one DNS record.
6
7## What you need
8
9- A Hetzner Cloud account and a project.
10- A DNS name, such as `forge.example.com`.
11- Your SSH public key.
12- The URL of a Vole `.deb` for `amd64` — or `arm64` on a CAX server type — from
13 [Vole's releases](https://rodent.software/vole/releases).
14
15## Render the first-boot file
16
17From a clone of the Vole repository, on your own computer:
18
19```sh
20cd deploy/cloud-init
21
22HOSTNAME=forge.example.com \
23ADMIN_ACCOUNT=admin \
24ADMIN_EMAIL=you@example.com \
25ADMIN_SSH_KEY="$(cat ~/.ssh/id_ed25519.pub)" \
26VOLE_PACKAGE_URL=https://rodent.software/vole/releases/v0.2.0/artifacts/vole_0.2.0-1_amd64.deb \
27 ./render.sh > user-data.yaml
28```
29
30Open `user-data.yaml` before you paste it. It is the whole setup, and it is the
31last chance to notice a wrong account name: names are permanent.
32
33## Create the server
34
35In the Hetzner console, **Add Server**:
36
37| Field | What to choose |
38|---|---|
39| Location | wherever your team is |
40| Image | Ubuntu 24.04 (Debian 13 works too) |
41| Type | CX22 — 2 vCPU, 4 GB, 40 GB. Raise it if you expect big pushes or LFS |
42| Networking | public IPv4 |
43| SSH key | yours, the same one you put in the file |
44| Backups | on. It is a snapshot, not a backup of the instance — see [Backups](README.md#backups-are-still-your-job) |
45| **Cloud config** | paste the contents of `user-data.yaml` |
46
47Before or after creating it, add a **Firewall** in the same project and apply it to
48the server. Hetzner leaves a new server open if you do not:
49
50| Direction | Protocol | Port | Source |
51|---|---|---|---|
52| in | tcp | 22 | your team's addresses, for shell access |
53| in | tcp | 2222 | your team's addresses. This is Git over SSH, the write transport |
54| in | tcp | 80 | anywhere, for the certificate challenge |
55| in | tcp | 443 | anywhere |
56| out | any | any | anywhere, so the server can fetch its package and certificate |
57
58Restricting 2222 to your team is the cheapest way to keep the write side private.
59If you leave it open, Vole still authenticates every connection by key.
60
61## Point the name at it
62
63Create an `A` record for `forge.example.com` pointing at the server's IPv4
64address. Vole cannot obtain a certificate for a name that does not resolve to it
65yet, and it retries, so a record created a few minutes after the server is fine.
66
67## Check it works
68
69Open `https://forge.example.com`. The first request may take a few seconds while
70the certificate is issued; if it fails, wait a minute and reload.
71
72Issue a passkey for the admin account from your laptop:
73
74```sh
75vole admin --remote ssh://git@forge.example.com:2222 account invite admin
76```
77
78Then create a repository, and clone it:
79
80```sh
81vole admin --remote ssh://git@forge.example.com:2222 repo create demo --owner admin
82vole admin --remote ssh://git@forge.example.com:2222 acl set-default-role demo read
83git clone ssh://git@forge.example.com:2222/demo.git
84```
85
86## Adding a CI runner
87
88Create a second server the same way, but give it the runner's file:
89
90```sh
91./render.sh runner > runner-user-data.yaml # FORGE_HOST=forge.example.com and VOLE_PACKAGE_URL
92```
93
94It needs no inbound ports except 22. It needs one more step per repository before
95it takes work; see [CI runners](runner.md).
96
97## The settings worth changing
98
99The values you pass to `render.sh`. Everything else has a working default.
100
101| Variable | Default | What it does |
102|---|---|---|
103| `HOSTNAME` | required | The name the instance answers on. It becomes `public_url`, and passkeys bind to it |
104| `ADMIN_ACCOUNT` | required | The first account, made an instance admin. Permanent |
105| `ADMIN_SSH_KEY` | required | That account's public key |
106| `VOLE_PACKAGE_URL` | required | The `.deb` for the machine's architecture |
107| `ADMIN_EMAIL` | empty | Contact address for the certificate authority |
108| `INSTANCE_NAME` | `Vole` | What the landing page calls this forge |
109| `TLS_MODE` | `acme` | `proxy` leaves TLS to a proxy you run, and serves plain HTTP on loopback |
110
111## If something is not right
112
113- **The browser shows a certificate warning.** DNS is not answering with the
114 server's address yet, or ports 80 and 443 are blocked by the firewall. Check
115 `dig +short forge.example.com`, then `journalctl -u vole | grep -i acme`.
116- **The server is up but the forge is not.** First-boot output is in
117 `/var/log/vole-first-boot.log`; the service log is `journalctl -u vole`. The
118 usual cause is a package URL that 404s or is for the other architecture.
119- **Nothing listens on 2222.** The firewall is not applied to the server, or its
120 source addresses do not include where you are pushing from.
121- **Pushing is refused.** `vole whoami --remote ssh://git@forge.example.com:2222`
122 says which account your key is; if it is not the one you expected, the key was
123 not the one in `user-data.yaml`.
124
125## Next
126
127- [Deploying Vole](README.md) — what first boot does, backups, and upgrades
128- [Packages](../packaging.md) — the `.deb`, the unit file, and upgrades on the machine
129- [Accounts and access](../accounts-and-access.md) — the first accounts, roles, and passkeys
130