포스트혼: 메일 서버 없는 셀프 호스팅 메일 통합 게이트웨이
셀프 호스팅 프로젝트를 위한 통합 발신 메일 릴레이 계층인 '포스트혼(Posthorn)'이 소개되었습니다. 이 도구는 사용자가 직접 메일 서버를 운영할 필요 없이, 다양한 앱의 발신 메일을 하나의 설정과 자격 증명으로 관리하여 Postmark, Resend 등의 트랜잭셔널 메일 제공자로 안전하게 중계합니다. 단일 Go 바이너리와 TOML 설정만으로 이메일 통합의 복잡성을 크게 줄여주는 오픈소스 솔루션입니다.
포스트혼(Posthorn): 셀프 호스팅 프로젝트를 위한 통합 발신 메일 계층
당신이 셀프 호스팅하는 모든 앱과 이미 선택한 트랜잭셔널 메일 제공자(Provider) 사이를 잇는 단 하나의 게이트웨이입니다. 3가지 수신 방식(HTTP 폼, HTTP API, SMTP), 5가지 전송 수단(Postmark, Resend, Mailgun, AWS SES, 아웃바운드 SMTP 릴레이)을 지원하며, 단일 Go 바이너리와 단일 TOML 설정 파일로 작동합니다.
실제 활용 사례: Hugo + Comentario · Ghost · Gitea · Umami 다이제스트 크론 · Cloudflare Worker
왜 이 도구가 필요한가? 2026년에 아무도 메일 서버를 직접 운영하고 싶어 하지 않습니다. 셀프 호스팅 운영자들은 Postmark, Resend, Mailgun 또는 AWS SES를 사용합니다. 저렴하고, 이메일 전달률을 적절히 처리해 주며, SPF / DKIM / DMARC / 반송 이메일 / 발신자 평판 관리와 같은 골치 아픈 일을 대신해 주기 때문입니다.
하지만 셀프 호스팅하는 모든 앱은 해당 서비스와 독립적으로 연동해야 합니다. 연락처 폼, Ghost 블로그의 관리자 이메일, Gitea 매직 링크, Mastodon 알림, 누군가 링크를 클릭할 때 비밀번호 재설정 이메일을 발송하는 Cloudflare Worker까지 모두 각각의 처리가 필요합니다. 각 앱은 고유한 API 키 복사본, 고유한 연동 코드, 재시도 및 반송 처리에 대한 고유한 예외 상황이 필요합니다. 동일한 발신 처리 로직이 스택 전체에 중복되는 것입니다. 게다가 아웃바운드 SMTP를 차단하는 클라우드 호스팅(DigitalOcean, AWS Lightsail, Linode, Vultr)에서는 SMTP만 지원하는 앱이 우회 방법 없이는 아예 작동하지 않습니다.
포스트혼이 이 간극을 메워주는 다리 역할을 합니다. 하나의 컨테이너, 하나의 설정, 하나의 자격 증명 세트면 충분합니다. 당신의 앱은 포스트혼을 바라보고, 포스트혼은 당신의 메일 제공자와 통신합니다.
앱의 연결 방식에 따른 포스트혼의 역할:
- HTTP 폼 (연락처 폼, 가입, 알림 웹훅): 허니팟(Honeypot) + Origin/Referer 확인 + 속도 제한 + 선택적 CSRF 적용, 이메일 템플릿화, 발송
- HTTP API 모드 (워커, 크론, 결제 핸들러, 내부 서비스): Authorization: Bearer 인증, JSON 본문, 멱등성(Idempotent) 재시도, 트랜잭셔널 발송을 위한 per-request to_override 지원
- SMTP 리스너 (Ghost, Gitea, Mastodon, Matrix, NextCloud, Authentik 등 SMTP를 내보내는 모든 앱): AUTH PLAIN 또는 클라이언트 인증서, STARTTLS 필수, 발신자 + 수신자 허용 목록, MIME 파싱, HTTP API를 통한 전달
이 세 가지 수신 경로는 하나의 transport.Message와 하나의 아웃바운드 제공자(Postmark, Resend, Mailgun, AWS SES 또는 아웃바운드 SMTP 릴레이 중 선택)로 통합됩니다.
포스트혼이 아닌 것들 (잘못된 접근을 피하기 위한 안내):
- 메일 서버가 아닙니다: 메일함 스토리지, IMAP/JMAP, DKIM 키 관리, MX 타겟 기능이 없습니다. (대안: Stalwart, Mailcow, iRedMail)
- 자체 아웃바운드 인프라가 아닙니다: 포스트혼은 사용자가 선택한 제공자를 통해 릴레이할 뿐, 자체 SMTP 클러스터를 운영하거나 IP 평판을 관리하지 않습니다. (대안: Postal, Hyvor Relay)
- 마케팅 이메일 플랫폼이 아닙니다: 목록 관리, 세분화, 캠페인 대시보드 기능이 없습니다. (대안: Listmonk)
- 웹메일 / 메일함 UI가 아닙니다: 이메일을 읽기 위한 인터페이스가 없습니다. (대안: Roundcube, Snappymail - 메일 서버와 함께 사용)
이 도구는 셀프 호스팅 앱과 이미 선택한 트랜잭셔널 제공자 사이의 통합 계층 역할을 하는 핵심 매개체입니다.
문서화:
posthorn.dev에서 시작하기, 설정 참조, 배포 가이드, 기능 심층 분석, 보안 모델, HTTP API 참조, FAQ를 확인할 수 있습니다. 연락처 폼, 뉴스레터 가입, 다중 폼 사이트, 모니터링 경고, Cloudflare Workers, 내부 SMTP 릴레이(Docker Compose)를 다루는 10가지 레시피와 Hugo+Comentario, Ghost, Gitea 및 셀프 호스팅 Umami 다이제스트에 대한 전체 사례 연구가 포함되어 있습니다. 프로젝트 기록 및 v1.0 사양은 spec/ 디렉터리를 참조하십시오.
빠른 시작 (Docker):
docker-compose.yml
services: posthorn: image: ghcr.io/craigmccaskill/posthorn:latest restart: unless-stopped volumes: - ./posthorn.toml:/etc/posthorn/config.toml:ro environment: POSTMARK_API_KEY: ${POSTMARK_API_KEY} ports: - "127.0.0.1:8080:8080" # 루프백에 바인딩, 전면 프록시(Reverse-proxy)에서 연결
posthorn.toml
[[endpoints]] path = "/api/contact" to = ["you@example.com"] from = "Contact Form noreply@example.com" honeypot = "_gotcha" allowed_origins = ["https://example.com"] required = ["name", "email", "message"] subject = "Contact from {{.name}}" body = """ From: {{.name}} <{{.email}}> {{.message}} """ redirect_success = "/thank-you"
[endpoints.transport] type = "postma...