Self-hosted URL-Shortener, erreichbar unter short.nettailor.net.

Setup

Drei Services (kutt, postgres, redis) auf dem Hetzner-VPS, via Komodo aus einem eigenen Compose-Repo deployed.

  • Secrets (JWT_SECRET, DB_PASSWORD) direkt als Komodo-Stack-Environment-Variablen gesetzt (nicht als _FILE-Secrets im Repo) – einfacher, da Komodo sie intern verschlüsselt und man sich das Verteilen von Secret-Dateien auf den Host spart.
  • DISALLOW_REGISTRATION: "true" dauerhaft gesetzt. Für die Admin-Ersteinrichtung einmalig auf "false" umgestellt, registriert, danach zurückgesetzt.
  • Container kutt läuft ohne Authentik Forward Auth davor – anders als sonst üblich in diesem Setup.

⚠️ Wichtig: Kein Forward Auth vor URL-Shortenern

Anders als bei Stirling PDF, Grafana & Co. darf hier nicht die komplette Domain hinter Authentik Forward Auth liegen. Kurzlinks müssen für jeden anonymen Klick öffentlich erreichbar sein – sonst landet jeder externe Besucher, der auf einen short.nettailor.net/xyz-Link klickt, erst auf der Authentik-Login-Seite statt beim Ziel. Kutt bringt sein eigenes Login-System für Dashboard/API/Admin mit, das reicht als Zugriffsschutz für die Verwaltung.

Netzwerk (NPM-Erreichbarkeit)

Bestätigt die generelle Komodo-Lektion: NPM-proxied Container müssen explizit ins proxy-Netzwerk. Ursprüngliche Compose-Datei hatte kutt nur mit Host-Port-Mapping (127.0.0.1:3000:3000) – das reicht nicht, wenn NPM selbst containerisiert im proxy-Netz läuft und nicht auf dem Docker-Host-Netzwerk-Namespace sitzt.

Fix: ports: beim kutt-Service entfernt, stattdessen zweites Netzwerk proxy: external: true ergänzt und den Service dort zusätzlich zum internen default-Netz reingehängt. NPM zeigt dann per Container-Name (kutt) statt IP auf den Service.

🔧 Lessons Learned: AdGuard-Cache nach neuem DNS-Record

Datum: August 2026

Nach dem Anlegen des A-Records für short.nettailor.net lieferte dig short.nettailor.net +short lokal weiterhin leer, obwohl dig @1.1.1.1 short.nettailor.net +short bereits die korrekte IP zurückgab. Auch curl schlug mit Could not resolve host fehl.

Ursache: Doppelter Cache-Effekt:

  1. AdGuard Home hatte vermutlich einen negativen Cache-Eintrag von vor dem Anlegen des DNS-Records – Query-Log zeigte zwar “Verarbeitet”, aber mit auffällig kurzen Antwortzeiten (~0,2 ms), untypisch für eine echte Upstream-Abfrage.
  2. macOS’ mDNSResponder cached zusätzlich eigenständig, unabhängig von dig-Ergebnissen – dig und curl nutzen nicht zwingend denselben Auflösungspfad.

Fix:

# macOS lokal:
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

Bei AdGuard selbst hilft Einstellungen → DNS-Einstellungen → Cache leeren, notfalls Container-Neustart.

Merke: Bei “DNS liefert nichts, obwohl der Record längst existiert” immer beide Cache-Ebenen prüfen (lokaler Resolver und macOS-eigener DNS-Cache), nicht nur den Router/AdGuard.

Custom Domains

Kutt unterstützt zusätzliche Domains neben der DEFAULT_DOMAIN (Settings → Custom Domain). Die im Kutt-Settings-UI vorgeschlagene Beispiel-IP ist die von kutt.it – bei Self-Hosting muss stattdessen auf die eigene VPS-IP gezeigt werden. Für eine zusätzliche Domain: A-Record auf die VPS-IP, eigener NPM-Proxy-Host (gleicher Forward-Target kutt:3000, eigenes Let’s-Encrypt-Zertifikat), danach Domain in Kutt unter Settings eintragen.

Aktive Domains:

DomainZweck
short.nettailor.netDefault-Domain
ghstbx.deCustom Domain
veedel.netCustom Domain

Jede zusätzliche Domain braucht einen eigenen NPM-Proxy-Host + eigenes Let’s-Encrypt-Zertifikat, auch wenn sie letztlich denselben Kutt-Container (kutt:3000) anspricht.

Status

  • Compose-Stack (kutt, postgres, redis) über Komodo deployed
  • proxy-Netzwerk korrekt eingebunden, NPM-Proxy-Host mit Let’s Encrypt
  • DNS-Record gesetzt, Cache-Probleme gelöst
  • Admin-Account angelegt, Registration wieder gesperrt
  • Custom Domains ghstbx.de und veedel.net ergänzt

Erstellt: 2026-08-14 · Autor: Philipp