- Python 44.6%
- Jinja 29.1%
- HCL 26.3%
The Fabric server holds 8.8 GB of asxpio-services' 15 GB of RAM, more than everything else on the host together, and the XMPP server moves here next. Save the world, remove its containers and the daily restart timer. The world stays in /opt/minecraft-fossils/data, and in the restic backups, until it is deleted by hand. Assisted-by: Claude:opus-5-5 |
||
|---|---|---|
| .forgejo/workflows | ||
| ansible | ||
| terraform | ||
| .gitignore | ||
| README.md | ||
asxp.io infrastructure
Two Hetzner Cloud servers, described by Terraform and configured by
Ansible. Pushing to main deploys: a Forgejo Actions workflow applies
ansible/site.yaml to both servers.
Servers
| Server | Size | Runs |
|---|---|---|
| asxpio-ops | 2 vCPU, 4 GB | Traefik, Forgejo, crawler-alert; app stacks deployed from their own repos |
| asxpio-services | 8 vCPU, 16 GB | Forgejo Actions runner, Minecraft (Paper and Fossils), SillyTavern |
Both are reachable for administration only over WireGuard (10.20.0.0/24) or the private network (10.10.0.0/20).
Layout
terraform/: servers, network, firewalls, object storage buckets.ansible/site.yaml: everything the pipeline manages (see below).ansible/playbooks/: host bootstrap, run by hand..forgejo/workflows/deploy.yaml: the pipeline.
What the pipeline manages
| Role | Host | What |
|---|---|---|
base |
both | unattended upgrades, fail2ban, journal size cap, Docker daemon config, weekly image prune |
traefik |
ops | reverse proxy, TLS, dashboard on WireGuard |
crawler_alert |
ops | daily and weekly crawler report timers |
forgejo |
ops | Forgejo at git.asxp.io (git.czsk.it is an alias), SSH passthrough |
forgejo_runner |
services | Actions runner and its job labels |
sillytavern |
services | SillyTavern on the WireGuard address |
minecraft, minecraft_fossils |
services | game servers, daily restart timer |
Not managed here: the app stacks on asxpio-ops (asxpio, datacenterbingo,
blog, czsk, vhpbot, storage). Each deploys from its own repository into
/opt/<app>.
Changing something
- Edit the role or variable, for example an image tag in
ansible/inventory/group_vars/or a compose file underansible/roles/<role>/files/. - Push a branch. The pipeline does a dry run against production.
- Merge to
main. The pipeline dry-runs again, then applies.
A run with nothing to change touches nothing and restarts nothing.
Things to know:
- Game servers are saved and stopped with a 120 s timeout before a compose change recreates them.
- The runner executes the pipeline. A change to it recreates the container mid-job: the change lands, that job is reported as failed, and the next run confirms it.
- Forgejo restarts on a config change; the job log may pause for a few seconds.
To run it yourself, over WireGuard:
cd ansible
ansible-playbook site.yaml --check --diff # dry run
ansible-playbook site.yaml --tags traefik # one service
Secrets
Nothing secret is stored in the clear. Service secrets live in
ansible/inventory/group_vars/all/vault.yaml, encrypted with Ansible
Vault. The pipeline needs two repository secrets:
ANSIBLE_VAULT_PASSWORD: decrypts the vault.SSH_DEPLOY_KEY: private key of userdeployon both servers.
Locally, put the vault password in ansible/.vault_pass (ignored by git).
Bootstrap (by hand)
ansible/playbooks/ holds what is done once per host or can lock you
out: WireGuard, SSH hardening, s3fs mounts, swap, user creation.
See ansible/README.md.