- First-run setup: replace one big bash comment block with numbered sub-sections, each step getting its own prose explanation and separate code block(s) so individual commands can be copied without selecting around surrounding lines - Step 4: add explicit table showing what happens when each side of the Caddy/Authelia gate is missing; add policy comparison table - Frigate section: move subnet discovery command out of YAML comment into its own code block; separate restart commands into own blocks - Troubleshooting "access denied": show the exact rule YAML to add and the restart command, each in their own block - Troubleshooting "Caddy can't resolve authelia": show full compose YAML snippet rather than describing it in prose - SMTP section: separate each action into its own code block https://claude.ai/code/session_012eTokAaGiZo7aGt1T2W9BC
Authelia + fail2ban
Self-hosted authentication portal (Authelia) with an IP-banning sidecar
(fail2ban). Sits next to your dockerized Caddy and gates every public
subdomain behind a single sign-on portal at auth.example.com.
Internet
|
v
+-------+ caddy_net (docker) +--------------------+
| Caddy |--- forward_auth -------------->| Authelia |
+---+---+ | /api/authz/... |
| reverse_proxy +--------+-----------+
| |
v v
Frigate (LAN), NAS, Pi, etc. ./authelia/db.sqlite3
./authelia/authelia.log
^
| tail
+------+--------+
| fail2ban | host net
| DOCKER-USER | + iptables
+---------------+
- One docker-compose file, two services, one external network (
caddy_net). - File-backed users database, SQLite storage, no Redis, no external DB.
- Filesystem notifier for password reset (swap to SMTP later, one block change).
- fail2ban bans via the
DOCKER-USERiptables chain: drops happen at the host edge before traffic reaches any docker-published port. - Caddy is not in this stack. Copy blocks from
caddy/snippets.caddyfileinto your Caddy setup.
Repo layout
authelia-stack/
├── docker-compose.yml
├── .env.example # copy to .env
├── .gitignore
├── README.md
│
├── authelia/
│ ├── configuration.yml # main config -- edit your domain here
│ ├── users_database.yml.example # copy to users_database.yml (gitignored)
│ ├── secrets/ # gitignored; secret files mounted as /secrets
│ └── notifications/ # filesystem notifier writes here (gitignored)
│
├── fail2ban/
│ └── data/ # mounted as /data in the container
│ ├── filter.d/
│ │ ├── authelia.local # matches Authelia text-log auth failures
│ │ └── caddy-4xx.local # matches Caddy JSON 4xx responses
│ └── jail.d/
│ ├── authelia.local # 3 fails/10 min -> 1 hr IP ban
│ └── caddy.local # 30 fails/2 min -> 30 min IP ban
│
└── caddy/
└── snippets.caddyfile # per-service snippets to add to your existing Caddyfile
Prerequisites
- Docker + docker compose v2.
- Caddy already running, in Docker, joined to an external network named
caddy_net. If your network is named differently, replacecaddy_neteverywhere in this repo. - Caddy v2.5.1 or newer (for the
forward_authdirective; tested on v2.11.2). - A root domain you control. DNS A records for
auth.<root>and every protected subdomain must point at the Caddy host's public IP.
Which sites go behind Authelia?
There are four ways a site can relate to Authelia. Pick one per site.
| Case | App has built-in auth? | Supports proxy auth? | What to do |
|---|---|---|---|
| 1 | No | n/a | import authelia in Caddy + rule in Authelia. Authelia is the only login. |
| 2a | Yes | Yes (Remote-User header) | import authelia + disable app's own login form. Single login via headers. |
| 2b | Yes | Yes (OIDC) | import authelia + configure Authelia as OIDC provider in the app. Single login via token exchange. |
| 3 | Yes | No | import authelia in Caddy. App auth is unchanged. User logs into Authelia then the app. Two logins. |
| 4 | Yes | — | Plain reverse_proxy. No import authelia, no rule. App handles auth. |
Concretely:
doorbell.example.com(Pi PTT page) -- case 1. No app auth at all. Authelia is the only gate. Usetwo_factor-- this URL controls a speaker.cam.example.com(Frigate UI) -- case 2a. Frigate 0.14+ supports proxy auth viaRemote-Userheader. Disable Frigate's login form and let Authelia drive both the access gate and the role mapping (admin vs. viewer).books.example.com(Audiobookshelf) -- case 2b. App redirects to Authelia, Authelia issues a JWT token, app accepts it. No password set in the app itself.- Router admin / NAS UI -- case 3 if you want a 2FA gate in front, case 4 if you just leave it to the app.
What is OIDC?
OpenID Connect (OIDC) is an identity protocol layered on top of OAuth 2.0. The short version: instead of an app checking your password itself, it redirects you to Authelia, Authelia authenticates you and issues a signed token (JWT), and the app trusts that token. The app never handles your password — it only ever sees the token.
Authelia becomes the identity provider (IdP). Apps like Audiobookshelf, Immich, Jellyfin, and Mealie become relying parties — they trust Authelia's tokens and use them to identify users.
The practical difference from proxy-header auth (case 2a):
- Headers: Caddy adds
Remote-Userto every request and the app reads it. Works silently. Requires the app to support header-based auth. - OIDC: The browser does a full redirect dance (app → Authelia → app). Users see the Authelia login page. Requires the app to support OIDC/OAuth.
Both result in the same thing: one Authelia credential covers the app.
OIDC requires additional setup in authelia/configuration.yml —
an identity_providers.oidc block with a client entry per app, each with
its own client_id and client_secret. See Authelia's OIDC docs for the
full config. The Caddy side is identical to case 2a: import authelia.
Default policy in configuration.yml is deny, so a domain with no rule
AND no import authelia in Caddy never reaches Authelia at all.
Should you use Authelia at all?
A password manager with per-service credentials and per-service TOTP is a solid security posture. Authelia improves on it in specific situations:
| Situation | Password manager alone | Authelia |
|---|---|---|
| App has no auth at all (Homer, Dozzle, doorbell page) | Can't help | Gates it with 2FA, zero app changes |
| App has auth but no native TOTP (Uptime Kuma, Syncthing, phpIPAM) | Password-only | Adds 2FA in front for free |
| Multiple people need access | Change credentials in N places | Disable one account in Authelia |
| True SSO -- log in once, reach 10 services | Still authenticates 10× (autofilled) | One session covers all gated services |
| Consistent rate-limiting / brute-force protection | Wildly varies per app | fail2ban + regulation applied uniformly |
Where Authelia is marginal for a single user:
- All your services already have native TOTP support → Authelia adds mostly friction. You're right that 2FA doesn't require Authelia: Vaultwarden, Nextcloud, Grafana, and Gitea all support TOTP natively. If you've already set that up in a manager like Bitwarden, Authelia's 2FA argument is weaker.
- The Authelia password itself becomes keys-to-the-kingdom for everything gated behind it, which is why the TOTP requirement on Authelia matters more than on any individual service.
The real sweet spot:
- You have services with zero auth (case 1: Homer, Prometheus, NUT web UI, Gatus) -- something has to gate them.
- You have services with auth but no TOTP -- Authelia gives them 2FA without touching the app at all.
- You manage access for more than one person.
How to tell if an app supports proxy auth (case 2)
Look for any of these in the app's docs: "Remote-User header", "trusted upstream", "trusted proxies", "header-based auth", "SSO via reverse proxy".
| App | Case | Notes |
|---|---|---|
| Proxy-header auth (case 2a) | ||
| Frigate 0.14+ | 2a | auth.enabled: False + proxy: block in config.yml |
| Grafana | 2a | [auth.proxy] in grafana.ini; GF_AUTH_PROXY_ENABLED=true |
| Gitea / Forgejo | 2a | ENABLE_REVERSE_PROXY_AUTHENTICATION=true in app.ini |
| Nextcloud | 2a | trusted_proxies + user_external app + HTTP header auth |
| Paperless-ngx | 2a | PAPERLESS_ENABLE_HTTP_REMOTE_USER=true |
| Miniflux | 2a | AUTH_PROXY_HEADER=Remote-User env var |
| Home Assistant | 2a | trusted_networks auth provider + header forwarding |
| BookStack | 2a | AUTH_METHOD=http + HTTP_AUTH_HEADER=Remote-User in .env |
| OIDC auth (case 2b) | ||
| Audiobookshelf | 2b | Native OIDC; configure in Settings > Authentication |
| Jellyfin | 2b | Requires Jellyfin.Plugin.SSO from Plugin Catalogue |
| Immich | 2b | No header auth; OIDC only. Admin > OAuth settings |
| Mealie | 2b | OIDC supported; or use case 3 as a simpler gate |
| Portainer | 2b | OIDC in Settings > Authentication; or case 3 as simple gate |
| No built-in auth (case 1) | ||
| Homer / Heimdall | 1 | No auth at all -- Authelia is the only gate |
| Dozzle | 1 | No auth by default -- Authelia is the only gate |
| Prometheus | 1 | No auth built in; always gate, metrics expose internals |
| Alertmanager | 1 | No auth built in |
| Gatus | 1 | Status page; optional built-in OIDC but simpler to gate here |
| WatchYourLAN | 1 | Network ARP scanner, no built-in auth |
| NUT web UI | 1 | NUT daemon has no web UI; frontend web apps vary -- most have no auth |
| App keeps own auth (case 3) | ||
| Uptime Kuma | 3 | No proxy auth, no native TOTP -- Authelia is the only way to add 2FA |
| qBittorrent | 3 | Web UI has own auth; no proxy headers |
| Plex | 4 ⚠ | Do NOT use import authelia -- native clients go through Caddy but cannot complete browser-redirect auth; use Case 4, Plex handles its own auth |
| Emby | 4 ⚠ | Same as Plex |
| Gotify | 3 | Notification server; own auth |
| ntfy | 3 | Notification server; token-based auth |
| wg-easy | 3 | WireGuard web UI; WireGuard clients bypass Caddy (UDP 51820) |
| Umami | 3 | Analytics; own auth |
| phpIPAM | 3 | IP address management; own auth |
| Checkmk | 3 | Monitoring; own auth (LDAP in enterprise edition) |
| Snipe-IT | 3 | Asset management; own auth |
| Zammad | 3 | Help desk / ticketing; own auth |
| Lubelog | 3 | Vehicle maintenance; own auth |
| UniFi | 3 | Network controller; own auth |
| MeshCentral | 3 | Remote management; own auth; OIDC in enterprise builds |
| Vaultwarden | 3 or 4 | Strong native auth + TOTP; many skip Authelia here entirely |
| Router / NAS admin | 3 or 4 | Depends on firmware; case 4 is usually fine |
| App handles own auth (case 4) | ||
| Syncthing | 4 | Decent native auth; proxy auth not supported |
Getting git and authenticating to GitHub
Set your preferred editor first
The setup steps below use $EDITOR to open config files. It's a standard
shell variable that points to whichever editor you like. If it isn't set,
running $EDITOR somefile fails because the shell tries to execute an empty
string.
Check whether it's already set:
echo $EDITOR # blank = not set
Set it for the current session, or make it permanent by adding the export
to ~/.bashrc (bash) or ~/.zshrc (zsh):
# Pick one -- whatever you actually have installed:
export EDITOR=nano
export EDITOR=vim
export EDITOR=micro
export EDITOR="code --wait" # VS Code (the --wait keeps the terminal paused until you close the file)
# Make it permanent:
echo 'export EDITOR=nano' >> ~/.bashrc && source ~/.bashrc
# or for zsh:
echo 'export EDITOR=nano' >> ~/.zshrc && source ~/.zshrc
Once set, $EDITOR somefile opens that file in your chosen editor every time.
Install git
If git isn't installed on the server yet:
# Debian / Ubuntu / Raspberry Pi OS
sudo apt update && sudo apt install -y git
# Fedora / RHEL / Rocky / AlmaLinux
sudo dnf install -y git
The easiest way to authenticate is the GitHub CLI (gh). Install it by
adding GitHub's official apt repository (their Linux equivalent of a Homebrew
tap), then run gh auth login to authenticate interactively:
# Add the GitHub CLI apt repository
sudo apt install -y curl
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
| sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] \
https://cli.github.com/packages stable main" \
| sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update && sudo apt install -y gh
# Authenticate -- follow the prompts (browser or paste a token)
gh auth login
When prompted: choose GitHub.com, HTTPS, and Login with a web
browser (or paste a personal access token if the server has no browser).
Once done, gh passes credentials to git automatically -- no extra config
needed.
On macOS the whole thing is two lines:
brew install git gh
gh auth login
First-run setup
0. Clone the repo
The Frigate stack lives on main. This auth stack is on the authelia branch -- clone it separately into its own directory.
git clone -b authelia \
https://github.com/outis1one/frigate_w_audio.git \
~/docker/authelia
cd ~/docker/authelia
1. Create the external Docker network
Caddy must join this same network so it can reach Authelia by container name. Skip if caddy_net already exists.
docker network create caddy_net 2>/dev/null || true
2. Generate secrets
Authelia loads these from files so they never appear in docker inspect or process listings.
mkdir -p authelia/secrets
openssl rand -hex 32 > authelia/secrets/JWT_SECRET
openssl rand -hex 32 > authelia/secrets/SESSION_SECRET
openssl rand -hex 32 > authelia/secrets/STORAGE_ENCRYPTION_KEY
chmod 600 authelia/secrets/*
3. Set your domain
DOMAIN is the only value you set here. It flows into authelia/configuration.yml via Go template substitution and into your Caddyfile via {env.DOMAIN} -- no find-and-replace needed anywhere else.
cp .env.example .env
$EDITOR .env
Set DOMAIN=yourdomain.com and TZ=Your/Timezone. Save and close.
4. Add access control rules
This step and step 8 (Caddy wiring) must be done together for every site you want to gate. Both are required -- neither alone is enough.
$EDITOR authelia/configuration.yml
Scroll to access_control.rules. Uncomment the rule for each site you want to protect and choose a policy:
- domain: 'cam.{{ env "DOMAIN" }}'
policy: 'two_factor'
Why both sides are required
Caddy and Authelia each control one half of the gate:
| What you configure | What it does |
|---|---|
import authelia in a Caddy site block |
Sends that site's requests to Authelia for a decision |
Rule in access_control.rules |
Tells Authelia what decision to make |
The default policy is deny. If a request reaches Authelia with no matching rule, it gets a 403 Forbidden -- no login prompt, no redirect, just blocked. This is true even for an already-logged-in user.
Miss either side and here is what happens:
Caddy import authelia |
Rule in configuration.yml |
Result |
|---|---|---|
| Missing | Present | Site is open -- Authelia is never consulted |
| Present | Missing | 403 Forbidden, no login prompt |
| Both missing | Site is open -- Authelia is never consulted | |
| Both present | Works correctly |
Which policy to use
| Policy | Requires |
|---|---|
bypass |
Nothing -- Authelia waves the request through. Used for the portal itself only. |
one_factor |
Password only |
two_factor |
Password + TOTP. Use this for everything. |
5. Create your first user
cp authelia/users_database.yml.example authelia/users_database.yml
$EDITOR authelia/users_database.yml
Fill in username, email, and displayname. Then generate the password hash:
docker compose run --rm authelia \
authelia crypto hash generate argon2 --password 'your-real-password'
Copy the $argon2id$... line and paste it as the password: value in users_database.yml.
6. Pre-create the Authelia log file
Docker creates a directory at a bind-mount path if the source file does not exist yet. That breaks fail2ban's read-only mount. Create it as an empty file first:
touch authelia/authelia.log
7. Validate the config
docker compose run --rm authelia \
authelia validate-config --config /config/configuration.yml
Expect: Configuration: validation complete with no errors. Fix any YAML issues before continuing.
8. Wire Caddy
See Wire Caddy into Authelia below. Add the Caddy site block for each site alongside the rule you added in step 4.
9. Bring it up
docker compose up -d
Confirm both services started cleanly:
docker compose logs -f authelia
Expect: Authelia is listening on ...
docker compose logs -f fail2ban
Expect: Jail authelia is now active
Wire Caddy into Authelia
Open caddy/snippets.caddyfile. It contains copy-paste blocks for your
existing Caddyfile, not a replacement for it:
(authelia)and(accesslog)snippet definitions -- paste once near the top of your Caddyfile.auth.{env.DOMAIN}-- the Authelia portal block.- Per-service examples for all four cases (Frigate, Grafana, Gitea, Uptime Kuma, Homer, etc.) with the required per-app config notes inline.
Copy the blocks you need into your real Caddyfile and replace 192.168.x.x
with real upstream IPs. The domain is already templated as {env.DOMAIN} --
just make sure the DOMAIN environment variable is available to Caddy:
# Dockerized Caddy -- add to its .env or compose environment:
DOMAIN=yourdomain.com
# System Caddy -- add to /etc/caddy/caddy.env (or wherever systemd reads env):
echo 'DOMAIN=yourdomain.com' | sudo tee -a /etc/caddy/caddy.env
# Then make sure the systemd unit loads it:
# EnvironmentFile=/etc/caddy/caddy.env (in the [Service] section)
sudo systemctl daemon-reload && sudo systemctl reload caddy
Every site block should have import accesslog -- even case 4 sites.
fail2ban's caddy-4xx jail watches the one log file and covers all your
subdomains automatically. Scanners spray everything, not just gated sites.
Caddy access log path
fail2ban mounts /var/log/caddy from the host as read-only. Your Caddy
service must write to the same path. In your Caddy compose:
services:
caddy:
volumes:
- /var/log/caddy:/var/log/caddy
Create the directory before starting:
sudo mkdir -p /var/log/caddy
sudo chown caddy:caddy /var/log/caddy # adjust to your Caddy UID
Switching Frigate to Authelia (case 2)
Edit frigate_config/config.yml in your Frigate stack:
First, find your caddy_net subnet -- you need this for trusted_proxies:
docker network inspect caddy_net | jq '.[0].IPAM.Config'
Then edit frigate_config/config.yml:
auth:
enabled: False
trusted_proxies:
- 172.18.0.0/16 # replace with your caddy_net subnet from above
proxy:
header_map:
user: remote-user # matches `copy_headers Remote-User` in (authelia) snippet
role: remote-groups
default_role: viewer
separator: '|'
# Optional shared secret -- prevents LAN header spoofing.
# auth_secret: 'your-32-byte-hex'
To generate the optional auth_secret:
openssl rand -hex 32
Set the same value in the header_up X-Proxy-Secret line in your Caddy site block.
Add the rule to authelia/configuration.yml (step 4 of first-run), then restart both services:
docker compose restart authelia
docker compose restart frigate # in your Frigate stack
Verify in a private browser window: https://cam.yourdomain.com should go to Authelia and back without a Frigate login screen.
First login + TOTP enrollment
- Visit a protected subdomain in a private browser window.
- Caddy bounces you to
https://auth.example.com-- log in with your username and plaintext password. - If the rule is
two_factor, Authelia prompts you to register a second factor. Pick TOTP and scan the QR with your authenticator app. - Authelia writes a confirmation link to the filesystem notifier file:
Click the link to confirm TOTP registration.
docker compose exec authelia cat /config/notifications/notification.txt - Enter the TOTP code -- you're in. The
authelia_sessioncookie is scoped to your root domain and covers every protected subdomain automatically.
User management
Add a user
Append to authelia/users_database.yml, generate a hash:
docker compose run --rm authelia \
authelia crypto hash generate argon2 --password 'new-password'
Paste the hash as password:. Restart or wait 5 minutes for auto-reload.
Disable a user
Set disabled: true on their entry. Takes effect at next refresh.
Reset TOTP (force re-enrollment)
docker compose exec authelia \
authelia storage user totp delete --username USERNAME \
--config /config/configuration.yml
fail2ban
fail2ban does not need its own separate stack or compose file. It lives
alongside Authelia in this same docker-compose.yml. It uses host networking
(no docker network needed) and watches two log sources:
| Jail | Log | Trigger | Ban |
|---|---|---|---|
authelia |
./authelia/authelia.log |
3 failed logins in 10 min | 1 hour |
caddy-4xx |
/var/log/caddy/access.log |
30 HTTP 4xx in 2 min | 30 min |
The caddy-4xx jail covers every site on your Caddyfile as long as each
block has import accesslog. You don't need per-site jails.
Defense in depth: Authelia's regulation block locks the user account
after 3 bad passwords. fail2ban bans the source IP independently.
Verify jails are active
docker compose exec fail2ban fail2ban-client status
docker compose exec fail2ban fail2ban-client status authelia
docker compose exec fail2ban fail2ban-client status caddy-4xx
Test filters against real logs
docker compose exec fail2ban fail2ban-regex \
/var/log/authelia/authelia.log \
/data/filter.d/authelia.local
docker compose exec fail2ban fail2ban-regex \
/var/log/caddy/access.log \
/data/filter.d/caddy-4xx.local
If nothing matches: confirm log.format: 'text' in authelia/configuration.yml
and format json in the (accesslog) snippet in your Caddyfile.
Manually unban an IP
docker compose exec fail2ban fail2ban-client set authelia unbanip 1.2.3.4
docker compose exec fail2ban fail2ban-client set caddy-4xx unbanip 1.2.3.4
Tune thresholds
Edit fail2ban/data/jail.d/*.local, then:
docker compose restart fail2ban
Day-to-day
docker compose ps # services running?
docker compose logs -f authelia # follow Authelia
docker compose logs -f fail2ban # follow fail2ban
docker compose pull && docker compose up -d # upgrade images
Bump AUTHELIA_VERSION in .env when upgrading Authelia. After any
upgrade, re-run validate-config -- the schema evolves between releases.
Switching the notifier to SMTP
Replace notifier: in authelia/configuration.yml:
notifier:
disable_startup_check: false
smtp:
address: 'smtps://smtp.example.com:465'
username: 'authelia@example.com'
sender: 'Authelia <authelia@example.com>'
subject: '[Authelia] {title}'
# password loaded via AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE
Add the secret file:
echo 'your_smtp_password' > authelia/secrets/SMTP_PASSWORD
chmod 600 authelia/secrets/SMTP_PASSWORD
Add to the authelia service environment: block in docker-compose.yml:
- AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE=/secrets/SMTP_PASSWORD
Restart and check for a successful startup message:
docker compose restart authelia && docker compose logs -f authelia
Expect: Notifier SMTP startup check successful
Security notes
.env,authelia/secrets/*,authelia/users_database.yml, andauthelia/db.sqlite3*are all gitignored. Rungit statusbefore every commit to confirm nothing sensitive is staged.- Authelia is not port-mapped to the host. Only containers on
caddy_netcan reach it; only Caddy is configured to forward_auth there. - TOTP secrets in the SQLite DB are encrypted at rest with
STORAGE_ENCRYPTION_KEY. Back up both the DB and the key file -- losing either means every user must re-enroll TOTP. regulationis per-user account lockout; fail2ban is per-IP. Both are on.
Troubleshooting
Redirect loop between a site and auth.example.com
Cookie domain mismatch. The domain: under session.cookies[] must be the
bare root domain (example.com), and every protected site must be a subdomain
of it served over HTTPS. Mixed HTTP/HTTPS won't work; the session cookie is
Secure.
"access denied" with no login prompt
default_policy: deny -- a request reached Authelia with no matching rule for that domain. Add a rule under access_control.rules in authelia/configuration.yml:
- domain: 'yoursite.{{ env "DOMAIN" }}'
policy: 'two_factor'
Then restart Authelia:
docker compose restart authelia
Authelia container restarts forever
docker compose logs authelia | head -50
Most often: missing/empty secret files in authelia/secrets/, bad YAML in
configuration.yml, or an invalid argon2 hash in users_database.yml.
Caddy can't resolve authelia
Caddy isn't on caddy_net. Add this to your Caddy service in its compose file:
services:
caddy:
networks:
- caddy_net
networks:
caddy_net:
external: true
Then recreate the Caddy container:
docker compose up -d caddy
fail2ban bans don't block traffic
fail2ban is writing to the wrong iptables chain. With dockerized Caddy you
need chain = DOCKER-USER (already set in the shipped jail files). Verify:
sudo iptables -L DOCKER-USER -n
# Should show f2b-* jump rules.
fail2ban: authelia jail missing / "No such file" on authelia.log
Docker created a directory at ./authelia/authelia.log instead of a file
because the file didn't exist when the container started:
docker compose down fail2ban
rm -rf authelia/authelia.log # remove the directory Docker created
touch authelia/authelia.log # create as an empty file
docker compose up -d fail2ban
Frigate still shows its own login after switching to proxy auth
- Confirm
auth.enabled: Falseinfrigate_config/config.yml. - Confirm
trusted_proxiessubnet matches yourcaddy_netsubnet:docker network inspect caddy_net | grep -A2 '"Config"' - Restart Frigate:
docker compose restart frigate(in your Frigate stack).