- Converted Traefik section to tangle blocks with absolute paths - Created .gitea/workflows/tangle.yaml Gitea Action - tangle-deploy.sh: tangles org → writes files → restarts services
32 KiB
Infrastructure Documentation — gharbeia.net
Architecture
Hosts
-
production-1(10.10.10.201) - Docker host, runs all services
- Hermes Agent
- Management/automation host
Network
- Docker network
networking(172.28.10.0/24) - Proxmox VLANs: 1/10/20/30/40/50
- Services VLAN: 10.10.10.0/24
- Domain: gharbeia.net via Cloudflare (orange cloud/proxied)
External Access Architecture
Cloudflare (edge, orange cloud) └─ Cloudflare Tunnel "home" (cloudflared on production-1) └─ Traefik (entrypoint=tunnel, port 8081) ├─ Authentik Forward Auth (external routers) ├─ gharbeia-site (nginx) ├─ jellyfin (SSO via plugin + OIDC) ├─ gitea (native OIDC) └─ *.gharbeia.net services
Internal Access Architecture
LAN client (browser) └─ Traefik (entrypoint=secureweb, port 443) ├─ Authentik Forward Auth (internal.yaml routers) ├─ gharbeia-site (public, no auth) ├─ jellyfin (SSO via plugin) └─ *.gharbeia.net services
Service-to-service / automation / cross-VLAN
└─ Traefik (entrypoint=internal, port 8083 — NO auth)
└─ Same routing as secureweb, from traefik-internal-noauth.yaml
Key distinction: :443 = browsers/humans with Authentik auth.
:8083 = runners, automated tooling, services on other VLANs.
Traefik — Reverse Proxy
Traefik is the edge router for all HTTP traffic. It handles TLS termination via Let's Encrypt (DNS-01 challenge through Cloudflare), routes traffic to the right container, and applies middleware chains for auth, security, and rate limiting.
Three entrypoints:
-
tunnel(:8081) - Receives traffic from the Cloudflare tunnel. All routers here have Authentik Forward Auth.
-
secureweb(:443) - Internal LAN traffic with TLS. Also has Authentik Forward Auth for browser access.
-
internal(:8083) - Service-to-service and cross-VLAN traffic. No auth. HTTP only. For runners, automation, and API calls that shouldn't hit Authentik.
Static Configuration
The static config sets entrypoints, TLS resolvers, providers, and plugins. It is the foundation everything else builds on.
global:
checkNewVersion: true
sendAnonymousUsage: true
log:
level: INFO
accessLog:
filePath: /var/log/access.log
format: json
api:
dashboard: true
insecure: true
entryPoints:
web:
address: :80
http:
redirections:
entryPoint:
to: secureweb
scheme: https
permanent: true
tunnel:
address: :8081
secureweb:
address: :443
http:
tls:
options: default
certResolver: letsencrypt
domains:
- main: gharbeia.net
sans:
- "*.gharbeia.net"
internal:
address: :8083
metrics:
address: :8082
metrics:
prometheus:
entryPoint: metrics
manualRouting: true
headerLabels:
useragent: User-Agent
buckets:
- 0.1
- 0.3
- 1.2
- 5.0
providers:
docker:
exposedByDefault: false
file:
directory: /etc/traefik
watch: true
certificatesResolvers:
letsencrypt:
acme:
storage: /letsencrypt/acme.json
email: gharbeia@riseup.net
keyType: EC384
caServer: https://acme-v02.api.letsencrypt.org/directory
dnsChallenge:
provider: cloudflare
resolvers:
- 1.1.1.1:53
- 1.0.0.1:53
propagation:
delayBeforeChecks: 60s
experimental:
plugins:
crowdsec-bouncer-traefik-plugin:
moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
version: v1.4.2
Why each piece:
web(:80) exists only to redirect to HTTPS. No TLS.tunnel(:8081) is inbound-only from cloudflared, never exposed to LAN. Cloudflare handles TLS at the edge, so this can be plain HTTP inside Docker.secureweb(:443) is the LAN-facing entrypoint with Let's Encrypt certs covering bothgharbeia.netand*.gharbeia.net.internal(:8083) is plain HTTP for service-to-service traffic. TLS overhead is unnecessary on the internal bridge network.metrics(:8082) exposes Prometheus metrics, manually routed.dnsChallengewith Cloudflare provider issues wildcard certs. The 60s propagation delay avoids rate-limit issues with Cloudflare's API.
Dynamic Configuration — Middleware
Shared middleware used by all routers. Defined once here, referenced by name in every router block.
http:
middlewares:
authentik-forwardauth:
forwardAuth:
address: http://authentik-server:9000/outpost.goauthentik.io/auth/traefik
trustForwardHeader: true
authResponseHeaders:
- X-authentik-username
- X-authentik-groups
- X-authentik-email
- X-authentik-name
- X-authentik-uid
security-headers:
headers:
customFrameOptionsValue: SAMEORIGIN
contentTypeNosniff: true
browserXssFilter: true
referrerPolicy: no-referrer
permissionsPolicy: ""
customResponseHeaders:
X-Robots-Tag: "noindex, nofollow"
Server: ""
traefik-bouncer:
plugin:
crowdsec-bouncer-traefik-plugin:
enabled: "true"
crowdsecMode: live
crowdsecLapiKey: __CROWDSEC_LAPI_KEY__
crowdsecLapiHost: crowdsec:8080
crowdsecLapiScheme: http
updateFrequencySec: 5
defaultDecisionLifetimeSec: 60
compress:
compress:
excludedContentTypes:
- text/event-stream
ratelimit:
rateLimit:
average: 100
burst: 50
The auth flow: Authentik's outpost runs as a sidecar (authentik-server in Docker) that validates session cookies. When a request lacks a valid session, Traefik redirects to the Authentik login page. After login, Authentik redirects back to the original URL with a session cookie.
security-headers locks down XSS, clickjacking, and fingerprinting. The empty
permissionsPolicy disables all browser APIs by default.
traefik-bouncer runs CrowdSec's LAPI bouncer as a Traefik plugin. IPs flagged
by CrowdSec get blocked. The LAPI key is a placeholder — fill from vault.
Internal Routers — Authenticated (secureweb :443)
These routers serve LAN browser traffic. All have Authentik Forward Auth.
Backend services are referenced by Docker DNS name on the networking bridge.
http:
routers:
# ── Media & Streaming ─────────────────────────────────────────
jellyfin:
rule: "Host(`jellyfin.gharbeia.net`)"
service: jellyfin-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
jellyseerr:
rule: "Host(`jellyseerr.gharbeia.net`)"
service: jellyseerr-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
# ── *arr Suite ────────────────────────────────────────────────
radarr:
rule: "Host(`radarr.gharbeia.net`)"
service: radarr-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
sonarr:
rule: "Host(`sonarr.gharbeia.net`)"
service: sonarr-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
lidarr:
rule: "Host(`lidarr.gharbeia.net`)"
service: lidarr-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
prowlarr:
rule: "Host(`prowlarr.gharbeia.net`)"
service: prowlarr-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
whisparr:
rule: "Host(`whisparr.gharbeia.net`)"
service: whisparr-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
mylar:
rule: "Host(`mylar.gharbeia.net`)"
service: mylar-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
lazylibrarian:
rule: "Host(`lazylibrarian.gharbeia.net`)"
service: lazylibrarian-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
# ── Downloaders ───────────────────────────────────────────────
sabnzbd:
rule: "Host(`sabnzbd.gharbeia.net`)"
service: sabnzbd-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
qbittorrent:
rule: "Host(`qbittorrent.gharbeia.net`)"
service: qbittorrent-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
flaresolverr:
rule: "Host(`flaresolverr.gharbeia.net`)"
service: flaresolverr-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
# ── Homepage / Dashboards ─────────────────────────────────────
homepage:
rule: "Host(`homepage.gharbeia.net`)"
service: homepage-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
homarr:
rule: "Host(`homarr.gharbeia.net`)"
service: homarr-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
heimdall:
rule: "Host(`heimdall.gharbeia.net`)"
service: heimdall-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
# ── Monitoring ────────────────────────────────────────────────
grafana:
rule: "Host(`grafana.gharbeia.net`)"
service: grafana-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
prometheus:
rule: "Host(`prometheus.gharbeia.net`)"
service: prometheus-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
# ── Website (public, no auth) ─────────────────────────────────
gharbeia-site:
rule: "Host(`gharbeia.net`) || Host(`www.gharbeia.net`)"
service: gharbeia-site-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
# ── Management ────────────────────────────────────────────────
gitea:
rule: "Host(`git.gharbeia.net`)"
service: gitea-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- security-headers@file
- traefik-bouncer@file
# No authentik-forwardauth — Gitea has native OIDC
portainer:
rule: "Host(`portainer.gharbeia.net`)"
service: portainer-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
guacamole:
rule: "Host(`guacamole.gharbeia.net`)"
service: guacamole-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
headplane:
rule: "Host(`headplane.gharbeia.net`) && PathPrefix(`/admin/`)"
service: headplane-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
traefik-dashboard:
rule: "Host(`traefik.gharbeia.net`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
service: traefik-dashboard-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
# ── Other ─────────────────────────────────────────────────────
bazarr:
rule: "Host(`bazarr.gharbeia.net`)"
service: bazarr-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
tdarr:
rule: "Host(`tdarr.gharbeia.net`)"
service: tdarr-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
ddns-updater:
rule: "Host(`ddns-updater.gharbeia.net`)"
service: ddns-updater-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
stash:
rule: "Host(`stash.gharbeia.net`)"
service: stash-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
audiobookshelf:
rule: "Host(`audiobookshelf.gharbeia.net`)"
service: audiobookshelf-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
# ── External hardware (via LAN) ───────────────────────────────
synology:
rule: "Host(`synology.gharbeia.net`)"
service: synology-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
gateway:
rule: "Host(`gateway.gharbeia.net`)"
service: gateway-internal
entryPoints:
- secureweb
tls:
certResolver: letsencrypt
middlewares:
- authentik-forwardauth@file
- security-headers@file
- traefik-bouncer@file
# ── Services (Docker DNS backends) ──────────────────────────────
services:
# VPN-routed (through Gluetun network namespace)
jellyfin-internal:
loadBalancer:
servers:
- url: "http://gluetun:8096"
jellyseerr-internal:
loadBalancer:
servers:
- url: "http://gluetun:5055"
radarr-internal:
loadBalancer:
servers:
- url: "http://gluetun:7878"
sonarr-internal:
loadBalancer:
servers:
- url: "http://gluetun:8989"
lidarr-internal:
loadBalancer:
servers:
- url: "http://gluetun:8686"
prowlarr-internal:
loadBalancer:
servers:
- url: "http://gluetun:9696"
whisparr-internal:
loadBalancer:
servers:
- url: "http://gluetun:6969"
mylar-internal:
loadBalancer:
servers:
- url: "http://gluetun:8090"
lazylibrarian-internal:
loadBalancer:
servers:
- url: "http://gluetun:5299"
sabnzbd-internal:
loadBalancer:
servers:
- url: "http://gluetun:8080"
qbittorrent-internal:
loadBalancer:
servers:
- url: "http://gluetun:8200"
flaresolverr-internal:
loadBalancer:
servers:
- url: "http://gluetun:8191"
bazarr-internal:
loadBalancer:
servers:
- url: "http://gluetun:6767"
tdarr-internal:
loadBalancer:
servers:
- url: "http://gluetun:8265"
stash-internal:
loadBalancer:
servers:
- url: "http://gluetun:7777"
audiobookshelf-internal:
loadBalancer:
servers:
- url: "http://gluetun:13378"
# Direct Docker DNS
homepage-internal:
loadBalancer:
servers:
- url: "http://homepage:3000"
homarr-internal:
loadBalancer:
servers:
- url: "http://homarr:7575"
heimdall-internal:
loadBalancer:
servers:
- url: "http://heimdall:80"
grafana-internal:
loadBalancer:
servers:
- url: "http://grafana:3000"
prometheus-internal:
loadBalancer:
servers:
- url: "http://prometheus:9090"
gitea-internal:
loadBalancer:
servers:
- url: "http://gitea:3000"
portainer-internal:
loadBalancer:
servers:
- url: "http://portainer:9000"
guacamole-internal:
loadBalancer:
servers:
- url: "http://guacamole:8080"
headplane-internal:
loadBalancer:
servers:
- url: "http://headplane:3000"
traefik-dashboard-internal:
loadBalancer:
servers:
- url: "http://traefik:8080"
ddns-updater-internal:
loadBalancer:
servers:
- url: "http://ddns-updater:8310"
gharbeia-site-internal:
loadBalancer:
servers:
- url: "http://gharbeia-site:80"
# External hardware
synology-internal:
loadBalancer:
servers:
- url: "https://192.168.1.8:5001"
passHostHeader: true
serversTransport: insecure-no-verify
gateway-internal:
loadBalancer:
servers:
- url: "https://192.168.1.1"
passHostHeader: true
serversTransport: insecure-no-verify
serversTransports:
insecure-no-verify:
insecureSkipVerify: true
Services behind Gluetun use http://gluetun:PORT because those containers share
Gluetun's network namespace via network_mode: service:gluetun. Traefik reaches
them via Docker DNS through the networking bridge.
External hardware (Synology, gateway) use serversTransport: insecure-no-verify
because their self-signed certs would otherwise fail Traefik's TLS verification.
Internal Routers — Authless (internal :8083)
Identical routing to the authenticated routers, but on entrypoint internal
(port 8083) and without authentik-forwardauth@file middleware. Used by
automation, runners, and cross-VLAN tooling.
http:
routers:
jellyfin-noauth:
rule: "Host(`jellyfin.gharbeia.net`)"
service: jellyfin-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
jellyseerr-noauth:
rule: "Host(`jellyseerr.gharbeia.net`)"
service: jellyseerr-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
radarr-noauth:
rule: "Host(`radarr.gharbeia.net`)"
service: radarr-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
sonarr-noauth:
rule: "Host(`sonarr.gharbeia.net`)"
service: sonarr-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
lidarr-noauth:
rule: "Host(`lidarr.gharbeia.net`)"
service: lidarr-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
prowlarr-noauth:
rule: "Host(`prowlarr.gharbeia.net`)"
service: prowlarr-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
whisparr-noauth:
rule: "Host(`whisparr.gharbeia.net`)"
service: whisparr-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
mylar-noauth:
rule: "Host(`mylar.gharbeia.net`)"
service: mylar-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
lazylibrarian-noauth:
rule: "Host(`lazylibrarian.gharbeia.net`)"
service: lazylibrarian-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
sabnzbd-noauth:
rule: "Host(`sabnzbd.gharbeia.net`)"
service: sabnzbd-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
qbittorrent-noauth:
rule: "Host(`qbittorrent.gharbeia.net`)"
service: qbittorrent-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
flaresolverr-noauth:
rule: "Host(`flaresolverr.gharbeia.net`)"
service: flaresolverr-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
homepage-noauth:
rule: "Host(`homepage.gharbeia.net`)"
service: homepage-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
homarr-noauth:
rule: "Host(`homarr.gharbeia.net`)"
service: homarr-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
heimdall-noauth:
rule: "Host(`heimdall.gharbeia.net`)"
service: heimdall-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
grafana-noauth:
rule: "Host(`grafana.gharbeia.net`)"
service: grafana-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
prometheus-noauth:
rule: "Host(`prometheus.gharbeia.net`)"
service: prometheus-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
gharbeia-site-noauth:
rule: "Host(`gharbeia.net`) || Host(`www.gharbeia.net`)"
service: gharbeia-site-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
gitea-noauth:
rule: "Host(`git.gharbeia.net`)"
service: gitea-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
portainer-noauth:
rule: "Host(`portainer.gharbeia.net`)"
service: portainer-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
guacamole-noauth:
rule: "Host(`guacamole.gharbeia.net`)"
service: guacamole-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
headplane-noauth:
rule: "Host(`headplane.gharbeia.net`) && PathPrefix(`/admin/`)"
service: headplane-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
traefik-dashboard-noauth:
rule: "Host(`traefik.gharbeia.net`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
service: traefik-dashboard-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
bazarr-noauth:
rule: "Host(`bazarr.gharbeia.net`)"
service: bazarr-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
tdarr-noauth:
rule: "Host(`tdarr.gharbeia.net`)"
service: tdarr-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
ddns-updater-noauth:
rule: "Host(`ddns-updater.gharbeia.net`)"
service: ddns-updater-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
stash-noauth:
rule: "Host(`stash.gharbeia.net`)"
service: stash-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
audiobookshelf-noauth:
rule: "Host(`audiobookshelf.gharbeia.net`)"
service: audiobookshelf-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
synology-noauth:
rule: "Host(`synology.gharbeia.net`)"
service: synology-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
gateway-noauth:
rule: "Host(`gateway.gharbeia.net`)"
service: gateway-internal
entryPoints:
- internal
middlewares:
- security-headers@file
- traefik-bouncer@file
Services
gharbeia-site (Static Website)
- Container:
gharbeia-site(nginx:stable-alpine3.17-perl) - Purpose: Landing page for gharbeia.net
- Docroot:
/docker/appdata/gharbeia-site/html - Nginx config:
/docker/appdata/gharbeia-site/nginx.conf - Traefik router:
gharbeia-siteon entrypointstunnelandsecureweb
www.gharbeia.net → 301 redirect → gharbeia.net (handled by nginx)
Both domains in Traefik router rule: Host(\`gharbeia.net\`) || Host(\`www.gharbeia.net\`)
Cloudflare Tunnel "home"
- Container:
cloudflared(cloudflare/cloudflared:latest) - Config:
/docker/compose/cloudflared-config.yml(local, unused at runtime) - Runtime:
docker compose up -d cloudflaredwith--token(remote config from dashboard) - Local config is IGNORED when running with
--token— ingress rules come from Cloudflare Zero Trust dashboard's public hostname configuration - DNS CNAME records must point to
<tunnel-uuid>.cfargotunnel.com - Tunnel UUID:
c29295c5-946a-4ddf-bdfe-7eafcd74faa3
Public Hostnames (Cloudflare Dashboard)
These must be added in Cloudflare Zero Trust > Networks > Tunnels > home > Public Hostnames:
- *.gharbeia.net → https://traefik:443
- gharbeia.net → https://traefik:443 (must be explicit, wildcard doesn't cover root)
- www.gharbeia.net → https://traefik:443
DNS Records
gharbeia.net:
- CNAME → c29295c5-946a-4ddf-bdfe-7eafcd74faa3.cfargotunnel.com (proxied)
- MX → in1-smtp.messagingengine.com
- MX → in2-smtp.messagingengine.com
- TXT → v=spf1 include:spf.messagingengine.com ?all
www.gharbeia.net:
- CNAME → c29295c5-946a-4ddf-bdfe-7eafcd74faa3.cfargotunnel.com (proxied)
Authentication
Authentik (IdP)
- Provides SSO for all services
- Two modes: Forward Auth (proxy-level) and OIDC (service-level)
- External tunnel traffic: Forward Auth on all routers in compose labels
- Internal LAN: Forward Auth on all routers in internal.yaml
- Exceptions: Jellyfin (SSO plugin), Gitea (native OIDC)
Gitea — Native OIDC
- Configured in Gitea → Site Administration → Authentication Sources
- Authentik OIDC provider registered
- Works with native Gitea clients (no browser redirect needed)
Jellyfin — SSO-Auth Plugin v4.0.0.4
- Plugin: SSO-Auth (via Jellyfin plugin catalog)
- Authentik OIDC provider created, redirect URI:
https://jellyfin.gharbeia.net/sso/OID/redirect/Authentik - Scope mapping sends
groupsclaim in OpenID token - Plugin configured via API (
docker cpXML into container) - SSO button added to login page via Jellyfin branding config
- No Forward Auth — Jellyfin handles auth itself via plugin
LOGBOOK
[2026-05-15 Thu 06:10] Static site launched
- Setup gharbeia.net and www.gharbeia.net with nginx container
- Tunnel + Traefik wiring
- www → root 301 redirect in nginx config
- Traefik router on both tunnel and secureweb entrypoints
[2026-05-15 Thu 06:38] Internal authless entrypoint + domain migration
- Added Traefik
internalentrypoint (port 8083) for authless service-to-service traffic - Created
/docker/compose/traefik-internal-noauth.yamlwith 28 router copies - Exposed port 8083 (
INTERNAL_PORT_TRAEFIK=8083) - Unbound already resolves
*.gharbeia.net→10.10.10.201vialocal-zone redirect— no changes needed - Updated Gitea runner:
GITEA_INSTANCE_URL→http://git.gharbeia.net:8083 - Architecture settled:
:443= browsers with auth,:8083= services without
[2026-05-15 Thu 06:18] Error 1033 on gharbeia.net
- Problem: CNAME for
gharbeia.netpointed to old tunnel (2cd53dc4-…), not "home" tunnel (c29295c5-…) - www.gharbeia.net worked because its CNAME was correct
- www → root redirect → Cloudflare tried old tunnel → 1033
- Fix: Updated CNAME DNS record via Cloudflare API (DNS token)
- Lesson: Bare domain DNS must point to the same tunnel UUID as subdomains
- Lesson: The local cloudflared-config.yml is decorative when running with
--token
[2026-05-15 Thu 03:07] Literate infrastructure established
- infrastructure.org becomes the source of truth — all config files are
tangle targets embedded as
#+BEGIN_SRCblocks with absolute paths tangle-deployscript installed at/usr/local/bin/tangle-deployon production-1; run after git push to regenerate configs and restart services- Gitea repo:
git@git.gharbeia.net:amr/infrastructure.git - Emacs-nox installed on production-1 for headless tangle