피드로 돌아가기
Deploying Mercure alongside Caddy on a shared VPS
Dev.toDev.to
DevOps

Mercure를 기존 Caddy 서버와 같은 VPS에 배포하면서 포트 충돌·SSE 스트림 버퍼링·압축 문제를 Ansible 자동화로 해결한 사례

Deploying Mercure alongside Caddy on a shared VPS

Russell Jones2026년 3월 29일4intermediate

Context

Mercure는 자체 Caddy 서버를 내장하고 443 포트와 2019 포트를 요구한다. 기존에 Caddy를 실행 중인 VPS에서 두 개의 Caddy 프로세스가 같은 포트를 두고 경합하게 된다. SSE 스트림을 역프록시 할 때 기본 설정으로는 응답을 버퍼링하기 때문에 클라이언트가 이벤트를 수신하지 못한다.

Technical Solution

  • 포트 충돌 해결: Mercure의 auto_https를 비활성화하고 admin API를 localhost:2039로 변경해 주 Caddy와의 충돌을 제거
  • SSE 스트림 프록시: 메인 Caddy에서 flush_interval -1 설정을 추가해 즉시 바이트를 전송하도록 강제
  • 압축 제외: encode 지시문에 matcher를 사용해 /.well-known/mercure* 경로를 gzip에서 제외
  • BoltDB 디렉토리 생성: /home/{deploy_user}/.local/share/caddy 디렉토리를 0755 권한으로 사전 생성
  • Systemd 서비스 배포: Ansible 역할을 통해 환경 파일과 Caddyfile을 읽는 systemd unit 생성
  • JWT 인증 구성: Ansible Vault에 MERCURE_JWT_SECRET을 저장하고 Publisher/Subscriber에서 동일 시크릿으로 서명
  • 헬스 체크 엔드포인트: /healthz 경로에서 200을 반환하는 모니터링 포인트 제공

Key Takeaway

SSE 기반 실시간 프로토콜을 역프록시할 때 flush_interval -1은 필수 설정이며, 응답 압축과 스트림 버퍼링의 상충 관계를 반드시 고려해야 한다. 공유 인프라에서 동일한 기술 스택의 두 프로세스를 배포할 때는 포트·관리자 API·인증서 발급을 명시적으로 분리해야 운영 안정성을 확보할 수 있다.


Caddy 리버스 프록시 뒤에 SSE 기반 서비스를 배포하는 환경에서 flush_interval -1을 적용하면 클라이언트가 실시간 이벤트를 정상 수신할 수 있다. 또한 Mercure 경로에 대해 gzip 압축을 명시적으로 제외하면 스트림 버퍼링으로 인한 이벤트 전달 지연을 방지할 수 있다.

원문 읽기
Deploying Mercure alongside Caddy on a shared VPS | Devpick