fkzys/infra

Infrastructure-as-code for a personal server stack and home network.

★ 1Forks 0PythonGitHub ↗Compare
elementgrafanajinja2mirotalknextcloudpodmanprometheuspythonquadletsing-boxsopssynapsetraefik

README

infra

CI License Spec

Infrastructure-as-code for a personal server stack and home network. Podman Quadlet configs, OpenWrt router configs, service configs and secrets — all templated, versioned and deployed over SSH or distributed via Cloudflare Workers KV.

Docs

Stack

Service What
system Base OS hardening: sshd (TCP forwarding allowed), sysctl, systemd-networkd, maintenance timers (btrfs scrub, paccache, sysctl reapply)
firewall Firewalld zones: public, wireguard, filter-closed, trusted — ports opened per-instance from secrets
backup Kopia snapshots to S3 with btrfs atomic snapshots, systemd timer
certs Centralized wildcard TLS certificates (Google ACME + Cloudflare DNS challenge via lego)
traefik Reverse proxy, TLS termination
synapse Matrix homeserver + PostgreSQL
element Element Web + Synapse Admin
element-call Element Call via LiveKit SFU + lk-jwt-service
nextcloud Nextcloud + MariaDB + Valkey + Nginx + cron timer
metrics Prometheus + Node Exporter + Grafana
i2p I2P anonymous overlay network (i2pd daemon, native, no container)
mirotalk MiroTalk SFU — WebRTC video conferencing (Podman container)
wireguard WireGuard mesh + client tunnels (native, no container)
sing-box Proxy server + client/router config generator with Cloudflare KV distribution
router OpenWrt router configs: nftables tproxy, network, wireless, firewall, dhcp — distributed via KV

Structure

infra/
...
├── certs/
│   ├── deploy.py
│   ├── secrets/
│   └── .certstore/              ← gitignored, lego state + certs
...
├── sing-box/
│   ├── deploy.py              ← server deploy (render/diff/deploy)
│   ├── generate.py            ← client/router config generator + KV
│   ├── templates/
│   ├── secrets/
│   └── .output/               ← gitignored
...
└── router/
    ├── generate.py            ← OpenWrt config generator + KV
    ├── templates/
    ├── secrets/
    └── .output/               ← gitignored

How it works

Each service has:

  • templates/ — Jinja2 templates for Quadlet units, configs or scripts
  • secrets/ — SOPS-encrypted YAML with passwords, domains, keys
  • deploy.py — thin config that plugs into lib/deploy.py

Server deploy flow

  1. Decrypts secrets with SOPS
  2. Resolves SSH target from secrets/hosts.enc.yaml
  3. Renders Jinja2 templates
  4. Syncs files to remote via rsync (checksum-based, idempotent)
  5. Applies file ownership/permissions if specified (owner, mode in file config)
  6. Restarts systemd units only if something changed (restart command can be a static string or a callable for dynamic commands)

Router/client config flow

sing-box/generate.py and router/generate.py use a different delivery model — configs are rendered locally, uploaded to Cloudflare Workers KV, and pulled by devices over HTTPS:

sops decrypt → jinja render → KV upload → device wget/curl

This avoids SSH to constrained devices (OpenWrt routers, phones) while keeping configs versioned and secrets encrypted at rest.

Secrets

All secrets are SOPS-encrypted.

SSH connection info (shared by all services) is stored at:

sops secrets/hosts.enc.yaml

And service secrets are stored at:

.../secrets/secrets.enc.yaml

Prerequisites

  • Python 3.10+
  • pip install jinja2 pyyaml requests
  • SOPS configured with your age key
  • SSH access to target hosts
  • rsync
  • lego (for certs/)
  • dnsproxy (for certs/)

License

Fork-Parity-1.0.1

Contributors

fkzys

Issues