Coolify에 Authelia 적용하기: 모든 도커 서비스에 SSO·2FA 붙이는 법
Coolify에 인증 서버 Authelia를 배포하고 caddy 포워드 인증으로 대시보드·관리도구 같은 다른 서비스 앞단에 SSO와 2단계 인증(2FA)을 붙이는 방법을 단계별로 정리했습니다. 배포부터 설정, 트러블슈팅, 외부 노출 보안까지.
⚠️ 시작 전 배경지식 & 경고
본격적으로 들어가기 전에 세 가지 개념만 짚고 가겠습니다.
- 포워드 인증(Forward Auth): 리버스 프록시(여기서는 caddy)가 들어오는 요청을 백엔드로 보내기 전에, 먼저 인증 서버(Authelia)에 "이 사람 통과시켜도 돼?"라고 물어보는 방식입니다. 인증되지 않은 사용자는 로그인 페이지로 리다이렉트되고, 통과한 사용자만 실제 서비스에 도달합니다. 서비스 자체에 로그인 기능이 없어도 앞단에서 한 겹 막을 수 있습니다.
- Coolify의 내장 caddy: Coolify는 기본 프록시로 caddy을 사용하며, 서비스마다 라우터·미들웨어 라벨을 자동 생성합니다. 그리고 라벨로 매번 컨테이너를 재생성하지 않도록 동적 설정(Dynamic Configuration) 도 지원합니다. Authelia 미들웨어는 이 동적 설정에 한 번 정의해 두고, 보호할 서비스의 라우터에 갖다 붙이는 식으로 씁니다.
- Authelia의 역할: Authelia는 직접 인터넷에 노출되는 서비스가 아니라, 프록시 뒤에서 인증·인가를 담당하는 컨트롤 플레인입니다. 그래서 Authelia 자체의 보안 설정(시크릿, 접근 정책)이 곧 전체 서비스의 보안 수준이 됩니다.
경고: 인증 레이어를 붙였다고 외부 노출이 안전해지는 게 아닙니다. HTTPS 종단, 강한 시크릿, 접근 정책, 정기 업데이트가 함께 가야 합니다. 마지막 [외부 노출 심화]와 [보안 체크리스트]를 꼭 읽으세요.
1. 문제 제기
Coolify로 서비스를 하나둘 올리다 보면 금세 이런 상황이 됩니다. 대시보드, 모니터링 도구, 관리 패널, 내부용 앱… 각각 로그인 방식이 제각각이고, 어떤 건 아예 로그인이 없습니다. 외부에서 접근 가능하게 열어 두면 그대로 무방비입니다.
서비스마다 따로 계정을 만들고 비밀번호를 관리하는 건 번거롭고, 보안 관점에서도 일관성이 없습니다. 원하는 건 단순합니다. 로그인은 한 번, 보호는 전 서비스에. Authelia를 Coolify 앞단에 두면, 한 곳에서 인증·2단계 인증(2FA)·접근 정책을 관리하고 새로 올리는 서비스도 라벨 한 줄로 보호할 수 있습니다.
이 글에서는 "도커는 써봤지만 Authelia·caddy 포워드 인증은 처음"인 독자를 가정하고, Coolify 환경에 맞춰 단계별로 진행합니다.
2. 해결 과정: 설치
2-1. 사전 준비
- 도메인 하나와, Authelia 포털용 서브도메인(예:
auth.example.com). 보호할 서비스도 같은 루트 도메인의 서브도메인(예:app.example.com)을 쓰는 게 쿠키 공유 측면에서 가장 간단합니다. - DNS가 Coolify 서버를 가리키고, Coolify에서 해당 도메인으로 HTTPS(Let's Encrypt)가 정상 발급되는 상태.
- Coolify 기본 프록시가 caddy 인지 확인(Servers → Proxy). 이 글은 caddy 기준입니다.
coolify docker network authelia 생성해야합니다.

2-2. 시크릿과 비밀번호 해시 먼저 만들기
Authelia는 세 가지 시크릿이 필요합니다: 세션 시크릿, 저장소 암호화 키, 비밀번호 재설정용 JWT 시크릿. 각각 충분히 긴 랜덤 문자열로 만듭니다.
# 랜덤 시크릿 3개 생성 (각각 따로 보관)
openssl rand -hex 64 # session secret
openssl rand -hex 64 # storage encryption key
openssl rand -hex 64 # identity validation JWT secret
사용자 비밀번호는 평문으로 저장하지 않고 argon2id 해시로 넣습니다. Authelia 이미지로 바로 생성할 수 있습니다.
2-3. Coolify에 Authelia 배포
Coolify에서 New Resource → Docker Compose Empty 를 선택하고, 아래 Compose를 붙여넣습니다. 이미지 태그는 :latest가 아니라 버전을 고정합니다(브레이킹 체인지 방지 — 이건 Authelia 팀의 공식 권고이기도 합니다).
services:
authelia:
image: 'authelia/authelia:latest'
container_name: authelia
volumes:
- '/root/volumes/userdata/authelia:/config'
environment:
- TZ=Asia/Seoul
restart: unless-stopped
networks:
- authelia-net
healthcheck:
test:
- CMD
- wget
- '--quiet'
- '--tries=1'
- '--spider'
- 'http://127.0.0.1:9091/api/health'
interval: 10s
timeout: 5s
retries: 5
networks:
authelia-net:
external: true배포 후 컨테이너가 뜨면, 설정 파일을 채울 차례입니다.
2-4. configuration.yml 작성
/config/configuration.yml(위 볼륨 기준 호스트의 ./authelia/config/configuration.yml)에 아래 최소 설정을 둡니다. 이건 출발점 템플릿이며, 전체 스키마는 Authelia 공식 "Get Started" 문서로 최종 검증하세요.
theme: dark
server:
address: tcp://0.0.0.0:9091
log:
level: info
identity_validation:
reset_password:
jwt_secret: "openssl_rand_hex_32_첫번째_결과값"
session:
secret: "openssl_rand_hex_32_두번째_결과값"
cookies:
- domain: "도메인"
authelia_url: "https://auth.도메인"
expiration: 1h
inactivity: 10m
remember_me: 1M
regulation:
max_retries: 3 # 무차별 대입 차단
find_time: '2 minutes'
ban_time: '5 minutes'
storage:
encryption_key: "openssl_rand_hex_32_세번째_결과값"
local:
path: /config/db.sqlite3
notifier:
disable_startup_check: true #true/false
smtp:
username: youremail@gmail.com #your email address
password: Y0uRp@55W0rD! #your email password
host: smtp.gmail.com #email smtp server
port: 587 #email smtp port
sender: youremail@gmail.com
identifier: localhost
subject: "[Authelia] {title}" #email subject
startup_check_address: youremail@gmail.com
disable_require_tls: false
disable_html_emails: false
tls:
skip_verify: false
minimum_version: TLS1.2
access_control:
default_policy: deny
rules:
- domain: "*.도메인" # <- 모든 서브도메인 허용
policy: one_factor # <- two_factor 2FA하게 되려면 위 smtp설정은 필수
authentication_backend:
file:
path: /config/users_database.yml
password:
algorithm: argon2id핵심 포인트 몇 가지를 부연합니다.
access_control.default_policy: 'deny'— 화이트리스트 방식입니다. 규칙에 없는 도메인은 전부 차단되므로, 보호할 서비스를rules에 하나씩 추가해야 합니다.one_factor(아이디·비번),two_factor(+2FA),bypass(인증 없음),deny(차단)를 도메인별로 줍니다.session.cookies[].domain과 보호 대상 서비스가 같은 루트 도메인이어야 SSO 쿠키가 공유됩니다.notifier.filesystem은 2FA 등록·비밀번호 재설정 메일을 파일로 떨굽니다(테스트용). 운영에서는notifier.smtp로 실제 메일 발송을 설정하세요.
2-5. users_database.yml 작성
/config/users_database.yml에 사용자와 2-2에서 만든 해시를 넣습니다.
users:
myuser: # 원하는 아이디로 변경
displayname: "My User"
password: "위에서_생성한_argon2_해시값"
email: myuser@example.com
groups:
- admins설정·사용자 파일을 채웠으면 Coolify에서 Authelia를 재배포(Redeploy) 해 설정을 다시 읽게 합니다.
2-6. 테스트용 서비스 설치
services:
metube:
image: 'ghcr.io/alexta69/metube:latest'
environment:
- UID=1000
- GID=1000
volumes:
- 'metube-downloads:/downloads'
networks:
- authelia-net
healthcheck:
test:
- CMD
- curl
- '-f'
- 'http://127.0.0.1:8081'
interval: 2s
timeout: 10s
retries: 15
filebrowser:
image: 'hurlenko/filebrowser:latest'
container_name: mutubefilebrowser
user: '1000:1000'
security_opt:
- 'no-new-privileges:true'
restart: 'on-failure:5'
volumes:
- 'metube-downloads:/data'
networks:
- authelia-net
volumes:
metube-downloads: null
networks:
authelia-net:
external: true
2-7. dynamic configurations 연결


3. 동작 확인

