Files

67 lines
3.4 KiB
Markdown

---
name: traefik-expose
description: Make a service on the tachi host reachable at https://NAME.i.grosinger.net through Traefik, or remove it. Use when asked to expose, register, route, or publish a dev server, host process, or docker container on an internal hostname, or to unexpose/deregister one.
---
> [!info] This skill only applies to the host `voyager`
> This skill may be synced to other hosts, but it only applies to `voyager`. If you are running on a different host and need to expose a service to an internal or external hostname, stop and ask for guidance from the user.
# traefik-expose — put a service on NAME.i.grosinger.net
Traefik on this host (`/tachi/docker`) serves `*.i.grosinger.net` on its `internal` entrypoint. That entrypoint is the default and already carries the wildcard TLS certificate and the home-only IP allowlist, and Blocky resolves every `*.i.grosinger.net` name to this host. So registering a service is only a routing rule and a target; there is no DNS, certificate, or middleware step.
Pick the branch by where the service runs.
## Host process (not in a container)
Register with the script in the docker repo:
```sh
/tachi/docker/traefik/expose NAME PORT # https://NAME.i.grosinger.net -> host:PORT
/tachi/docker/traefik/expose -l # list
/tachi/docker/traefik/expose -d NAME # remove
```
It writes one file to `traefik/dynamic.d/`, which Traefik watches, so the route is live within seconds and needs no restart. The file is gitignored.
The process must listen on `0.0.0.0` or on `172.19.0.1`. Traefik connects from inside its container network and cannot reach a listener bound to `127.0.0.1`, which most dev servers default to; pass the tool's host flag (`--host 0.0.0.0` or equivalent).
## Docker container
Join the container to the `docker_traefik` network and add labels. From a compose file outside `/tachi/docker`, declare the network as external:
```yaml
services:
myapp:
networks: [traefik]
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`myapp.i.grosinger.net`)"
- "traefik.http.services.myapp.loadbalancer.server.port=3000"
networks:
traefik:
external: true
name: docker_traefik
```
Inside `/tachi/docker/docker-compose.yaml` the network is simply `traefik`. Traefik ignores containers without `traefik.enable=true`. Always set the port label: it is the port the app listens on inside the container, not a published one, and no `ports:` mapping is needed. Removing the labels or the container removes the route.
## Verify
```sh
curl -sk -o /dev/null -w '%{http_code}\n' https://NAME.i.grosinger.net/
```
- **200-ish**: done.
- **502**: Traefik has the route but cannot reach the target. Host process: check the bind address with `ss -ltnp | grep PORT`. Container: check the port label matches what the app listens on and the container is on `docker_traefik`.
- **404**: Traefik has no router for that host. Ask the dashboard API, which needs no docker socket:
```sh
curl -sk https://traefik.i.grosinger.net/api/http/routers/NAME@file # host process
curl -sk https://traefik.i.grosinger.net/api/http/routers/NAME@docker # container
```
## Public exposure is a different task
Everything above is reachable only from the LAN and Tailscale. Making a service internet-facing means a real `grosinger.net` name, an explicit `traefik.http.routers.NAME.entrypoints=websecure` label, and a deliberate decision by the user. Do not add `websecure` when asked for an internal hostname.