Security Advanced 15 min

Deploying Authelia: The Open Source Identity Provider

Host your own OIDC/OAuth2 Identity Provider with Authelia on Kubernetes. Secure your apps with 2FA, SSO, and fine-grained access control.

By Victor Robin Updated:

Introduction

Choosing a self-hosted identity provider was one of the most consequential decisions for BlueRobin. I evaluated Keycloak, Authentik, and Authelia over a weekend. Keycloak was too heavy for a homelab—its JVM footprint consumed 1.2GB of RAM just idling. Authentik was promising but still young. Authelia won because it runs in 60MB of RAM, has excellent OIDC support, and integrates natively with Traefik’s ForwardAuth middleware. Two years later, it handles authentication for every service in the cluster without a single unplanned outage.

Before we connect our applications, we need an Identity Provider (IdP). While you could use Auth0 or Azure AD B2C, strict data sovereignty requires self-hosting.

[GDPR Article 44 - Data Transfers] — European Union , 2018

Authelia is the gold standard for self-hosted auth.

[Authelia Documentation] — Authelia , 2024

It supports:

  • Hardware 2FA (YubiKey/WebAuthn).
[WebAuthn Specification] — W3C , 2021
  • OIDC/OAuth2 for modern apps (like our Blazor app).
  • ForwardAuth for legacy apps (proxied via Traefik).

What We’ll Build

  1. Authelia Deployment: A high-availability setup on Kubernetes.
  2. Redis Session Store: For distributing sessions across pod replicas.
  3. OIDC Configuration: Defining the clients that will trust Authelia.

Architecture Overview

flowchart TB
    subgraph K8s["☸️ Kubernetes Cluster"]
        Ingress["🌍 Traefik Ingress"]
        
        subgraph Auth["🔐 Authelia Namespace"]
            Pod1["Authelia Pod 1"]
            Pod2["Authelia Pod 2"]
            Redis["⚡ Redis (Sessions)"]
        end
        
        subgraph App["📦 Application Namespace"]
            WebApp["🐦 Web App"]
        end
    end

    User["👤 User"] --> Ingress
    Ingress -->|Auth Request| Pod1
    Pod1 <--> Redis
    Ingress -->|ForwardAuth| WebApp
    WebApp -->|OIDC Backchannel| Pod1

    classDef primary fill:#7c3aed,color:#fff
    classDef secondary fill:#06b6d4,color:#fff
    classDef db fill:#f43f5e,color:#fff
    classDef warning fill:#fbbf24,color:#000

    class WebApp,Pod1,Pod2 primary
    class Ingress secondary
    class Redis db
    class User warning

Section 1: The Configuration

Authelia’s configuration is dense.

[OpenID Connect Core 1.0] — OpenID Foundation , 2014

We focus here on the Identity Provider section, which activates OIDC.

# configuration.yml
identity_providers:
  oidc:
    # 1. Security Keys
    hmac_secret: "$env:OIDC_HMAC_SECRET"
    jwks:
      - key: |
          {{ secret "/secrets/oidc/private.pem" | mindent 10 "|" }}

    # 2. Clients (The Apps)
    clients:
      - client_id: my-web-client
        client_name: My Web App
        client_secret: "$env:BLUEROBIN_CLIENT_SECRET"
        public: false
        authorization_policy: two_factor # 2FA Mandatory
        redirect_uris:
          - https://web.bluerobin.local/signin-oidc
        scopes: [openid, profile, email, groups]
        token_endpoint_auth_method: client_secret_post

# 3. Access Control (The Rules)
access_control:
  default_policy: deny
  rules:
    # Public Login Page
    - domain: "auth.bluerobin.local"
      policy: bypass
    # API requires just a password (machine-to-machine)
    - domain: "api.bluerobin.local"
      policy: one_factor
    # Web App requires 2FA
    - domain: "*.bluerobin.local"
      policy: two_factor

Section 2: Kubernetes Deployment

[Kubernetes Secrets Best Practices] — Kubernetes , 2024

Run Authelia as a Deployment with 2 replicas for redundancy.

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: authelia
  namespace: authelia
spec:
  replicas: 2
  template:
    spec:
      containers:
        - name: authelia
          image: authelia/authelia:v4.38
          env:
            - name: AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET
              valueFrom:
                secretKeyRef:
                  name: authelia-secrets
                  key: OIDC_HMAC_SECRET
          volumeMounts:
            - name: config
              mountPath: /config
            - name: secrets
              mountPath: /secrets
      volumes:
        - name: config
          configMap:
            name: authelia-config

Section 3: Protecting Legacy Apps (Traefik ForwardAuth)

For apps that don’t support OIDC (like a simple static dashboard), use ForwardAuth.

[Traefik ForwardAuth Middleware] — Traefik Labs , 2024

Traefik asks Authelia “Is this user logged in?” before every request.

# middleware.yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: authelia-auth
  namespace: authelia
spec:
  forwardAuth:
    address: http://authelia.authelia.svc:9091/api/verify?rd=https://auth.bluerobin.local/
    trustForwardHeader: true
    authResponseHeaders:
      - Remote-User
      - Remote-Groups

Conclusion

You now have a sovereign Identity Provider. Your secrets stay in your cluster, your user data stays in your database, and you can enforce 2FA on any application—whether it supports OIDC natively or just relies on Traefik Middleware.

Running Authelia for two years has taught me that the initial setup complexity pays dividends every time a new service joins the cluster. Adding a new OIDC client takes five lines of YAML and a Flux reconciliation. Adding ForwardAuth to a legacy app takes three lines of Traefik annotations. The centralized audit log shows every authentication attempt across every service, which has been invaluable for debugging “I can’t log in” reports.

Next Steps:

Further Reading