name deploying-public-apps description Deploys public-facing apps that handle their own authentication (like Jellyfin, game servers) without Authelia forward auth
Deploying Public-Facing Apps
For apps that handle their own auth (Jellyfin, Minecraft, etc.) and don't need Authelia forward auth.
Checklist
When deploying a new public-facing app, complete these steps:
Create app manifests in flux/clusters/superbloom/apps/<app>/
Add app to flux/clusters/superbloom/apps/kustomization.yaml
Add domain to DDNS in flux/clusters/superbloom/infra/ddns/release.yaml
Add Caddy route in flux/clusters/superbloom/infra/caddy/caddyfile.yaml
Commit, push, and reconcile Flux
1. App Manifests
Create flux/clusters/superbloom/apps/<app>/:
kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: <app>
resources:
- ns.yaml
- release.yaml
ns.yaml
apiVersion: v1
kind: Namespace
metadata:
name: <app>
release.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: <app>
namespace: <app>
spec:
interval: 5m
chart:
spec:
chart: app-template
version: "4.1.1"
sourceRef:
kind: HelmRepository
name: bjw-s
namespace: flux-system
values:
controllers:
main:
containers:
main:
image:
repository: <image>
tag: latest
env:
TZ: America/Denver
probes:
liveness:
enabled: true
service:
main:
controller: main
ports:
http:
port: <port>
persistence:
2. Add to Apps Kustomization
Edit flux/clusters/superbloom/apps/kustomization.yaml:
resources:
- ...existing...
- <app>/
3. Add Domain to DDNS
Edit flux/clusters/superbloom/infra/ddns/release.yaml:
env:
DOMAINS: saavylab.dev,...existing...,<subdomain>.saavylab.dev
4. Add Caddy Route (NO forward_auth)
Edit flux/clusters/superbloom/infra/caddy/caddyfile.yaml:
data:
Caddyfile: |
# <App> - no forward_auth (handles own auth)
<subdomain>.saavylab.dev {
reverse_proxy <app>.<app>.svc.cluster.local:<port>
}
Key difference from internal services: No forward_auth block. The app handles its own authentication.
5. Deploy
cd ~/dev/sb
git add -A && git commit -m "Add <app> at <subdomain>.saavylab.dev"
git push
flux reconcile kustomization flux-system --with-source
kubectl get pods -n <app> -w
Examples
Jellyfin (Media Server)
URL: watch.saavylab.dev
Port: 8096
Auth: Built-in (easier for TVs, Xbox, etc.)
Special: GPU passthrough via /dev/dri hostPath
Minecraft (Game Server)
URL: mc.saavylab.dev
Port: 25565
Auth: Mojang/Microsoft accounts
Special: Uses LoadBalancer service, not Caddy
Common Patterns
GPU Passthrough
persistence:
dri:
enabled: true
type: hostPath
hostPath: /dev/dri
globalMounts:
- path: /dev/dri
Requires securityContext.privileged: true on the container.
Host Storage (ZFS Pool)
persistence:
media:
enabled: true
type: hostPath
hostPath: /tank/media
globalMounts:
- path: /media
readOnly: true
NVMe Storage (Fast Cache)
persistence:
cache:
enabled: true
type: persistentVolumeClaim
accessMode: ReadWriteOnce
size: 50Gi
storageClass: local-path
globalMounts:
- path: /cache
Post-Deploy: Certificate Provisioning
After adding a new domain, restart Caddy so it provisions the TLS certificate:
kubectl rollout restart deployment/caddy -n caddy-system
kubectl logs deployment/caddy -n caddy-system -f | grep -E "(obtain|certificate|watch)"
If cert fails with 520 error:
Check DDNS logs: kubectl logs deployment/ddns -n ddns
Verify DNS points to correct IP: dig +short <subdomain>.saavylab.dev
Restart DDNS if needed: kubectl rollout restart deployment/ddns -n ddns
Debugging
kubectl get pods -n <app>
kubectl logs -n <app> -l app.kubernetes.io/name=<app>
kubectl get endpoints -n <app>
kubectl get helmrelease -n <app>
flux logs --kind=HelmRelease --name=<app> -n <app>
kubectl run -it --rm debug --image=curlimages/curl -- curl http://<app>.<app>.svc.cluster.local:<port>/health
Why No Authelia?
Some apps need direct access without SSO:
Smart TVs / Streaming devices - Can't handle browser redirects
Game consoles - Xbox, PlayStation native apps
Mobile apps - Native Jellyfin/Plex apps
Game clients - Minecraft, etc.
These apps have their own auth mechanisms (app logins, Mojang accounts, etc.) that work better for their use case.