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.
Document deploying to a server, one guide per path
b8e7a5679847cf4cdda0754f6154805585e1be96 · Keenan Nemetz · 3d ago · parent b86148d1
Document deploying to a server, one guide per path The guides are for someone who has a machine and a domain and has not deployed a forge before, so they start from what to have ready and end at a working https address with one instance admin. Each ends with the same three things: where first-boot output is, what a certificate warning means, and that pushing needs port 2222 reachable and a key that belongs to an account. The index page carries what the per-cloud pages should not repeat: backups are separate from a cloud snapshot because secrets.key must not travel with the data, and upgrades are a package install rather than an apply.
docs/deploy/hetzner.md +92
| @@ -0,0 +1,92 @@ | |||
| 1 | # Vole on Hetzner Cloud | ||
| 2 | |||
| 3 | This creates one server, a firewall, and an SSH key, then turns the machine into | ||
| 4 | a Vole instance on first boot. It takes about three minutes and costs about the | ||
| 5 | price of a small server. | ||
| 6 | |||
| 7 | ## What you need | ||
| 8 | |||
| 9 | - A Hetzner Cloud project and an API token for it, with read and write access. | ||
| 10 | Put it in `HCLOUD_TOKEN`. | ||
| 11 | - A DNS name, such as `forge.example.com`. Either point it at the server address | ||
| 12 | the plan prints, or let Terraform create the record if the domain's DNS is a | ||
| 13 | zone in the same Hetzner project. | ||
| 14 | - Your SSH public key, and the URL of a Vole `.deb` for `amd64` (or `arm64` on a | ||
| 15 | CAX server type). | ||
| 16 | |||
| 17 | ## Deploy | ||
| 18 | |||
| 19 | From a clone of the Vole repository: | ||
| 20 | |||
| 21 | ```sh | ||
| 22 | export HCLOUD_TOKEN=... # your project token | ||
| 23 | cd deploy/terraform/hetzner | ||
| 24 | terraform init | ||
| 25 | |||
| 26 | terraform apply \ | ||
| 27 | -var domain=forge.example.com \ | ||
| 28 | -var "admin_ssh_public_key=$(cat ~/.ssh/id_ed25519.pub)" \ | ||
| 29 | -var vole_package_url=https://example.com/r/vole/releases/v0.2.0/artifacts/vole_0.2.0-1_amd64.deb | ||
| 30 | ``` | ||
| 31 | |||
| 32 | Type `yes` when it asks. Then, until DNS answers on the server's address, the | ||
| 33 | certificate cannot be issued; point `forge.example.com` at the `public_ipv4` | ||
| 34 | output (or pass `-var create_dns=true -var dns_zone=example.com` to have | ||
| 35 | Terraform do it). | ||
| 36 | |||
| 37 | ## What you get | ||
| 38 | |||
| 39 | ```text | ||
| 40 | web_url = "https://forge.example.com" | ||
| 41 | git_remote = "ssh://git@forge.example.com:2222/" | ||
| 42 | public_ipv4 = "203.0.113.10" | ||
| 43 | admin_account = "admin" | ||
| 44 | server_login = "ssh root@203.0.113.10" | ||
| 45 | ``` | ||
| 46 | |||
| 47 | Open `web_url` after DNS resolves. The account named by `admin_account` is an | ||
| 48 | instance admin and has no passkey yet; issue one from your laptop: | ||
| 49 | |||
| 50 | ```sh | ||
| 51 | vole admin --remote ssh://git@forge.example.com:2222 account invite admin | ||
| 52 | ``` | ||
| 53 | |||
| 54 | ## The settings worth changing | ||
| 55 | |||
| 56 | Everything else has a working default. Pass these with `-var` or in a | ||
| 57 | `terraform.tfvars` file beside `main.tf`. | ||
| 58 | |||
| 59 | | Setting | Default | What it does | | ||
| 60 | |---|---|---| | ||
| 61 | | `ssh_admin_cidrs` | everything | Addresses that may reach port 22 (server login) and 2222 (Git over SSH). Setting this to your team's range is the cheapest way to keep the write side private | | ||
| 62 | | `server_type` | `cx22` | 2 vCPU, 4 GB, 40 GB. `cx32` doubles the CPU and disk; `cax11` is the same size on arm64, and then the package URL must be the `arm64` one | | ||
| 63 | | `location` | `fsn1` | Falkenstein. `nbg1` Nuremberg, `hel1` Helsinki, `ash` Ashburn, `hil` Hillsboro, `sin` Singapore | | ||
| 64 | | `name` | `vole` | Name of the server, firewall, and SSH key in the project | | ||
| 65 | | `enable_backups` | `true` | Daily snapshots kept for a week, for about 20% of the server price | | ||
| 66 | | `tls_mode` | `acme` | `proxy` leaves TLS to a proxy you run instead, and serves plain HTTP on loopback | | ||
| 67 | | `instance_name` | `Vole` | What the landing page calls this forge | | ||
| 68 | | `protect_instance` | `true` | Refuses deletion and rebuild through the API, so `terraform destroy` needs `-var protect_instance=false` | | ||
| 69 | |||
| 70 | `terraform apply` is safe to run again: it reports no changes. It does not | ||
| 71 | upgrade Vole, and it does not touch the machine's data. | ||
| 72 | |||
| 73 | ## If something is not right | ||
| 74 | |||
| 75 | - **The browser shows a certificate warning.** DNS is not answering with the | ||
| 76 | server's address yet, or ports 80 and 443 are not reachable. Check with | ||
| 77 | `dig +short forge.example.com`, then look at `journalctl -u vole | grep -i acme` | ||
| 78 | on the machine. | ||
| 79 | - **The machine is up but the forge is not.** First-boot output is in | ||
| 80 | `/var/log/vole-first-boot.log`, and the service log is `journalctl -u vole`. | ||
| 81 | The usual cause is a package URL that 404s or is for the other architecture. | ||
| 82 | - **`terraform apply` fails on credentials.** `HCLOUD_TOKEN` is unset or the token | ||
| 83 | is read-only. | ||
| 84 | - **Pushing is refused.** Port 2222 has to be reachable from where you push, and | ||
| 85 | the key has to belong to an account. `vole whoami --remote | ||
| 86 | ssh://git@forge.example.com:2222` says which account your key is. | ||
| 87 | |||
| 88 | ## Next | ||
| 89 | |||
| 90 | - [Deploying Vole](README.md) — backups, upgrades, and what first boot does | ||
| 91 | - [Packages](../packaging.md) — the `.deb`, the unit file, and upgrades on the machine | ||
| 92 | - [Accounts and access](../accounts-and-access.md) — the first accounts, roles, and passkeys | ||