commit 02ae0764a4a8826d8664f2ff23bf31c99cb7c23d Author: h Date: Tue Sep 8 19:15:15 2026 +0200 Initial template diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4befed3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +.idea diff --git a/README.md b/README.md new file mode 100644 index 0000000..f68ecef --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# caddy + +Copier template for a `caddy/` directory: a portable `site.caddy` for the project's `api` and `frontend` services, a standalone Caddy compose file and an example global Caddyfile. + +```sh +copier copy --trust -d project_slug=my_thing -d 'routes=["api","frontend"]' +copier update -a .copier-answers.caddy.yml +``` diff --git a/copier.yml b/copier.yml new file mode 100644 index 0000000..b47ebce --- /dev/null +++ b/copier.yml @@ -0,0 +1,20 @@ +_subdirectory: template +_templates_suffix: .jinja +_answers_file: .copier-answers.caddy.yml +_envops: + keep_trailing_newline: true + +project_slug: + type: str + help: Project slug, snake_case + +routes: + type: str + multiselect: true + help: Services to route + choices: + api: api + frontend: frontend + default: + - api + - frontend diff --git a/template/caddy/.env.example.jinja b/template/caddy/.env.example.jinja new file mode 100644 index 0000000..1246d17 --- /dev/null +++ b/template/caddy/.env.example.jinja @@ -0,0 +1,2 @@ +{{ project_slug | upper }}_DOMAIN={{ project_slug | replace('_', '-') }}.localhost +CLOUDFLARE_API_TOKEN= diff --git a/template/caddy/.gitignore b/template/caddy/.gitignore new file mode 100644 index 0000000..acd0b35 --- /dev/null +++ b/template/caddy/.gitignore @@ -0,0 +1,2 @@ +.env +Caddyfile diff --git a/template/caddy/Caddyfile.example b/template/caddy/Caddyfile.example new file mode 100644 index 0000000..d38e81e --- /dev/null +++ b/template/caddy/Caddyfile.example @@ -0,0 +1,14 @@ +{ + admin localhost:2019 + + log { + format console + } + + servers { + trusted_proxies cloudflare + client_ip_headers Cf-Connecting-Ip + } +} + +import /etc/caddy/projects.d/*/site.caddy diff --git a/template/caddy/README.md.jinja b/template/caddy/README.md.jinja new file mode 100644 index 0000000..2434b06 --- /dev/null +++ b/template/caddy/README.md.jinja @@ -0,0 +1,23 @@ +{%- set dash = project_slug | replace('_', '-') -%} +# caddy + +`site.caddy` routes `{{ '{$' }}{{ project_slug | upper }}_DOMAIN}` to the project's services over the external `caddy` docker network. It works standalone and inside a shared Caddy that fronts several projects. + +Standalone: + +```sh +docker network create caddy +cp Caddyfile.example Caddyfile +cp .env.example .env +docker compose up -d +``` + +Shared Caddy: mount this directory as `/etc/caddy/projects.d/{{ dash }}:ro`, keep `import /etc/caddy/projects.d/*/site.caddy` in its Caddyfile and put `{{ project_slug | upper }}_DOMAIN` in its environment. + +Rules that keep `site.caddy` portable: + +- the domain comes only from the environment variable +- no `tls` block, certificates are configured by the host Caddy (`acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}` in the global block) +- snippets are prefixed with the project slug because Caddy snippets are global +- mount the directory, not the file: a single-file bind mount goes stale when git replaces the inode +- keep `admin localhost:2019`, `admin off` makes `caddy reload` impossible diff --git a/template/caddy/docker-compose.yml.jinja b/template/caddy/docker-compose.yml.jinja new file mode 100644 index 0000000..723712e --- /dev/null +++ b/template/caddy/docker-compose.yml.jinja @@ -0,0 +1,24 @@ +{%- set dash = project_slug | replace('_', '-') -%} +services: + caddy: + image: ghcr.io/caddybuilds/caddy-cloudflare:latest + restart: unless-stopped + ports: + - "0.0.0.0:80:80" + - "0.0.0.0:443:443" + - "0.0.0.0:443:443/udp" + networks: + - caddy + env_file: + - .env + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - .:/etc/caddy/projects.d/{{ dash }}:ro + - caddy_data:/data + +networks: + caddy: + external: true + +volumes: + caddy_data: diff --git a/template/caddy/site.caddy.jinja b/template/caddy/site.caddy.jinja new file mode 100644 index 0000000..3f6fe79 --- /dev/null +++ b/template/caddy/site.caddy.jinja @@ -0,0 +1,17 @@ +{%- set dash = project_slug | replace('_', '-') -%} +{%- set domain = project_slug | upper ~ '_DOMAIN' -%} +{$ {{- domain -}} } { + encode zstd gzip +{% if 'api' in routes and 'frontend' in routes %} + handle /api/* { + reverse_proxy {{ dash }}-api:8080 + } + + handle { + reverse_proxy {{ dash }}-frontend:3000 + } +{% elif 'api' in routes %} + reverse_proxy {{ dash }}-api:8080 +{% else %} + reverse_proxy {{ dash }}-frontend:3000 +{% endif %}} diff --git a/template/{{ _copier_conf.answers_file }}.jinja b/template/{{ _copier_conf.answers_file }}.jinja new file mode 100644 index 0000000..d0f5d97 --- /dev/null +++ b/template/{{ _copier_conf.answers_file }}.jinja @@ -0,0 +1 @@ +{{ _copier_answers|to_nice_yaml -}}