Infrastructure as Code of asxp.io https://asxp.io
  • Jinja 44.9%
  • Python 35.9%
  • HCL 17.8%
  • Shell 1.4%
Find a file
Sergei Poljanski cf142c6e3b
Some checks failed
deploy / ansible (push) Failing after 59s
xmpp_cert_sync: push asxp.io and xmpp.asxp.io as separate files
Traefik never requests a certificate for a name it already holds one
for, so the XMPP certificate came out without asxp.io, the XMPP domain
itself. ejabberd picks the matching certificate file per domain, so push
two: the site's asxp.io certificate and xmpp.asxp.io + conference.
The receiver takes the name from the SSH command (asxp.io or a
subdomain), checks the certificate matches it, and retires the
hand-made server.pem once both files are in place.

A second probe on 5223 sends SNI asxp.io, as clients do, and the
outdated-certificate alert compares each served certificate with its
source in acme.json.

Assisted-by: Claude:opus-5-5
2026-11-13 08:26:08 +04:00
.forgejo/workflows Pin exact versions of images and actions 2026-11-13 02:46:54 +04:00
ansible xmpp_cert_sync: push asxp.io and xmpp.asxp.io as separate files 2026-11-13 08:26:08 +04:00
terraform firewall: open port 80 on asxpio-services 2026-11-13 07:58:09 +04:00
.gitignore add wg: sanya 2026-04-05 19:17:18 +03:00
README.md ansible: remove the retired Minecraft Fossils role 2026-11-12 23:24:09 +04:00
renovate.json Pin exact versions of images and actions 2026-11-13 02:46:54 +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), SillyTavern, XMPP (asxp.io), Xray

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 services Paper server
xmpp services ejabberd, apushd and tackygram for asxp.io; secrets stay in /opt/xmpp/state
xray services Xray VLESS Reality on :443, config in the vault
restic_backup both nightly restic backup to object storage, hel1 and fsn1

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.

Backups

Hetzner's server backups are off. Each host runs restic-backup.timer nightly (ops 03:30, services 04:15 UTC) into two independent repositories, restic/<host> in asxpio-backups-main (hel1) and asxpio-backups-repl (fsn1). Databases go in as dumps or online copies; see restic_pre_hooks in ansible/inventory/host_vars/. A failed run mails its journal.

restic-backup hel1 snapshots
restic-backup fsn1 restore latest --target /tmp/restore --include /opt/xmpp

The password is vault_restic_password in the vault. Keep a copy outside the servers and this repo's CI: without it nothing can be read.

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.