Files
ulicraft-server-v1/plan/02-caddy.md
Oier Bravo Urtasun 67d1f82e6a refactor!: unify into one compose, drop airgap/mirror/dnsmasq/avahi/blessingskin
Collapse the override-file matrix into a single docker-compose.yml holding the
whole stack: drasl, minecraft, mc-backup, caddy, nmsr, uptime-kuma. Bring-up is
now just `docker compose up -d --build`.

Removed features (dead-ends for this deployment):
- airgap / full asset mirror (mirror-airgap.sh, mirror-mods.sh, 20-mirror.caddy,
  caddy local-CA export, /ca.crt)
- dnsmasq + avahi/mDNS + dns-records.sh + mdns-host-setup.sh + ENABLE_MDNS;
  DNS is now handled outside this repo (HOST_LAN_IP dropped)
- Blessing Skin auth variant (compose + 00-core-blessingskin.caddy + BS_* env)
- host-nginx-static variant (docker-compose.nginx.yml, nginx/ulicraft.conf.tmpl)
- build-stack.sh and its run modes (online/airgap/core)

Production ingress is the only path now: host nginx terminates TLS (LE certs)
in front of caddy on a localhost-only port; caddy routes every vhost by Host
header. Launcher downloads move mirror/launcher -> launcher/.

Docs: README, deploy skill, and plan/ rewritten to match; obsolete plan docs
(dnsmasq, blessing-skin, assets-mirror, full-airgap-mirror, dns-and-run-modes)
deleted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 02:14:09 +02:00

2.6 KiB

Caddy — internal router behind host nginx

Summary

caddy is the stack's internal HTTP router, sitting behind the host's own nginx (which terminates TLS — see 15-letsencrypt.md). caddy is published only on a localhost-only port (CADDY_HTTP_BIND=127.0.0.1, CADDY_HTTP_PORT=8880); nginx reverse-proxies every public vhost to it by Host header. Roles:

  1. Reverse proxyauth. → drasl, avatar. → nmsr, status. → uptime-kuma.
  2. Staticpack. serves packwiz metadata; apex serves the landing page + /launcher/ downloads; distribution. serves a static site from another repo.

Image: caddy:alpine. caddy natively reads {$BASE_DOMAIN} env placeholders — no template render needed.

Network aliases (internal resolution)

caddy:
  networks:
    mcnet:
      aliases:
        - "auth.${BASE_DOMAIN}"
        - "pack.${BASE_DOMAIN}"

Lets minecraft reach http://auth.ulicraft.net/authlib-injector and http://pack.ulicraft.net/pack.toml from inside the stack.

conf.d snippets

The Caddyfile imports per-vhost snippets from caddy/conf.d/:

  • 00-core.caddyauth. → drasl, pack./srv/pack (browse).
  • 10-static.caddy — apex landing (/srv/www) + /launcher/* (/srv/launcher).
  • 30-distribution.caddydistribution./srv/distribution (static, from DISTRIBUTION_WEB_ROOT, another repo).
  • 40-avatar.caddyavatar.nmsr:8080 (skin/avatar renderer).
  • 50-status.caddystatus.uptime-kuma:3001 (status page).

All vhosts are plain http://…; TLS is nginx's job. Mounts: ./www:/srv/www:ro, ./pack:/srv/pack:ro, ./launcher:/srv/launcher:ro, ${DISTRIBUTION_WEB_ROOT}:/srv/distribution:ro.

Notes / risks

  • drasl behind a proxy: with matching BaseURL and nginx terminating TLS it is fine; nginx passes the original Host through to caddy → drasl.
  • file_server browse gives a directory listing — handy for debugging the pack.
  • /launcher/ is a sibling mount (/srv/launcher), not nested under the read-only /srv/www; handle_path strips the prefix.

Tasks

  • caddy service in docker-compose.yml, published 127.0.0.1:${CADDY_HTTP_PORT}:80
  • Pass BASE_DOMAIN + DISTRIBUTION_WEB_ROOT into caddy env
  • Caddyfile imports conf.d/*.caddy
  • Network aliases for auth. and pack. subdomains
  • Mount ./www, ./pack, ./launcher, ${DISTRIBUTION_WEB_ROOT}
  • Confirm reverse_proxy to drasl works (auth web UI loads, Yggdrasil API responds)
  • Verify internal alias resolution from minecraft container (curl pack.toml)
  • Verify nmsr + uptime-kuma proxied (avatar. renders, status. loads)