Security Hardening
The default Compose templates work out of the box without any special security configuration. For users who want additional container-level hardening, the template below adds capability restrictions, privilege escalation prevention, and a noexec tmpfs mount.
What the settings do
Section titled “What the settings do”| Setting | Effect |
|---|---|
security_opt: no-new-privileges:true |
Prevents processes inside the container from gaining additional privileges (e.g., via setuid binaries) |
cap_drop: ALL |
Drops all Linux capabilities by default |
cap_add: [...] |
Re-adds only the capabilities each service actually needs |
tmpfs: /tmp:noexec,nosuid |
Mounts /tmp as a tmpfs with execution and SUID disabled |
Built-in application protections
Section titled “Built-in application protections”These are always on and need no configuration; they are listed so you know what the app does on its own and where the environment variables above fit in.
| Protection | What it does |
|---|---|
| Session revocation | Every request, page or API, checks the session cookie against the admin record. Changing credentials, linking/unlinking SSO, or logging out increments a version counter and every outstanding cookie stops working immediately — on all devices. A stolen cookie is dead the moment you log out. |
| Session lifetime | 30 days, enforced on both the encrypted payload and the cookie’s Max-Age. |
Secure cookie |
Set automatically on HTTPS requests (see COOKIE_SECURE in Installation). |
| CSRF | Every state-changing /api/* request (POST, PUT, PATCH, DELETE) must carry an Origin or Referer on the app’s own host; a request from another site is rejected with 403. Combined with the SameSite=Lax cookie this also blocks a sibling app on the same parent domain. Requests with neither header (curl, scripts) are allowed — they carry no ambient cookie. |
| Rate limiting | Login, Plex PIN, SSO and password-change endpoints allow 10 attempts per 15 minutes per client, plus a global ceiling of 60 per 15 minutes across all clients that no forged header can escape. See TRUST_PROXY_HEADERS. |
| Login timing | The local login spends the same bcrypt cost whether or not the username exists, so response time does not reveal which usernames are real. |
| Secret masking | API keys, tokens, the OIDC client secret, the AI key and the Discord webhook URL are never returned by the API once saved; the UI shows a placeholder and saving the placeholder keeps the stored value. |
| Content Security Policy | frame-ancestors 'none' and X-Frame-Options: DENY prevent clickjacking; scripts and connections are restricted to the app itself and plex.tv. The production build does not permit eval. |
Hardened Compose template
Section titled “Hardened Compose template”services: librariarr: image: ahembree/librariarr:latest container_name: librariarr ports: - "${LIBRARIARR_PORT:-3000}:3000" environment: - DATABASE_URL=postgresql://librariarr:${DB_PASSWORD}@librariarr-db:5432/librariarr - SESSION_SECRET=${SESSION_SECRET:-} - TZ=${TZ:-UTC} - PUID=${PUID:-1000} - PGID=${PGID:-1000} volumes: - ./config:/config security_opt: - no-new-privileges:true cap_drop: - ALL cap_add: - CHOWN - SETUID - SETGID - FOWNER tmpfs: - /tmp:noexec,nosuid healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] interval: 30s timeout: 5s start_period: 60s retries: 3 depends_on: librariarr-db: condition: service_healthy restart: unless-stopped
librariarr-db: image: postgres:18-alpine container_name: librariarr-db environment: POSTGRES_USER: librariarr POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_DB: librariarr volumes: - librariarr-db:/var/lib/postgresql security_opt: - no-new-privileges:true cap_drop: - ALL cap_add: - CHOWN - DAC_OVERRIDE - FOWNER - SETUID - SETGID tmpfs: - /tmp:noexec,nosuid healthcheck: test: ["CMD-SHELL", "pg_isready -U librariarr"] interval: 5s timeout: 5s retries: 5 restart: unless-stopped
volumes: librariarr-db:Applying to an external database setup
Section titled “Applying to an external database setup”If you use an existing PostgreSQL server, add the security settings to only the librariarr service:
services: librariarr: image: ahembree/librariarr:latest container_name: librariarr # ... your existing config ... security_opt: - no-new-privileges:true cap_drop: - ALL cap_add: - CHOWN - SETUID - SETGID - FOWNER tmpfs: - /tmp:noexec,nosuidRequired capabilities
Section titled “Required capabilities”Librariarr container — CHOWN, SETUID, SETGID, FOWNER: needed to set up file ownership for the PUID/PGID user mapping and write to the /config volume.
PostgreSQL container — CHOWN, DAC_OVERRIDE, FOWNER, SETUID, SETGID: needed by the PostgreSQL entrypoint to initialize the data directory and switch to the postgres user.