---
id: security
slug: /security
description: Maintainerr has no built-in authentication. How to run it safely, and how to put authentik in front of it.
title: Security & Authentication
---
:::danger
Maintainerr has **no authentication of any kind**. Every API endpoint is
unauthenticated. Anyone who can reach the port can read your configuration, run
rules, and delete media. Never expose a Maintainerr instance directly to the
internet.
:::
## What an unauthenticated instance exposes
This is not theoretical. With plain network access to Maintainerr, a caller can:
- `GET /api/settings/database/download` - download the entire SQLite database,
including your Plex token, Jellyfin/Emby API key, Radarr/Sonarr API keys,
Seerr, Tautulli, TMDB, TVDB, and download-client credentials, all in plain
text.
- `POST /api/collections/handle` and `POST /api/rules/:id/execute` - trigger
collection handling, which **deletes media** from your library and your \*arr
instances.
- `POST /api/settings/...` - repoint Maintainerr at a different Plex, Jellyfin,
Emby, or \*arr server.
- `GET /api/logs/files/:file` - read the log files.
The settings API masks secrets in its JSON responses, but the database download
does not, so masking is not a protection boundary.
### Local network exposure counts as exposure
Maintainerr sends `Access-Control-Allow-Origin` reflecting whatever origin asks.
Combined with the lack of authentication, that means a web page you visit in a
browser on your LAN can read Maintainerr's API on `localhost` or a private
address and exfiltrate the response. Browsers are gradually restricting requests
from public sites to private networks, but that is not a defence you should rely
on today.
The practical rule: **treat the Maintainerr port as a secret**. Bind it to
loopback or an internal Docker network, and let only your reverse proxy reach
it.
## The recommended setup
Maintainerr is designed to sit behind something that authenticates for it. It
does not implement authentication itself, and there is no plan to. Put an
identity-aware reverse proxy in front, and do not publish the container port.
```mermaid
architecture-beta
service client(server)[Client]
service revprox(server)[Reverse Proxy]
service outpost(server)[Outpost]
service app(server)[Maintainerr]
service idp(server)[Identity Provider]
client:R -- L:revprox
revprox:R -- L:outpost
outpost:R -- L:app
outpost:T -- B:idp
```
In `docker-compose.yml`, that means no `ports:` mapping on the Maintainerr
service at all. Your proxy reaches it over the shared Docker network:
```yaml
services:
maintainerr:
image: ghcr.io/maintainerr/maintainerr:latest
container_name: maintainerr
volumes:
- ./data:/opt/data
networks:
- proxy
restart: unless-stopped
# Deliberately no "ports:" - only the reverse proxy should reach 6246.
networks:
proxy:
external: true
```
If you need direct access for troubleshooting, bind to loopback only
(`127.0.0.1:6246:6246`) rather than `6246:6246`.
## Authenticating with authentik
[authentik](https://goauthentik.io/) is our recommended option. Its **Proxy
Provider** authenticates requests before they reach Maintainerr, so Maintainerr
needs no configuration and no code changes. authentik supports two shapes:
| Mode | Use when |
| ----------------------------------- | ------------------------------------------------------------------------------- |
| **Forward auth (single application)** | You already run nginx, SWAG, Traefik, or Caddy. Recommended for most setups. |
| **Proxy** | You want the authentik outpost itself to be the reverse proxy. |
Full step-by-step setup lives in the authentik documentation:
[Integrate with Maintainerr](https://integrations.goauthentik.io/media/maintainerr/).
### nginx forward auth
Add the authentik `auth_request` block to your existing Maintainerr server block
(see [Reverse Proxy](/reverseproxy) for the base configuration). Two Maintainerr
specifics matter:
```nginx
# Maintainerr streams live logs and events over Server-Sent Events. nginx must
# not buffer those responses or the Logs page and live task updates will stall.
location ~ ^/api/(logs|events)/stream {
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 24h;
proxy_pass http://maintainerr:6246;
auth_request /outpost.goauthentik.io/auth/nginx;
error_page 401 = @goauthentik_proxy_signin;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
}
```
The authentik outpost also emits fairly large response headers. If you see
`upstream sent too big header while reading response header from upstream`,
raise the buffers:
```nginx
proxy_buffers 8 16k;
proxy_buffer_size 32k;
```
### Traefik, Caddy, and Envoy
authentik publishes ready-made middleware snippets for each. Maintainerr needs
nothing beyond the standard configuration, other than making sure streaming
responses are not buffered. See
[authentik forward auth](https://docs.goauthentik.io/add-secure-apps/providers/proxy/forward_auth).
### Calling the API through authentik
Once the outpost is in front, scripts and automations authenticate to the
outpost rather than to Maintainerr:
- **Bearer**: send `Authorization: Bearer <token>` where the token is issued for
the proxy provider.
- **Basic**: send the reserved username `goauthentik.io/token` with an app
password as the password.
Persist the cookies the outpost returns, otherwise every request re-authenticates
against authentik and adds load.
Leave the health endpoints reachable without authentication if an external
monitor needs them. `/api/health/live` and `/api/health/ready` return no
sensitive data. Add them to the provider's **Unauthenticated Paths** field:
```
^/api/health(/.*)?$
```
:::warning
Paths on the **Unauthenticated Paths** allowlist bypass authentik entirely.
Never add anything under `/api/settings`, `/api/collections`, `/api/rules`, or
`/api/logs`.
:::
## Other options
authentik is the recommendation, not a requirement. Anything that terminates
authentication before Maintainerr works:
- [Authelia](https://www.authelia.com/) - forward auth, same shape as authentik.
- [Tinyauth](https://tinyauth.app/) - lighter weight, single-binary.
- [Cloudflare Access](https://www.cloudflare.com/zero-trust/products/access/) -
no self-hosted identity provider needed.
- **Reverse proxy basic auth** - nginx `auth_basic`, Caddy `basic_auth`. Crude,
but far better than nothing.
- **No public exposure at all** - reach Maintainerr over
[Tailscale](https://tailscale.com/), WireGuard, or your existing VPN. This is
the simplest safe answer and needs no proxy.
## Rotating credentials after exposure
If your instance was reachable without authentication, assume everything in it
leaked and rotate:
- Plex: sign out of all devices, then reconnect Maintainerr.
- Jellyfin/Emby: delete and reissue the API key.
- Radarr, Sonarr, Sportarr, Seerr, Tautulli, Tracearr: reissue each API key.
- TMDB and TVDB API keys.
- Download-client (qBittorrent) password.
- Any notification agent webhook URLs and SMTP credentials.
## About the "API key" in settings
The **API key** shown under Settings is not an authentication credential for
Maintainerr's own API. Setting or regenerating it does not protect any endpoint.
It exists for internal use only. Do not treat it as a security control.
Summary
Add a dedicated Security & Authentication page, and recommend authentik as the supported way to put authentication in front of Maintainerr.
Background: authentication is one of the most requested features (feature board post #6, previously Maintainerr#623 and Maintainerr#990). Implementing authentication inside Maintainerr means owning sessions, cookies, password storage, and the CVE surface that comes with them. Delegating to an identity-aware reverse proxy avoids all of it, and it works today with no code changes.
Why this is a docs change and not a code change
I looked into whether Maintainerr could ship built-in authentik support. It cannot, and the reason is structural rather than a matter of effort: authentik has no plugin API or app-side SDK. It integrates only through standard protocols.
So the Proxy Provider path is both the only zero-code option and the one that matches how the rest of the *arr ecosystem documents authentik. Docs are the right deliverable.
Proposed page
New
docs/Security.md, in the sidebar under Getting Started next toreverseproxy:GET /api/settingsis not a protection boundarydocker-compose.ymlexample with noports:mapping)The existing danger callout in
docs/API.mdwould link here instead of standing alone.Two Maintainerr-specific details worth documenting
These came out of reading the server source and are easy to get wrong:
Server-Sent Events must not be buffered. Maintainerr streams live logs and task events from
/api/logs/streamand/api/events/streamastext/event-stream, and does not sendX-Accel-Buffering: no. Under nginx forward auth the Logs page and live task progress appear to hang unlessproxy_buffering offis set for those paths. authentik's own proxy mode is fine here, since the outpost flushes immediately.Nothing needs to be exempted from authentication. Maintainerr has no inbound webhook receivers (all integrations are outbound), the UI and API are same-origin on a single port, and the Docker
HEALTHCHECKruns inside the container so it bypasses the proxy entirely. The only path worth allowlisting is/api/health/*, and only if an external monitor needs it.Upstream authentik docs
website/integrations/media/in goauthentik/authentik currently has sonarr, tautulli, seerr, and jellyfin but no maintainerr. Worth contributing an Integrate with Maintainerr page there too, so the setup is discoverable from both sides.Status
Drafts for both pages are written and ready to open as PRs. Filing this first so the approach can be agreed before review.
Drafts
Both pages in full, verbatim, ready to lift into PRs.
docs/Security.md (Maintainerr_docs)
website/integrations/media/maintainerr/index.md (goauthentik/authentik)