Infrastructure as Code of asxp.io https://asxp.io
  • Python 44.6%
  • Jinja 29.1%
  • HCL 26.3%
Find a file
Sergei Poljanski dc0f732d0f terraform: match the servers; open only ports in use
Checked against the Hetzner project:

 - Both servers have daily backups and delete and rebuild protection
   switched on, while the code said backups = false and set no
   protection. An apply would have turned all three off and discarded
   the existing backups. Record them as they are.

 - The firewalls had drifted both ways. Live, asxpio-ops also accepts
   XMPP ports and tcp/5000, and asxpio-services accepts 80, 443, XMPP,
   TURN, Valheim and Factorio ports; none of those services run. The
   code in turn missed tcp/25566, where the second Minecraft server
   listens.

Describe the firewalls as they should be: internal networks, plus the
public ports of services that run. Public SSH to asxpio-services goes
too; sshd and iptables already refuse it from outside the VPN.

This is not applied. The next plan will show the stale ports being
closed and nothing else.

Assisted-by: Claude:fable-5-2
2026-11-12 15:52:29 +04:00
.forgejo/workflows ci: deploy site.yaml from Forgejo Actions 2026-11-12 06:47:09 +04:00
ansible base: drop the one-off crontab migration 2026-11-12 07:01:07 +04:00
terraform terraform: match the servers; open only ports in use 2026-11-12 15:52:29 +04:00
.gitignore add wg: sanya 2026-04-05 19:17:18 +03:00
README.md docs: describe the servers as they are 2026-11-12 06:47:09 +04:00

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

  1. Edit the role or variable, for example an image tag in ansible/inventory/group_vars/ or a compose file under ansible/roles/<role>/files/.
  2. Push a branch. The pipeline does a dry run against production.
  3. 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 user deploy on 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.