AMD FX-4130 desktop. Runs fanfiction.lol (an otwarchive deployment, the same codebase as AO3) plus a stack of legacy services and a duplicate leftover folk.zone stack.
LAN: 192.168.1.70. Public traffic arrives through a Cloudflare tunnel (cafe-cloudflared-1 container), so edge TLS is terminated by Cloudflare and the LAN Caddy listens on port 80 only.
~/caddy/Caddyfile - single :80 site block routing by Host header to Docker services. Managed with docker exec cafe-caddy-1 caddy reload –config /etc/caddy/Caddyfile.fanfiction-web - the Rails app, built by docker-compose from ~/cafe/fanfiction-lol/ (an otwarchive source snapshot, not a git repo) via ./config/docker/Dockerfile. Backed by fanfiction-db, fanfiction-es (Elasticsearch), fanfiction-redis, fanfiction-mc (memcached), and a fanfiction-worker sidekiq container.*.brennan.cafe.The fanfiction.lol route blocks the ActiveStorage direct-upload endpoint before it reaches Rails:
@fanfiction host fanfiction.lol
handle @fanfiction {
@direct_uploads path /rails/active_storage/direct_uploads*
respond @direct_uploads "Forbidden" 403
reverse_proxy fanfiction-web:3000 {
header_up X-Forwarded-Proto https
header_up X-Forwarded-Host {host}
header_up X-Forwarded-For {header.Cf-Connecting-Ip}
}
}
Direct uploads are a Rails feature for client-to-storage uploads, unused here. Blocking the endpoint at the proxy is recommended practice for public otwarchive deployments. Do not block all of /rails/active_storage/ - the disk service path under it serves locally stored files and must stay reachable.
Note the Cf-Connecting-Ip headers: because traffic comes through Cloudflare, real client IPs live in that header, and the banned-IP matcher at the top of the Caddyfile reads it rather than the connection's remote address.
Ruby was bumped to 3.4.12 on 2026-10-24 (AO3-7634 and AO3-7634 Dockerfile): changed the FROM line in config/docker/Dockerfile, ran docker compose build fanfiction-web fanfiction-worker fanfiction-sc, then recreated with docker rm -f + docker compose up -d –no-deps per container. Plain up -d fails with a name conflict because the containers predate compose's tracking. The app source is still a July 2026 snapshot; a future update means refreshing the source tree too, not just the Dockerfile.
Outbound mail for fanfiction.lol goes through Resend on a separate free account (one domain per free tier). The live config is in config/local.yml - SMTP_SERVER: smtp.resend.com, port 465, SMTP_USE_TLS: true, user resend, sender noreply@fanfiction.lol. The smtp_config.rb initializer prefers SMTP_* env vars when present, so stale SendGrid env vars were removed from the compose file to keep them from ever overriding local.yml.