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.
Vole on Hetzner Cloud
One server, configured on its first boot by a file you paste into the console. No API token, nothing to install locally: a rendered file, a server, a firewall, and one DNS record.
What you need
- A Hetzner Cloud account and a project.
- A DNS name, such as
forge.example.com. - Your SSH public key.
- The URL of a Vole
.debforamd64— orarm64on a CAX server type — from Vole’s releases.
Render the first-boot file
From a clone of the Vole repository, on your own computer:
cd deploy/cloud-init
HOSTNAME=forge.example.com \
ADMIN_ACCOUNT=admin \
ADMIN_EMAIL=you@example.com \
ADMIN_SSH_KEY="$(cat ~/.ssh/id_ed25519.pub)" \
VOLE_PACKAGE_URL=https://rodent.software/vole/releases/v0.2.0/artifacts/vole_0.2.0-1_amd64.deb \
./render.sh > user-data.yaml
Open user-data.yaml before you paste it. It is the whole setup, and it is the
last chance to notice a wrong account name: names are permanent.
Create the server
In the Hetzner console, Add Server:
| Field | What to choose |
|---|---|
| Location | wherever your team is |
| Image | Ubuntu 24.04 (Debian 13 works too) |
| Type | CX22 — 2 vCPU, 4 GB, 40 GB. Raise it if you expect big pushes or LFS |
| Networking | public IPv4 |
| SSH key | yours, the same one you put in the file |
| Backups | on. It is a snapshot, not a backup of the instance — see Backups |
| Cloud config | paste the contents of user-data.yaml |
Before or after creating it, add a Firewall in the same project and apply it to the server. Hetzner leaves a new server open if you do not:
| Direction | Protocol | Port | Source |
|---|---|---|---|
| in | tcp | 22 | your team’s addresses, for shell access |
| in | tcp | 2222 | your team’s addresses. This is Git over SSH, the write transport |
| in | tcp | 80 | anywhere, for the certificate challenge |
| in | tcp | 443 | anywhere |
| out | any | any | anywhere, so the server can fetch its package and certificate |
Restricting 2222 to your team is the cheapest way to keep the write side private. If you leave it open, Vole still authenticates every connection by key.
Point the name at it
Create an A record for forge.example.com pointing at the server’s IPv4
address. Vole cannot obtain a certificate for a name that does not resolve to it
yet, and it retries, so a record created a few minutes after the server is fine.
Check it works
Open https://forge.example.com. The first request may take a few seconds while
the certificate is issued; if it fails, wait a minute and reload.
Issue a passkey for the admin account from your laptop:
vole admin --remote ssh://git@forge.example.com:2222 account invite admin
Then create a repository, and clone it:
vole admin --remote ssh://git@forge.example.com:2222 repo create demo --owner admin
vole admin --remote ssh://git@forge.example.com:2222 acl set-default-role demo read
git clone ssh://git@forge.example.com:2222/demo.git
Adding a CI runner
Create a second server the same way, but give it the runner’s file:
./render.sh runner > runner-user-data.yaml # FORGE_HOST=forge.example.com and VOLE_PACKAGE_URL
It needs no inbound ports except 22. It needs one more step per repository before it takes work; see CI runners.
The settings worth changing
The values you pass to render.sh. Everything else has a working default.
| Variable | Default | What it does |
|---|---|---|
HOSTNAME |
required | The name the instance answers on. It becomes public_url, and passkeys bind to it |
ADMIN_ACCOUNT |
required | The first account, made an instance admin. Permanent |
ADMIN_SSH_KEY |
required | That account’s public key |
VOLE_PACKAGE_URL |
required | The .deb for the machine’s architecture |
ADMIN_EMAIL |
empty | Contact address for the certificate authority |
INSTANCE_NAME |
Vole |
What the landing page calls this forge |
TLS_MODE |
acme |
proxy leaves TLS to a proxy you run, and serves plain HTTP on loopback |
If something is not right
- The browser shows a certificate warning. DNS is not answering with the
server’s address yet, or ports 80 and 443 are blocked by the firewall. Check
dig +short forge.example.com, thenjournalctl -u vole | grep -i acme. - The server is up but the forge is not. First-boot output is in
/var/log/vole-first-boot.log; the service log isjournalctl -u vole. The usual cause is a package URL that 404s or is for the other architecture. - Nothing listens on 2222. The firewall is not applied to the server, or its source addresses do not include where you are pushing from.
- Pushing is refused.
vole whoami --remote ssh://git@forge.example.com:2222says which account your key is; if it is not the one you expected, the key was not the one inuser-data.yaml.
Next
- Deploying Vole — what first boot does, backups, and upgrades
- Packages — the
.deb, the unit file, and upgrades on the machine - Accounts and access — the first accounts, roles, and passkeys