---
title: "Deploy Authelia | (Just Updated) SSO and OIDC Provider, Forward-Auth, Admin Login and Agent Token Ready"
description: "Authelia SSO server: OIDC provider, forward-auth, admin login preset"
category: "Authentication"
url: https://railway.com/deploy/authelia-or-just-updated-sso-and-oidc-pr
---

# Deploy Authelia | (Just Updated) SSO and OIDC Provider, Forward-Auth, Admin Login and Agent Token Ready

Authelia SSO server: OIDC provider, forward-auth, admin login preset

**[Deploy Authelia | (Just Updated) SSO and OIDC Provider, Forward-Auth, Admin Login and Agent Token Ready on Railway](https://railway.com/template/authelia-or-just-updated-sso-and-oidc-pr)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/authelia-or-just-updated-sso-and-oidc-pr/manifest.json

- **Creator:** SuperSlowSloth
- **Category:** Authentication

## Template content

### authelia

- **Image:** ghcr.io/authelia/authelia:4.39.28@sha256:bd97cff4fcbf715b5ff1f9ae286afbe6033afce385302520b0368122d43a6f54
- **Start command:** `/bin/sh -c 'set -eu
umask 077
C=/config
mkdir -p "$C/conf.d"
PORT="${PORT:-8080}"
DOMAIN="${RAILWAY_PUBLIC_DOMAIN:-}"
# Authelia rejects a cookie domain without a dot, so a host with none (a local smoke test) gets a placeholder.
case "$DOMAIN" in *.*) ;; *) DOMAIN=authelia.example.com ;; esac
ADMIN_USER=admin
CLIENT_ID=agent

# Users file: written once, so a redeploy (or a password changed through Authelia) is never overwritten.
if [ ! -s "$C/users_database.yml" ]; then
  HASH="$(authelia crypto hash generate argon2 --password "$ADMIN_PASSWORD" | sed -n "s/^Digest: //p")"
  cat > "$C/users_database.yml" <<USERS
users:
  $ADMIN_USER:
    disabled: false
    displayname: "Administrator"
    password: "$HASH"
    email: "admin@$DOMAIN"
    groups:
      - admins
USERS
  echo "[boot] admin user created"
else
  echo "[boot] admin user already exists"
fi

# OIDC signing key: generated once, kept on the volume so issued tokens survive a redeploy.
if [ ! -s "$C/oidc.key" ]; then
  authelia crypto pair rsa generate --bits 2048 --directory "$C" --file.private-key oidc.key --file.public-key oidc.pub > /dev/null
  echo "[boot] OIDC signing key generated"
fi

# The agent client secret is stored hashed; hash it again each boot so rotating the variable takes effect.
CLIENT_HASH="$(authelia crypto hash generate pbkdf2 --variant sha512 --password "$OIDC_CLIENT_SECRET" | sed -n "s/^Digest: //p")"
KEY="$(sed "s/^/          /" "$C/oidc.key")"

cat > "$C/configuration.yml" <<CONFIG
theme: auto
server:
  address: "tcp://0.0.0.0:$PORT/"
  endpoints:
    authz:
      forward-auth:
        implementation: ForwardAuth
        authn_strategies:
          - name: HeaderAuthorization
            schemes:
              - Bearer
          - name: CookieSession
      auth-request:
        implementation: AuthRequest
        authn_strategies:
          - name: HeaderAuthorization
            schemes:
              - Bearer
          - name: CookieSession
log:
  level: info
authentication_backend:
  file:
    path: $C/users_database.yml
access_control:
  default_policy: one_factor
session:
  cookies:
    - domain: "$DOMAIN"
      authelia_url: "https://$DOMAIN"
storage:
  local:
    path: $C/db.sqlite3
notifier:
  filesystem:
    filename: $C/notification.txt
identity_providers:
  oidc:
    jwks:
      - key_id: railway
        algorithm: RS256
        use: sig
        key: |
$KEY
    clients:
      - client_id: $CLIENT_ID
        client_name: Agent
        client_secret: "$CLIENT_HASH"
        public: false
        audience:
          - "https://$DOMAIN/"
        token_endpoint_auth_method: client_secret_basic
        authorization_policy: one_factor
        grant_types:
          - client_credentials
        scopes:
          - authelia.bearer.authz
CONFIG

exec authelia --config "$C/configuration.yml" --config "$C/conf.d"'`
- **Health check:** /api/health
- **Public domain:** Yes

## Documentation

# Deploy and Host Authelia on Railway

**Built for an LLM agent to deploy and use.** There is no setup wizard and nothing to fill in: the admin password, the OIDC client secret and every Authelia secret are generated at deploy time, the admin user and one OIDC client exist from the first boot, and the contract below lists every URL, variable name and the one script that proves it works.

Authelia is an open-source (Apache 2.0) single sign-on and authorization server. It puts a login portal in front of web apps through forward-auth and auth-request (Traefik, Caddy, nginx, Envoy), and it is a certified OpenID Connect 1.0 provider for apps that speak OIDC. Machines authenticate with a bearer token from the `client_credentials` grant, so an agent can pass the same gate a person does.

## About Hosting Authelia

This template runs one service, the official **Authelia 4.39.28** image (`ghcr.io/authelia/authelia`, pinned by digest), with a volume at `/config`. On every boot the start command writes `/config/configuration.yml` from the service variables, so the config never drifts from them. The volume keeps the users file (`users_database.yml`), the SQLite database (sessions, OIDC state), the OIDC signing key and the filesystem notifier file (`notification.txt`, where password-reset and device-enrolment links are written because no mail server is attached). The first boot creates the user `admin` (group `admins`, password from `ADMIN_PASSWORD`) and the OIDC client `agent` (grant `client_credentials`, scope `authelia.bearer.authz`, secret from `OIDC_CLIENT_SECRET`, audience `https://PUBLIC_DOMAIN/`). The default access policy is `one_factor`. Allow about a minute for the first deploy to turn healthy.

Drop extra Authelia YAML files into `/config/conf.d`: they are merged over the generated config on every boot, which is where access-control rules, more OIDC clients, an SMTP notifier or an LDAP backend go. The generated config sets the cookie domain to the public domain of the service; to protect apps on your own domain, override `session.cookies` there.

### Agent contract

- Public URL: `https://` plus the `RAILWAY_PUBLIC_DOMAIN` of the `authelia` service, also published as the variable `AUTH_PUBLIC_URL`; used below as `AUTH_URL`. The login portal is at `/`, the OIDC issuer is `AUTH_URL` itself with discovery at `/.well-known/openid-configuration`.
- Private URL: `http://authelia.railway.internal:8080` (also the variable `AUTH_PRIVATE_URL`), for forward-auth and auth-request calls from other services in the project
- Credentials: `ADMIN_PASSWORD` (user `admin`) and `OIDC_CLIENT_SECRET` (client id `agent`). Both stay the same across redeploys.
- Auth: the portal login is `POST /api/firstfactor` with JSON `username` and `password`. API clients use HTTP basic auth `agent:$OIDC_CLIENT_SECRET` on `POST /api/oidc/token` with `grant_type=client_credentials`, `scope=authelia.bearer.authz` and `audience=$AUTH_URL/` (the audience must equal the resource being accessed, with the trailing slash), then present `Authorization: Bearer $TOKEN` to the protected route. A request with no or a bad token is redirected to the portal (302).
- Health: `GET /api/health` returns 200 with no credentials

```bash
# run with AUTH_URL, ADMIN_PASSWORD and OIDC_CLIENT_SECRET set
FA="-H X-Forwarded-Proto:https -H X-Forwarded-Host:${AUTH_URL#https://} -H X-Forwarded-URI:/ -H X-Forwarded-Method:GET"
curl -s -o /dev/null -w 'health %{http_code}\n' "$AUTH_URL/api/health"
curl -s -o /dev/null -w 'admin-login %{http_code}\n' -X POST -H 'Content-Type: application/json' -d "{\"username\":\"admin\",\"password\":\"$ADMIN_PASSWORD\"}" "$AUTH_URL/api/firstfactor"
curl -s -o /dev/null -w 'wrong-password %{http_code}\n' -X POST -H 'Content-Type: application/json' -d '{"username":"admin","password":"wrong"}' "$AUTH_URL/api/firstfactor"
TOKEN=$(curl -s -u "agent:$OIDC_CLIENT_SECRET" -d grant_type=client_credentials -d scope=authelia.bearer.authz -d "audience=$AUTH_URL/" "$AUTH_URL/api/oidc/token" | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')
echo "token ${#TOKEN} chars"
curl -s -u "agent:$OIDC_CLIENT_SECRET" -d "token=$TOKEN" "$AUTH_URL/api/oidc/introspection" | grep -o '"active":true'
curl -s -o /dev/null -w 'forward-auth-with-token %{http_code}\n' -H "Authorization: Bearer $TOKEN" $FA "$AUTH_URL/api/authz/forward-auth"
curl -s -o /dev/null -w 'forward-auth-no-token %{http_code}\n' $FA "$AUTH_URL/api/authz/forward-auth"
curl -s "$AUTH_URL/.well-known/openid-configuration" | grep -o '"issuer":"[^"]*"'
```

A fresh deploy prints `health 200`, `admin-login 200`, `wrong-password 401`, a token length above zero, `"active":true`, `forward-auth-with-token 200`, `forward-auth-no-token 302` and the issuer equal to `AUTH_URL`. In a reverse proxy, point forward-auth at `http://authelia.railway.internal:8080/api/authz/forward-auth` (Traefik `forwardAuth`, Caddy `forward_auth`) or `/api/authz/auth-request` (nginx `auth_request`).

## Why Deploy Authelia on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying Authelia on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.

- **Agent-ready** - generated admin password and OIDC client secret, a contract block and one working script; no wizard before the first login or token.
- **OIDC and forward-auth in one service** - the same server signs people in at the portal and issues bearer tokens to machines.
- **Login works from boot** - the admin user and the `agent` client exist before the first request.
- **State survives redeploys** - users, sessions, OIDC state and the signing key sit on a volume, so issued tokens stay valid.
- **Extendable** - YAML dropped in `/config/conf.d` is merged on every boot.
- **Pinned image** - pinned by digest, so a redeploy never changes the server version underneath you.

## Common Use Cases

- **Single sign-on for self-hosted apps** - put one login in front of dashboards and tools through a reverse proxy.
- **OpenID Connect provider** - sign in to Grafana, Gitea, Nextcloud, Outline and any OIDC-capable app with one account.
- **Machine-to-machine access** - give an AI agent a `client_credentials` token to pass the same gate as a user.

## Dependencies for Authelia Hosting

- Nothing else: Authelia keeps users, sessions and OIDC state in SQLite on the volume at `/config`.

### Deployment Dependencies

- [authelia/authelia](https://github.com/authelia/authelia) - upstream project
- [Authelia documentation](https://www.authelia.com/configuration/prologue/introduction/) - access control, OIDC clients and proxy integration


## Similar templates

- [Keycloak](https://railway.com/deploy/mSwigX) — [2026-11-04] Keycloak with keywind theme + Apple & Discord providers
- [lua-protector](https://railway.com/deploy/lua-protector) — Test deployed my project first
- [bknd](https://railway.com/deploy/p4nTYL) — Feature-rich yet lightweight backend

Open this page in a browser: https://railway.com/deploy/authelia-or-just-updated-sso-and-oidc-pr
