blumeops/ansible/roles/caddy/templates/Caddyfile.j2
Erich Blume 930f99d706 C1: migrate cv + docs from minikube to indri-native (deploy artifacts)
Replaces the cv and docs minikube Deployments with ansible roles that
download release tarballs into ~/cv/content and ~/docs/content on indri.
Caddy now serves those directories directly via a new kind=static
service-block in the Caddy template; no daemon, no nginx pod, no
ProxyGroup ingress on the request path.

This commit adds the deploy-side artifacts only. Live cutover (delete
argocd apps, run ansible, verify) is staged manually after PR review;
the dead containers/{cv,quartz} and argocd manifests are removed in a
follow-up commit so each commit is internally consistent.

Workflows are simplified: the deploy step now bumps the role's pinned
version and pushes; running ansible + purging the Fly cache is manual
from gilbert (matches the devpi pattern).

service-versions.yaml: cv and docs are type=ansible. docs current-version
remains 1.28.2 for now to keep container-version-check passing while
containers/quartz still exists; will move to the docs release tag in the
cleanup commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 14:33:57 -07:00

87 lines
2.5 KiB
Django/Jinja

# Caddy reverse proxy for blumeops services
# Managed by ansible - do not edit manually
#
# All *.{{ caddy_domain }} requests are proxied to backend services.
# TLS certificates are obtained via ACME DNS-01 challenge using Gandi.
{
# Global options
admin off
{% if caddy_tcp_services %}
# Layer 4 (TCP) routing
layer4 {
{% for tcp_svc in caddy_tcp_services %}
:{{ tcp_svc.port }} {
route {
proxy {{ tcp_svc.backend }}
}
}
{% endfor %}
}
{% endif %}
}
# Wildcard certificate for all services
*.{{ caddy_domain }}:{{ caddy_https_port }} {
tls {
dns gandi {env.GANDI_BEARER_TOKEN}
}
{% for service in caddy_services %}
@{{ service.name }} host {{ service.host }}
handle @{{ service.name }} {
{% if service.kind | default('proxy') == 'static' %}
root * {{ service.root }}
encode gzip
# Long-cache fingerprinted assets; everything else stays default.
@{{ service.name }}_assets path_regexp \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$
header @{{ service.name }}_assets Cache-Control "public, max-age=31536000, immutable"
{% for dl in service.download_paths | default([]) %}
@{{ service.name }}_dl{{ loop.index }} path {{ dl.path }}
header @{{ service.name }}_dl{{ loop.index }} Content-Disposition `attachment; filename="{{ dl.filename }}"`
{% endfor %}
{% if service.try_html | default(false) %}
try_files {path} {path}/ {path}.html
handle_errors 404 {
rewrite * /404.html
file_server
}
{% endif %}
file_server
{% else %}
{% if service.cache_policy | default('') == 'spa' %}
# SPA cache policy: hashed static assets are immutable, HTML must revalidate.
# Prevents stale HTML from referencing chunk hashes that no longer exist.
@{{ service.name }}_static path /static/dist/*
header @{{ service.name }}_static Cache-Control "public, max-age=31536000, immutable"
@{{ service.name }}_html path /if/*
header @{{ service.name }}_html Cache-Control "no-cache"
{% endif %}
{% if service.backend.startswith('https://') %}
reverse_proxy {{ service.backend }} {
# Caddy v2.11+ rewrites Host to upstream for HTTPS backends.
# Preserve the original Host so services see *.ops.eblu.me.
header_up Host {http.request.host}
}
{% else %}
reverse_proxy {{ service.backend }}
{% endif %}
{% endif %}
}
{% endfor %}
# Fallback for unknown hosts
handle {
respond "Unknown service" 404
}
}
# Base domain (ops.eblu.me)
{{ caddy_domain }}:{{ caddy_https_port }} {
tls {
dns gandi {env.GANDI_BEARER_TOKEN}
}
respond "blumeops services - use a subdomain (e.g., forge.{{ caddy_domain }})"
}