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 AWS
One EC2 instance, configured on its first boot by a file you paste into the launch form. AWS differs from the other two guides in one way that matters: a new instance gets a security group that allows nothing inbound, so the security group is not optional here.
What you need
- An AWS account, and a region you are happy to keep a team’s forge in.
- A DNS name, such as
forge.example.com. - An SSH key pair. The public half goes in the launch form and in the file; keep the private half where you keep keys.
- The URL of a Vole
.debforamd64, 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 security group and the instance
In the EC2 console, Launch instance:
| Field | What to choose |
|---|---|
| Name | vole |
| AMI | Ubuntu Server 24.04 LTS (HVM), SSD Volume Type, published by Canonical |
| Instance type | t3.medium — 2 vCPU, 4 GB. Raise it if you expect big pushes or LFS |
| Key pair | yours — the same one you put in the file |
| Network | the default VPC’s public subnet, with auto-assign public IPv4 enabled. Without a public address the instance cannot fetch its package |
| Storage | 40 GiB gp3, encrypted |
| Advanced details → User data | paste the contents of user-data.yaml |
When it asks for a security group, create one with these inbound rules. Anything else can stay closed; the instance dials out for its package, its certificate, and updates:
| Type | Protocol | Port | Source |
|---|---|---|---|
| SSH | tcp | 22 | your team’s addresses, for shell access |
| Custom TCP | tcp | 2222 | your team’s addresses. This is Git over SSH, the write transport |
| HTTP | tcp | 80 | anywhere, for the certificate challenge |
| HTTPS | tcp | 443 | anywhere |
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.
An Elastic IP is optional. It keeps the address after a stop and start, which
saves one DNS edit. Both the host name in public_url and the SSH host key
clients pin survive an address change, so it is convenience rather than
correctness.
Point the name at it
Create an A record for forge.example.com pointing at the instance’s public IPv4
address — in Route 53, or wherever the name lives. 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 instance 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
Launch a second instance the same way, with a security group that allows only 22 from your addresses, and give it the runner’s file:
./render.sh runner > runner-user-data.yaml # FORGE_HOST=forge.example.com and VOLE_PACKAGE_URL
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
- Everything times out. The security group has no inbound rule for the port, or the instance has no public IPv4 address.
- The browser shows a certificate warning. DNS is not answering with the
instance’s address yet, or ports 80 and 443 are blocked. Check
dig +short forge.example.com, thenjournalctl -u vole | grep -i acme. - The instance is up but the forge is not. First-boot output is in
/var/log/vole-first-boot.log; the service log isjournalctl -u volethrough the EC2 console or Systems Manager. The usual cause is a package URL that 404s or is for the other architecture. - 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