82 lines
3.6 KiB
Markdown
82 lines
3.6 KiB
Markdown
---
|
|
name: dankclaw-expose-service
|
|
description: "Expose a new service at *.franksdank.cloud on Dankclaw: quadlet container, Caddy site block, Pi-hole LAN record (custom.list), Porkbun public record; includes API vhost/volume gotchas proven on the ntfy deploy."
|
|
---
|
|
|
|
# Expose a service on Dankclaw (franksdank.cloud)
|
|
|
|
Proven end-to-end 2026-10-11 with ntfy. Four layers, each with a known trap.
|
|
|
|
## 0. Survey first
|
|
```bash
|
|
ssh dankclaw 'ls ~/.config/containers/systemd/; grep -nE "franksdank|anubis" /home/dank/Containers/caddy/Caddyfile'
|
|
```
|
|
- Caddy runs as quadlet `caddy.container`; config at `/home/dank/Containers/caddy/Caddyfile` (NOT `/etc/caddy` on host).
|
|
- Sites fronted by `anubis:8923` get bot-challenge. **CLI/API publishers (ntfy, webhooks, git) MUST bypass anubis** — use the `gitea`/`ai` pattern (direct `reverse_proxy`).
|
|
|
|
## 1. Quadlet container (user `dank`)
|
|
Write `~/.config/containers/systemd/<name>.container`:
|
|
```ini
|
|
[Unit]
|
|
Description=<name>
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
[Container]
|
|
ContainerName=<name>
|
|
Image=<image>
|
|
Network=dankclaw_default
|
|
Volume=<name>-cache:/var/lib/<name>
|
|
[Service]
|
|
Restart=always
|
|
RestartSec=5
|
|
[Install]
|
|
WantedBy=default.target
|
|
```
|
|
`Network=dankclaw_default` ⇒ Caddy reaches it as `<name>:<port>`. Then `systemctl --user daemon-reload && systemctl --user start <name>.service`.
|
|
Rootless podman: bare containers never boot-persist — quadlet is mandatory.
|
|
|
|
## 2. Caddy route
|
|
Append site block (wildcard `*.franksdank.cloud` cert already issued via Porkbun DNS-01; no new cert work):
|
|
```
|
|
<name>.franksdank.cloud {
|
|
reverse_proxy <name>:<port>
|
|
}
|
|
```
|
|
Validate, then reload in-place:
|
|
```bash
|
|
podman exec caddy caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
|
|
podman exec caddy caddy reload --config /etc/caddy/Caddyfile
|
|
```
|
|
|
|
## 3. LAN DNS (Pi-hole v6, container `pihole`)
|
|
Traps, in order of wasted turns:
|
|
- API is NOT at `127.0.0.1:8083/api` on the host port path — requests need `-H "Host: pi.hole"`.
|
|
- `[[dns.records]]` in pihole.toml and `gravity.db` `domain` table: absent/unused on this box.
|
|
- **Working location:** `/home/dank/Containers/pihole/etc-pihole/hosts/custom.list` (dnsmasq addn-hosts), format `IP FQDN`:
|
|
```bash
|
|
echo "192.168.0.143 <name>.franksdank.cloud" | sudo tee -a /home/dank/Containers/pihole/etc-pihole/hosts/custom.list >/dev/null
|
|
podman exec pihole pihole restartdns
|
|
```
|
|
|
|
## 4. Public DNS (Porkbun, for off-LAN clients)
|
|
Keys: `/home/dank/Containers/caddy/env` (`PORKBUN_API_KEY`, `PORKBUN_API_SECRET_KEY`).
|
|
```bash
|
|
curl -sL -X POST https://api.porkbun.com/api/json3/domain/api/create/franksdank.cloud \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"apikey\":\"$K\",\"secretapi\":\"$S\",\"name\":\"<name>\",\"type\":\"A\",\"content\":\"71.227.227.172\",\"ttl\":\"600\"}"
|
|
```
|
|
- `json3` path + `-L` (302-redirects to v3). Public IP: 71.227.227.172 (router forwards 443 → Dankclaw).
|
|
- Porkbun edge throws intermittent 503s; retry later or do it in the UI. LAN path works without this record.
|
|
|
|
## 5. Verify
|
|
```bash
|
|
# from Dankclaw (host): getent hosts <name>.franksdank.cloud → 192.168.0.143
|
|
# workstation w/ stale resolved negative cache: use curl --resolve <name>.franksdank.cloud:443:192.168.0.143
|
|
# workstation's own `sudo resolvectl flush-caches` needs a terminal (no passwordless sudo) — negative cache expires on its own
|
|
curl -sf --resolve <name>.franksdank.cloud:443:192.168.0.143 https://<name>.franksdank.cloud/
|
|
```
|
|
Publish-style test for ntfy inside container (CLI flags vary by version; raw HTTP is version-proof):
|
|
```bash
|
|
podman exec <name> wget -qO- --header="Title: smoke" --post-data="up" http://localhost:<port>/<topic>
|
|
```
|