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.
Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys
keenan · 17m ago
4b8c9e1d
| 1 | # Vole on AWS |
| 2 | |
| 3 | One EC2 instance, configured on its first boot by a file you paste into the launch |
| 4 | form. AWS differs from the other two guides in one way that matters: a new |
| 5 | instance gets a security group that allows nothing inbound, so the security group |
| 6 | is not optional here. |
| 7 | |
| 8 | ## What you need |
| 9 | |
| 10 | - An AWS account, and a region you are happy to keep a team's forge in. |
| 11 | - A DNS name, such as `forge.example.com`. |
| 12 | - An SSH key pair. The public half goes in the launch form and in the file; keep |
| 13 | the private half where you keep keys. |
| 14 | - The URL of a Vole `.deb` for `amd64`, from |
| 15 | [Vole's releases](https://rodent.software/vole/releases). |
| 16 | |
| 17 | ## Render the first-boot file |
| 18 | |
| 19 | From a clone of the Vole repository, on your own computer: |
| 20 | |
| 21 | ```sh |
| 22 | cd deploy/cloud-init |
| 23 | |
| 24 | HOSTNAME=forge.example.com \ |
| 25 | ADMIN_ACCOUNT=admin \ |
| 26 | ADMIN_EMAIL=you@example.com \ |
| 27 | ADMIN_SSH_KEY="$(cat ~/.ssh/id_ed25519.pub)" \ |
| 28 | VOLE_PACKAGE_URL=https://rodent.software/vole/releases/v0.2.0/artifacts/vole_0.2.0-1_amd64.deb \ |
| 29 | ./render.sh > user-data.yaml |
| 30 | ``` |
| 31 | |
| 32 | Open `user-data.yaml` before you paste it. It is the whole setup, and it is the |
| 33 | last chance to notice a wrong account name: names are permanent. |
| 34 | |
| 35 | ## Create the security group and the instance |
| 36 | |
| 37 | In the EC2 console, **Launch instance**: |
| 38 | |
| 39 | | Field | What to choose | |
| 40 | |---|---| |
| 41 | | Name | `vole` | |
| 42 | | AMI | Ubuntu Server 24.04 LTS (HVM), SSD Volume Type, published by Canonical | |
| 43 | | Instance type | `t3.medium` — 2 vCPU, 4 GB. Raise it if you expect big pushes or LFS | |
| 44 | | Key pair | yours — the same one you put in the file | |
| 45 | | Network | the default VPC's public subnet, with **auto-assign public IPv4** enabled. Without a public address the instance cannot fetch its package | |
| 46 | | Storage | 40 GiB gp3, encrypted | |
| 47 | | **Advanced details → User data** | paste the contents of `user-data.yaml` | |
| 48 | |
| 49 | When it asks for a security group, create one with these inbound rules. Anything |
| 50 | else can stay closed; the instance dials out for its package, its certificate, and |
| 51 | updates: |
| 52 | |
| 53 | | Type | Protocol | Port | Source | |
| 54 | |---|---|---|---| |
| 55 | | SSH | tcp | 22 | your team's addresses, for shell access | |
| 56 | | Custom TCP | tcp | 2222 | your team's addresses. This is Git over SSH, the write transport | |
| 57 | | HTTP | tcp | 80 | anywhere, for the certificate challenge | |
| 58 | | HTTPS | tcp | 443 | anywhere | |
| 59 | |
| 60 | Restricting 2222 to your team is the cheapest way to keep the write side private. |
| 61 | If you leave it open, Vole still authenticates every connection by key. |
| 62 | |
| 63 | An Elastic IP is optional. It keeps the address after a stop and start, which |
| 64 | saves one DNS edit. Both the host name in `public_url` and the SSH host key |
| 65 | clients pin survive an address change, so it is convenience rather than |
| 66 | correctness. |
| 67 | |
| 68 | ## Point the name at it |
| 69 | |
| 70 | Create an `A` record for `forge.example.com` pointing at the instance's public IPv4 |
| 71 | address — in Route 53, or wherever the name lives. Vole cannot obtain a |
| 72 | certificate for a name that does not resolve to it yet, and it retries, so a record |
| 73 | created a few minutes after the instance is fine. |
| 74 | |
| 75 | ## Check it works |
| 76 | |
| 77 | Open `https://forge.example.com`. The first request may take a few seconds while |
| 78 | the certificate is issued; if it fails, wait a minute and reload. |
| 79 | |
| 80 | Issue a passkey for the admin account from your laptop: |
| 81 | |
| 82 | ```sh |
| 83 | vole admin --remote ssh://git@forge.example.com:2222 account invite admin |
| 84 | ``` |
| 85 | |
| 86 | Then create a repository, and clone it: |
| 87 | |
| 88 | ```sh |
| 89 | vole admin --remote ssh://git@forge.example.com:2222 repo create demo --owner admin |
| 90 | vole admin --remote ssh://git@forge.example.com:2222 acl set-default-role demo read |
| 91 | git clone ssh://git@forge.example.com:2222/demo.git |
| 92 | ``` |
| 93 | |
| 94 | ## Adding a CI runner |
| 95 | |
| 96 | Launch a second instance the same way, with a security group that allows only 22 |
| 97 | from your addresses, and give it the runner's file: |
| 98 | |
| 99 | ```sh |
| 100 | ./render.sh runner > runner-user-data.yaml # FORGE_HOST=forge.example.com and VOLE_PACKAGE_URL |
| 101 | ``` |
| 102 | |
| 103 | It needs one more step per repository before it takes work; see |
| 104 | [CI runners](runner.md). |
| 105 | |
| 106 | ## The settings worth changing |
| 107 | |
| 108 | The values you pass to `render.sh`. Everything else has a working default. |
| 109 | |
| 110 | | Variable | Default | What it does | |
| 111 | |---|---|---| |
| 112 | | `HOSTNAME` | required | The name the instance answers on. It becomes `public_url`, and passkeys bind to it | |
| 113 | | `ADMIN_ACCOUNT` | required | The first account, made an instance admin. Permanent | |
| 114 | | `ADMIN_SSH_KEY` | required | That account's public key | |
| 115 | | `VOLE_PACKAGE_URL` | required | The `.deb` for the machine's architecture | |
| 116 | | `ADMIN_EMAIL` | empty | Contact address for the certificate authority | |
| 117 | | `INSTANCE_NAME` | `Vole` | What the landing page calls this forge | |
| 118 | | `TLS_MODE` | `acme` | `proxy` leaves TLS to a proxy you run, and serves plain HTTP on loopback | |
| 119 | |
| 120 | ## If something is not right |
| 121 | |
| 122 | - **Everything times out.** The security group has no inbound rule for the port, |
| 123 | or the instance has no public IPv4 address. |
| 124 | - **The browser shows a certificate warning.** DNS is not answering with the |
| 125 | instance's address yet, or ports 80 and 443 are blocked. Check |
| 126 | `dig +short forge.example.com`, then `journalctl -u vole | grep -i acme`. |
| 127 | - **The instance is up but the forge is not.** First-boot output is in |
| 128 | `/var/log/vole-first-boot.log`; the service log is `journalctl -u vole` through |
| 129 | the EC2 console or Systems Manager. The usual cause is a package URL that 404s |
| 130 | or is for the other architecture. |
| 131 | - **Pushing is refused.** `vole whoami --remote ssh://git@forge.example.com:2222` |
| 132 | says which account your key is; if it is not the one you expected, the key was |
| 133 | not the one in `user-data.yaml`. |
| 134 | |
| 135 | ## Next |
| 136 | |
| 137 | - [Deploying Vole](README.md) — what first boot does, backups, and upgrades |
| 138 | - [Packages](../packaging.md) — the `.deb`, the unit file, and upgrades on the machine |
| 139 | - [Accounts and access](../accounts-and-access.md) — the first accounts, roles, and passkeys |
| 140 |