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>
2.8 KiB
TLS — Let's Encrypt (OVH DNS-01) + host nginx ingress
Production serves real, publicly-trusted certs via the host's own nginx.
nginx terminates TLS and reverse-proxies every vhost to the caddy container
(published localhost-only on ${CADDY_HTTP_PORT}) by Host header. caddy is the
internal router; nginx is the public TLS terminator. This is the only ingress
path — there are no compose overrides.
Certs: one per name, via OVH DNS-01
tooling/issue-letsencrypt.sh issues a separate cert for the apex and each
subdomain (${BASE_DOMAIN}, auth.…, pack.…, plus the rest of
LE_SUBDOMAINS) using acme.sh + the OVH DNS-01 challenge — no inbound ports
needed, so it works regardless of public reachability.
One-time setup
- Install acme.sh:
curl https://get.acme.sh | sh -s email=you@example.com - OVH API credentials →
.env(see.env.exampleOVH_*,LE_EMAIL,LE_SUBDOMAINS). Create a token at https://eu.api.ovh.com/createToken/ withGET/POST/PUT/DELETEon/domain/zone/*, or leaveOVH_CKblank and run the script once — acme.sh prints an authorization URL; visit it, then paste the consumer key intoOVH_CKand re-run.
Issue
tooling/issue-letsencrypt.sh # real certs
LE_STAGING=1 tooling/issue-letsencrypt.sh # dry run against the LE staging CA
Certs install to certs/<name>/{cert.pem,key.pem} (gitignored). acme.sh adds a
cron for renewal; --reloadcmd reloads nginx automatically on renew. Override
the reload with RELOAD_CMD=... if nginx isn't systemd-managed.
nginx
- Bring the stack up (caddy publishes on
127.0.0.1:${CADDY_HTTP_PORT}):docker compose up -d --build - Render + install the vhost (
nginx/ulicraft-caddy.conf.tmpl):This substitutestooling/render-nginx.sh --install$BASE_DOMAIN $APP_DIR $CADDY_HTTP_PORT, writes tosites-available, symlinkssites-enabled, runsnginx -t, and reloads nginx. nginx then terminates TLS per vhost (certs under$APP_DIR/certs) and reverse-proxies each Host to caddy on127.0.0.1:${CADDY_HTTP_PORT}. Ensurewww-datacan read$APP_DIR/certs.
DNS
Point the public DNS for ${BASE_DOMAIN} and every subdomain
(auth. pack. avatar. status. distribution. www.) at the host running
nginx. DNS is configured outside this repo. The Minecraft container reaches
the stack's own names via caddy's mcnet aliases, not public DNS.
Why DNS-01 (not HTTP-01)
DNS-01 only needs the OVH API, so it issues regardless of public reachability —
and ulicraft.net is a real OVH-managed domain, which LE requires (it never
issues for .lan/.local). HTTP-01/TLS-ALPN-01 would need public inbound on
80/443. The JVM trusts the resulting Let's Encrypt certs with no CA import.