Self-hosted Gitea on a single e2-small AlmaLinux 10 VM in us-east1, serving gitea.jasonmross.dev. Runtime is podman quadlets (systemd .container/.network/.volume units). Both images are built on Debian 13: Gitea from a GPG-verified release binary, and Caddy from an xcaddy build carrying the Google Cloud DNS provider (ACME DNS-01) and the Coraza WAF with the OWASP CRS embedded. Infrastructure is a Pulumi program in Go against a GCS state backend. Cloud Build handles CI: a push trigger for images, one for infra, and a weekly scheduled rebuild. Everything Cloud Build touches is 2nd gen. Notable design decisions, each documented where it lives: - Quadlets track a floating :prod tag. AutoUpdate=registry compares digests for a tag, so a digest-pinned image silently disables auto-updates. - Git transport and LFS bypass the WAF. With the bypass removed, a plain git push returns 403 -- packfiles trip CRS reliably. - gitea:wafMode drives both SecRuleEngine and whether the fail2ban jail acting on WAF verdicts exists. Banning on detections that were never blocks would turn a tuning false positive into an nftables ban. - fail2ban bans at the nftables prerouting hook. Published container ports are DNAT'd and never traverse INPUT, where the stock actions install their rules. - The DNS zone, backup bucket, and Gitea signing secrets are not Pulumi-owned, so pulumi destroy cannot take them with it. - The podman subnet is pinned because it is what Gitea's REVERSE_PROXY_TRUSTED_PROXIES names. Three update layers: dnf5-automatic for the OS, podman-auto-update with health-gated rollback for containers, and a weekly image rebuild that gives the second layer something to pull. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
28 lines
821 B
Plaintext
28 lines
821 B
Plaintext
# Installed by vm/bootstrap.sh as /etc/dnf/automatic.conf.
|
|
# Consumed by dnf5-automatic.timer (dnf-automatic.timer on a dnf4 system).
|
|
|
|
[commands]
|
|
# `default` follows the distro's notion of what an upgrade is; switch to
|
|
# `security` if you want to narrow the blast radius of unattended patching.
|
|
upgrade_type = default
|
|
|
|
# Spread the herd. Every AlmaLinux box on the internet firing at 03:30 is how
|
|
# mirrors fall over.
|
|
random_sleep = 3600
|
|
|
|
network_online_timeout = 60
|
|
download_updates = yes
|
|
apply_updates = yes
|
|
|
|
# Never reboot from here. gitea-reboot-window.timer owns that decision, so
|
|
# restarts land in a known window with a fresh disk snapshot behind them.
|
|
reboot = never
|
|
|
|
[emitters]
|
|
# stdio -> journald -> Cloud Logging. No mail configured on this host.
|
|
emit_via = stdio
|
|
system_name = gitea-vm
|
|
|
|
[base]
|
|
debuglevel = 1
|