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.

Drop the Terraform configurations; cloud-init is the deployment path

51624dc663738e70cc37b8ac5e232841fd49aaf8 · Keenan Nemetz · 3d ago · parent e21f6c5e

Drop the Terraform configurations; cloud-init is the deployment path

A Vole instance is one package, one configuration file, and one directory.
The first-boot file that installs the package and writes the configuration
is that same work described once, and it needs no provider, no state file,
and no cloud token with write access to an account. What Terraform did
outside the machine — firewall, address, DNS record — is three fields in a
console the operator already has open.

So the three configurations go, and with them what existed only to check
them: the pinned Terraform download in the verify image, the `fmt -check`
step, the .terraform entries in the ignore file, and the directory skip in
the link checker. The deployment check now needs python3 and PyYAML and
nothing else — no network, no credentials.

deploy/README.md keeps the reasoning, so the next person tempted to add a
provider back sees what it would buy and what it would cost.

The templates' header comments no longer name a renderer that is gone:
render.sh fills them in, and check-deploy.py renders them the same way.

deploy/README.md +27−21

@@ -5,36 +5,42 @@ The files here put a Vole instance on a fresh machine. The guides are in
5 5
6 6 | Path | What it is |
7 7 |---|---|
8 | `cloud-init/vole.yaml` | The first-boot file. Installs the package, writes the configuration, creates the instance admin, starts the service. Used by every path below |
9 | `cloud-init/runner.yaml` | The CI runner's first-boot file: rootless Podman, an unprivileged account, a pinned host key, and the unit enabled |
10 | `cloud-init/render.sh` | Renders either first-boot file for a machine you create by hand, with no Terraform |
11 | `terraform/hetzner`, `terraform/digitalocean`, `terraform/aws` | One complete Terraform configuration per cloud: machine, firewall, address, optional DNS record, and `-var runner=true` for a second machine that runs CI jobs. Each is used as a root module |
8 | `cloud-init/vole.yaml` | The forge's first-boot file. Installs the package, writes the configuration, creates the instance admin, starts the service |
9 | `cloud-init/runner.yaml` | The CI runner's first-boot file. Rootless Podman, an unprivileged account, a pinned host key, and the unit enabled |
10 | `cloud-init/render.sh` | Fills either file in from the environment, for pasting into a provider's user-data field |
12 11 | `docker/Dockerfile` | The server in one image, built from this repository |
13 12 | `docker/compose.yaml` | That image, with a volume for the instance and a health check |
14 13 | `docker/vole.toml` | The configuration the image starts with, and the file to copy for a deployment |
15 14 | `docker/entrypoint.sh` | Runs `vole admin init` once, then the command you asked for |
16 15
16 Cloud-init is the whole deployment story, and it is deliberately the only one.
17 Vole is one package, one configuration file, and one directory, so a first-boot
18 file that installs the package and writes the configuration is the same work a
19 Terraform configuration would describe — without a provider, a state file, an API
20 token with write access to the account, or a second thing to keep upgraded. What
21 Terraform would do outside the machine — a firewall, an address, a DNS record —
22 is three fields in the console you already have open.
23
24 Three things are deliberate:
25
26 - **One first-boot file per role.** The two templates differ in what they install
27 and which account they create, and nothing else. Neither has a copy of the
28 other's setup steps.
29 - **No machine images.** A golden image per cloud would need a publishing pipeline
30 and a per-cloud import, and it would go stale between releases. Installing a
31 `.deb` during first boot takes about thirty seconds and always installs the
32 version you named.
33 - **Nothing here runs twice.** First boot happens once; changing the instance
34 afterwards is editing `/etc/vole/vole.toml` and restarting, and upgrading is a
35 package install. See [Packages](../docs/packaging.md).
36
17 37 The runner's unit and its configuration example ship in the package, not here:
18 38 `packaging/systemd/vole-runner.service` and `packaging/runner.env`. One binary is
19 39 the forge, the runner, the CLI, and the library, so one package carries both
20 40 units.
21 41
22 Two things are deliberate:
23
24 - **The cloud-init file is the single source of truth.** The Terraform
25 configurations render it with `templatefile()`; `render.sh` renders it with
26 `envsubst`. Neither has its own copy of the setup steps, so a fix lands
27 everywhere at once.
28 - **There are no machine images.** A golden image per cloud would need a
29 publishing pipeline and a per-cloud import, and it would go stale between
30 releases. Installing a `.deb` during first boot takes about thirty seconds and
31 always installs the version you named.
32
33 Nothing here configures a machine after that first boot. Upgrades are a package
34 install on the machine, because a plan that replaces a server holding a team's
35 repositories is not a safe default. See [Packages](../docs/packaging.md).
36
37 42 `scripts/check-deploy.py` and the `deployment files` step in
38 43 `.vole/workflows/verify.yaml` check these files on every push: the YAML parses,
39 the template renders with no placeholder left behind, the boot script is valid
40 `sh`, and the Terraform is formatted.
44 each template renders with no placeholder left behind, the boot scripts are valid
45 `sh`, the container files name paths that exist, and the runner's template, unit,
46 and packaged example agree. It needs no cloud credentials and no network.