Stalwart 메일 서버 설치 및 Postfix 비교 분석
Stalwart가 단일 Rust 바이너리로 Postfix와 Dovecot 스택을 어떻게 대체하는지 설명합니다. 포트 25 차단 문제와 엔터프라이즈 라이선스 제한 사항을 포함하여 직접 설치 전 반드시 확인해야 할 기술적 제약과 메일 도달률 최적화 전략을 상세히 다룹니다.
Stalwart가 단일 바이너리로 통합하는 구성 요소
Stalwart는 하나의 VPS에서 단일 Rust 바이너리로 실행하는 메일 서버다. 동일한 프로세스에서 SMTP, IMAP, POP3, JMAP, CalDAV, CardDAV, WebDAV에 응답하며, 자체 스팸 필터, 자체 메시지 저장소, 자체 ACME 클라이언트도 포함한다. 기존 스택에서는 Postfix, Dovecot, Rspamd, 계정용 데이터베이스, 별도의 인증서 도구로 같은 작업을 처리한다. Stalwart는 이 모든 구성 요소를 /etc/stalwart/config.json에 정의하는 하나의 서비스 unit과 하나의 설정 파일로 대체한다.
아래의 모든 수치, 설정 이름, 명령은 Stalwart의 공식 문서, 릴리스 페이지, 설치 스크립트에서 인용했다. 2026년 8월 28일에 v0.16.19 릴리스(2026년 8월 24일 게시)를 기준으로 처음 확인했고, 최신 릴리스가 v0.16.26(2026년 10월 11일 게시)이었던 2026년 10월 12일에 다시 확인했다. 아래 명령은 자신의 서버에서 실행해야 한다. 각 명령 뒤에는 실행 결과를 확인하는 방법을 제시한다.
Stalwart는 GNU Affero General Public License v3.0 (AGPL-3.0)과 Stalwart Enterprise License v2에 따라 이중 라이선스로 제공된다. 일부 기능은 Enterprise 전용이다. 문서에 나열된 HTTP endpoint 목록에서는 /scim/v2/*로 표시한다. 직접 실행해 보지 않은 기능을 기준으로 배포를 계획하기 전에 라이선스 조건을 확인해야 한다.
단일 바이너리를 사용하면 운영 구성 요소가 실제로 줄어든다. 그러나 메일이 도착하는지를 결정하는 두 가지 요소까지 줄어드는 것은 아니다.
포트 25와 DNS 평판은 어떤 소프트웨어를 실행하는지 신경 쓰지 않는다
아웃바운드 TCP 포트 25가 첫 번째 관문이다. 많은 VPS 제공업체는 신규 계정에서 이 포트를 기본적으로 차단한다. 포트 25가 차단되면 서버는 다른 메일 서버로 메일을 직접 전달할 수 없다. 무엇이든 설치하기 전에 포트를 테스트한다.
sudo apt update && sudo apt install -y netcat-openbsd
nc -vz -w 5 alt1.aspmx.l.google.com 25정상적인 결과에서는 약 1초 안에 Connection to alt1.aspmx.l.google.com ... 25 port [tcp/smtp] succeeded!이 출력된다. 차단은 두 가지 방식 중 하나로 나타난다. 패킷이 상위 네트워크에서 삭제되면 상대가 reset을 반환하지 않는다. 따라서 nc가 5초 동안 대기한 후 nc: connect to alt1.aspmx.l.google.com port 25 (tcp) failed: Connection timed out를 출력한다. 방화벽이 패킷을 거부하면 ICMP 오류를 반환한다. netfilter의 reject는 다른 값을 보내도록 지정하지 않는 한 port unreachable을 전송한다. 예를 들어 administratively prohibited를 전송하도록 지정할 수 있다. 따라서 보통 Connection refused 또는 No route to host로 즉시 실패가 반환된다. 어느 경우든 제공업체에 티켓을 열어야 한다.
제공업체가 포트를 열어 주지 않는다면 우회 방법은 relay를 사용하는 것뿐이다. Stalwart는 Relay route를 통해 모든 아웃바운드 메일을 smarthost로 전달할 수 있다. 이 route의 포트 기본값은 25이지만 1부터 65535까지의 값을 사용할 수 있다. implicitTls 스위치와 authUsername 및 authSecret 필드를 사용하면 465 또는 587에서 인증된 relay를 설정할 수 있다. 그러면 메일은 자신의 평판이 아니라 relay의 평판을 사용해 발송된다.
두 번째 관문은 수신 네트워크가 IP 주소와 도메인을 어떻게 평가하는지다. 여기에는 IP의 reverse DNS, SPF, DKIM, DMARC, 그리고 할당받은 주소 블록의 발송 이력이 포함된다. 28 August 2026에 확인한 Stalwart의 DNS 설정 페이지는 이 작업이 어디에서 이루어지는지 분명히 설명했다. reverse DNS 레코드는 "일반적으로 Stalwart 자체가 아니라 호스팅 제공업체가 설정한다"고 명시한다. 나머지도 마찬가지다. 이러한 설정은 메일 서버가 아니라 DNS zone과 제공업체의 control panel에 있다.
따라서 이 페이지에서는 해당 레코드를 다시 설명하지 않는다. 이를 설명하는 문서가 하나 있다: 메일을 발송하는 모든 서비스에 SPF, DKIM, DMARC를 한 번만 설정하기. 아직 자체 메일 서버를 운영할지 결정하지 않았다면 self-hosted email이 아직도 운영할 가치가 있는지 솔직하게 살펴보기부터 읽는다. Stalwart를 선택해도 이 계산은 전혀 달라지지 않는다.
Stalwart 메일 서버를 실행하는 데 필요한 소형 VPS 사양
2026년 10월 12일에 확인한 Stalwart 시스템 요구 사항 페이지에는 다음과 같은 수치가 제시되어 있다. 유휴 상태에서 메모리 사용량은 약 100 MB다. 사용자가 5~10명인 소규모 배포는 RAM 1 GB에서 충분히 실행할 수 있다. 사용자 약 5명인 저트래픽 환경은 CPU 코어 1개로 실행되며, 해당 페이지는 “동시성과 활동량이 증가하면 낮은 지연 시간과 높은 처리량을 유지하기 위해 더 많은 CPU 코어가 필요하다”고 덧붙인다. 기본 제한은 모든 서비스에서 동시 연결 8,192개이며, 이 값은 설정할 수 있다.
해당 페이지에는 최소 디스크 크기가 명시되어 있지 않다. 따라서 보관할 것으로 예상되는 메일 용량에 store 압축 작업을 위한 여유 공간을 더해 디스크 크기를 정해야 한다.
세 가지 outbound 경로가 작동해야 한다. 그렇지 않으면 메일과 무관한 문제로 서버가 고장 난 것처럼 보일 수 있다. 서버는 https://github.com/stalwartlabs/webui/releases/latest/에서 웹 인터페이스 번들을 가져온다. 인증서를 위해 https://acme-v02.api.letsencrypt.org/directory에 연결한다. MX 및 인증 레코드를 조회하려면 UDP와 TCP port 53에서 DNS에 연결할 수 있어야 한다. 첫 번째 경로를 차단하는 엄격한 egress firewall을 사용하면 실행 중인 메일 서버는 갖게 되지만 관리자 인터페이스는 사용할 수 없게 된다.
고정된 릴리스 버전을 설치하고 "latest"는 사용하지 않는다
공식 설치 프로그램은 셸 스크립트다. 실행하기 전에 내용을 확인한다.
curl --proto '=https' --tlsv1.2 -sSf https://get.stalw.art/install.sh -o install.sh
less install.sh
sudo sh install.sh2026년 10월 12일에 이 스크립트를 확인하면 스크립트가 정확히 무엇을 수행하는지 알 수 있다. 스크립트는 stalwart 서비스 계정과 필요한 디렉터리를 생성한다. 그런 다음 https://github.com/stalwartlabs/stalwart/releases/latest/download에서 다운로드한다. 바이너리는 mode 0755로 /usr/local/bin/stalwart에 저장된다. 설정은 /etc/stalwart/config.json에, 데이터는 /var/lib/stalwart에, 로그는 /var/log/stalwart에 저장된다. 이 3개 디렉터리는 모두 mode 0750이며 소유자는 stalwart이다. 환경 변수 파일은 mode 0640으로 root:stalwart가 소유하도록 /etc/stalwart/stalwart.env에 작성된다. 스크립트는 선택적 설치 prefix 1개와 FoundationDB 빌드에 사용하는 --fdb flag를 받는다. 버전 인수는 받지 않는다.
이 점이 중요하다. 스크립트는 항상 최신 릴리스를 가져오므로 일주일 간격으로 구축한 두 서버가 같은 코드를 실행하지 않을 수 있다. 따라서 설치 직후 직접 바이너리 버전을 고정해야 한다. v0.16.26 릴리스 노트에서도 이것을 업그레이드 방법으로 제시한다. "v0.16.x에서 업그레이드하는 경우 바이너리를 교체하거나 docker pull를 실행한다." 2026년 8월 24일부터 10월 11일까지 7개의 patch release가 릴리스되었고, 그중 여러 버전에 보안 수정이 포함되었다. 따라서 최신 버전으로 고정한 뒤 의도적으로 고정 버전을 변경한다.
STALWART_TAG=v0.16.26
curl -fsSLO "https://github.com/stalwartlabs/stalwart/releases/download/${STALWART_TAG}/stalwart-x86_64-unknown-linux-gnu.tar.gz"
tar zxf stalwart-x86_64-unknown-linux-gnu.tar.gz
sudo systemctl stop stalwart
sudo install -m 0755 -o root -g root stalwart /usr/local/bin/stalwart
sudo systemctl start stalwart
systemctl is-active stalwartsystemctl is-active stalwart을 실행하면 active이 출력되어야 한다. 다른 결과가 나오면 journalctl -u stalwart -n 50을 확인한다. 모든 릴리스 asset에는 일치하는 .sigstore.json bundle이 포함되어 있으므로, 설치하기 전에 다운로드한 파일의 서명을 검증할 수 있다.
스크립트가 작성하는 unit은 User=stalwart로 실행되며 AmbientCapabilities=CAP_NET_BIND_SERVICE을 설정한다. 이 capability가 있으면 권한이 없는 계정도 25, 443, 465, 993 포트에 bind할 수 있다. 나중에 직접 unit을 작성하면서 해당 줄을 생략하면 서비스가 시작 시 실패한다. 일반 사용자는 1024 미만의 포트에 bind할 수 없기 때문이다.
최초 관리자 비밀번호가 출력되는 위치
Stalwart는 부트스트랩 모드로 시작하며 16자리의 임시 비밀번호를 서비스 로그에 단 한 번 기록합니다.
sudo journalctl -u stalwart -n 200 | grep -A8 'bootstrap mode'설정 마법사는 8080 포트에서 일반 HTTP로 대기하므로, 해당 포트를 외부로 노출하지 마십시오. 대신 노트북에서 SSH를 통해 터널링하십시오:
ssh -N -L 8080:127.0.0.1:8080 you@your-vps그다음 http://127.0.0.1:8080/admin을 열고 로그에 있는 비밀번호를 사용하여 admin 계정으로 로그인하십시오. 마법사는 서버 호스트 이름, 기본 메일 도메인, TLS, 스토리지, 계정 디렉터리, 로깅 및 DNS 처리 방식을 묻습니다. 설정이 완료되면 서비스를 재시작하고, 이후부터는 https://<your-host>/admin를 사용하십시오.
비밀번호가 로그에서 밀려나 확인이 불가능하다면 고정 비밀번호를 설정하십시오. /etc/stalwart/stalwart.env에는 이를 위한 주석 처리된 항목이 포함되어 있으며, 여기에는 STALWART_RECOVERY_ADMIN=admin:changeme, STALWART_RECOVERY_MODE=true 및 STALWART_RECOVERY_MODE_PORT(기본값 8080)가 포함됩니다. 주석을 해제하고 재시작한 뒤 로그인하고, 다시 주석 처리하십시오. Stalwart의 보안 강화(hardening) 페이지에서는 해당 자격 증명을 비상시에만 사용하고, 관리자 계정으로 IMAP, JMAP 또는 WebDAV에 로그인하지 말 것을 권고합니다.
같은 페이지에는 유지해야 할 리스너 목록이 나열되어 있습니다. 인바운드 SMTP용 25번 포트, 암시적 TLS를 사용하는 제출(submission)용 465번 포트, IMAPS용 993번 포트, 그리고 모든 HTTP 트래픽을 위한 443번 포트입니다. 587, 143, 4190, 110, 995 및 8080 포트는 필수적이지 않은 것으로 간주하며, 설정이 완료되면 8080 포트를 비활성화할 것을 권장합니다.
Docker에서 고정 태그로 실행하기
문서에 명시된 이미지는 stalwartlabs/stalwart입니다. v0.16.26 태그는 2026년 10월 12일에 Docker Hub에 있었으며, -alpine 변형도 함께 제공되었습니다. Stalwart의 Docker 페이지에서는 프로덕션에 v0.16 마이너 태그를 사용할 것을 권장합니다. 위의 바이너리와 같은 이유로 패치 버전을 고정합니다.
services:
stalwart:
image: stalwartlabs/stalwart:v0.16.26
container_name: stalwart
restart: unless-stopped
ports:
- "25:25"
- "465:465"
- "993:993"
- "443:443"
- "127.0.0.1:8080:8080"
volumes:
- stalwart-etc:/etc/stalwart
- stalwart-data:/var/lib/stalwart
volumes:
stalwart-etc:
stalwart-data:이 파일은 문서에 명시된 docker run 명령을 Compose 형식으로 작성한 것입니다. 필수적이지 않은 리스너는 제외했고, 설정 포트는 localhost에 바인딩했습니다. 다음 명령으로 시작한 뒤 동일한 부트스트랩 줄을 확인합니다.
docker compose up -d
docker compose logs stalwart 2>&1 | grep -A8 'bootstrap mode'Docker 페이지에는 시작 시 고정 자격 증명을 설정하는 방법으로 -e STALWART_RECOVERY_ADMIN=admin:mySecretPass도 설명되어 있습니다. 로그를 확인하는 대신 이 방법을 사용하려면 Compose의 environment: 키를 사용하면 됩니다.
문서에 명시된 전체 포트 목록과 이 파일이 더 짧은 이유
Stalwart의 Docker 페이지에는 443, 8080, 25, 587, 465, 143, 993, 110, 995, 4190이 나와 있습니다. 하드닝 페이지에서는 587, 143, 110, 995, 4190을 필수적이지 않은 포트로 분류하고, 설정이 끝난 뒤 8080을 비활성화하라고 설명합니다. 실제 클라이언트가 요청하는 포트만 다시 추가합니다. 휴대폰에서 STARTTLS submission이 반드시 필요하다면 587을 공개합니다. 사용자가 데스크톱 클라이언트에서 Sieve 규칙을 작성한다면 4190을 공개합니다.
Compose가 익숙하지 않다면 VPS용 Docker Compose 안내서에서 이 구성에 사용한 파일 구조와 named volume 모델을 설명합니다. 특히 메일 서버에서 자주 문제가 되는 사항이 있습니다. Docker는 자체 방화벽 규칙을 작성하여 포트를 공개하며, 이 규칙은 ufw의 규칙보다 먼저 적용됩니다. 따라서 ufw deny 8080은 Compose가 공개한 포트를 닫지 못합니다. 포트 매핑에서 127.0.0.1에 바인딩해야 실제로 포트가 닫힙니다. 위 파일이 그렇게 구성된 이유이며, SSH 터널도 계속 적용됩니다.
certbot 없이 TLS를 구성하는 방법과 그에 따른 비용
Stalwart은 ACME(automatic certificate management environment)를 직접 구현하므로 certbot과 갱신 hook이 필요하지 않다. 문서에는 4가지 검증 방법이 나와 있다. HTTP-01은 port 80에서 challenge 요청에 응답한다. TLS-ALPN-01은 ACME 전용 ALPN protocol을 사용해 port 443에서 용도에 맞게 생성된 certificate를 제시한다. DNS-01은 임시 TXT record를 게시하며, wildcard를 발급할 수 있는 2가지 방법 중 하나다. DNS-PERSIST-01은 갱신할 때마다 새 record를 작성하는 대신 오래 유지되는 authorization TXT record를 사용한다.
그 대가로 Stalwart은 TLS handshake를 직접 처리해야 한다. TLS-ALPN-01은 Stalwart이 직접 handshake를 완료하는 방식으로 동작하므로, reverse proxy가 TLS를 대신 종료하는 환경에서는 성공할 수 없다. 해당 서버에서 nginx 또는 Caddy가 이미 443을 사용 중이라면 해결 방법은 3가지다. Stalwart의 nginx guide에서는 listen 443; proxy_pass 127.0.0.1:10443; proxy_protocol on;을 사용해 nginx를 TCP passthrough로 실행하므로 TLS connection이 변경되지 않은 상태로 Stalwart에 도달한다. 이 구성은 443으로 들어오는 모든 connection을 Stalwart에 전달하므로, 해당 port에서 nginx가 다른 서비스를 제공하지 않는 서버에 적합하다. 그렇지 않으면 Stalwart을 DNS-01 또는 HTTP-01로 전환하거나, Stalwart에 전용 IP address를 할당해야 한다.
DANE 및 MTA-STS와 알아두어야 할 기본값
두 설정 모두 웹 인터페이스의 Settings, MTA, Outbound, TLS Strategies 경로에 있는 MtaTlsStrategy 객체에서 TLS 전략별로 구성합니다. dane 필드의 기본값은 optional이며, 수신자가 TLSA 레코드를 게시한 경우 DANE 검증을 시도하고 그렇지 않으면 일반 STARTTLS로 대체합니다. require로 설정하면 검증 가능한 TLSA 레코드가 있을 때만 전송이 진행됩니다. mtaSts 필드도 동일하게 동작하며 기본값은 optional입니다. 관련 타임아웃 설정은 tlsTimeout(기본값 3분)와 mtaStsTimeout(기본값 5분)입니다.
인바운드 측면에서 Stalwart는 https://mta-sts.<domain>/.well-known/mta-sts.txt 경로에 자체 MTA-STS 정책을 게시할 수 있으며, 이를 위해서는 443번 포트가 열려 있어야 합니다. MtaSts 싱글톤에는 mode(기본값 testing), maxAge(기본값 7일), 그리고 mxHosts가 포함되어 있으며, mxHosts가 비어 있으면 TLS 인증서의 호스트 이름을 사용합니다. 두 개의 DNS 레코드를 제공해야 합니다. 메일 호스트를 가리키는 mta-sts CNAME 레코드와 정책 식별자를 포함하는 _mta-sts TXT 레코드입니다.
dig +short TXT _mta-sts.example.org
curl -s https://mta-sts.example.org/.well-known/mta-sts.txtTXT 조회 시 v=STSv1; id=... 문자열이 반환되어야 하며, curl 명령은 정책 본문을 반환해야 합니다. curl이 아무것도 반환하지 않는다면 443번 포트가 닫혀 있거나 mta-sts.example.org에 대한 인증서가 발급되지 않은 상태입니다.
두 확인 절차를 모두 통과할 때까지 mode을 testing로 유지하십시오. 인증서가 잘못된 상태에서 enforce 모드로 정책을 운영하면 다른 서버가 메일을 전달할 수 없게 되며, 로그가 아닌 사용자로부터 문제를 전해 듣게 될 것입니다. DANE에도 주의할 점이 있습니다. DANE은 DNSSEC으로 서명된 영역을 요구하며, 리프 인증서를 고정(pinning)하는 TLSA 레코드는 ACME가 갱신될 때마다 다시 게시해야 합니다. 발급 CA를 고정하거나, 갱신 작업을 감수하십시오.
저장 데이터 암호화는 종단 간 암호화가 아니다
가장 자주 잘못 이해되는 기능이므로, 문서에 명시된 내용을 정확히 설명한다. 각 사용자의 평문 메시지는 디스크에 기록되기 전에 사용자의 OpenPGP 또는 S/MIME 인증서로 자동 암호화된다. encryptAtRest는 기본적으로 활성화되어 있으며, 수신자가 암호화 키를 등록한 경우 SMTP 또는 LMTP로 수신되는 메시지에 적용된다. encryptOnAppend의 기본값은 false이며, "추가된 메시지는 변경하지 않으므로 클라이언트가 저장하는 내용 전체를 계속 제어할 수 있다". OpenPGP는 이전 방식인 PGP/Inline이 아니라 PGP/MIME을 사용하며, AES-256 또는 AES-128을 사용한다. Stalwart는 키를 생성하지 않는다. 사용자는 ASCII-armored 공개 키를 내보낸 다음 Account, Public Keys 아래에서 PublicKey 객체로 등록한다.
따라서 도난당한 디스크 이미지, 도난당한 백업, 그리고 메시지 전달 후 저장 영역을 열람하는 운영자로부터 메시지를 보호한다. 개인 키가 없으면 저장된 바이트를 읽을 수 없으며, 관리자도 메시지를 복호화할 수 없다.
메시지가 전송 중일 때는 보호하지 않는다. 메시지는 두 서버가 합의한 TLS 설정에 따라 인터넷을 통과하고 평문으로 도착한 다음, Stalwart가 그 시점에 암호화한다. 발신자, 발신자의 제공업체, 그리고 TLS를 제거한 모든 중간 홉은 이미 평문을 확인한 상태다.
추가로 세 가지 제한을 분명히 밝혀야 한다. Sent 및 Drafts 폴더는 클라이언트가 기록하며 이는 append 동작이다. 또한 encryptOnAppend는 기본적으로 false이므로 설정을 변경하지 않으면 해당 폴더의 내용은 평문으로 남는다. 2026년 10월 12일에 다시 확인한 문서에는 메시지 내용만 설명되어 있으며 envelope 데이터, 헤더 또는 인덱스 항목이 암호화된다고 명시되어 있지 않다. 따라서 암호화된다고 가정하지 않아야 한다. 키를 업로드하기 전에 이미 저장된 메시지가 다시 암호화되는지도 명시되어 있지 않다. 다시 암호화되지 않는다고 가정하고 확인해야 한다. 암호화된 본문에서도 전체 텍스트 검색이 계속 작동하는지는 명시되어 있지 않다. 다른 사용자에게 지원된다고 약속하기 전에 테스트용 계정에서 시험해야 한다.
사용자가 개인 키를 잃으면 해당 사용자의 메일도 사라진다. 설계상 복구 경로는 없다.
WKD는 메일 서버 작업이 아니라 웹 서버 작업이다
WKD(Web Key Directory)는 OpenPGP에서 별도의 역할을 하며, 다른 문제를 해결한다. WKD는 도메인 아래의 고정된 HTTPS URL에 공개 키를 게시한다. 그러면 발신자의 메일 클라이언트가 해당 키를 찾아 메시지가 발신자의 장치를 떠나기 전에 암호화할 수 있다. 이것이 종단 간 암호화다. Stalwart의 저장 데이터 암호화는 디스크에 저장된 사본을 보호한다. 한 가지를 설정해도 다른 기능이 자동으로 제공되지는 않는다.
Stalwart는 WKD를 제공하지 않는다. 2026년 10월 12일에 확인한 Stalwart의 문서화된 HTTP endpoint에는 jmap, caldav, carddav, oauth-authorization-server, openid-configuration, acme-challenge, mta-sts.txt, mail-v1.xml 및 autoconfig에 대한 well-known 경로가 나열되어 있다. openpgpkey 경로는 없다. WKD는 일반적인 정적 웹 서버에서 제공해야 한다.
이 사양은 2가지 레이아웃을 정의한다. advanced 방식은 https://openpgpkey.example.org/.well-known/openpgpkey/example.org/hu/iy9q119eutrkn8s1mk4r39qejnbu3n5q?l=Joe.Doe을 사용한다. direct 방식은 https://example.org/.well-known/openpgpkey/hu/iy9q119eutrkn8s1mk4r39qejnbu3n5q?l=Joe.Doe을 사용한다. 이 32자 문자열은 소문자로 변환한 local part를 SHA-1로 해시한 뒤 z-base-32로 인코딩한 값이다. 따라서 이러한 파일 이름을 직접 만들면 안 된다. GnuPG가 대신 생성한다.
gpg --export --armor you@example.org > you.asc
gpg-wks-client --print-wkd-url you@example.org
gpg-wks-client --install-key you.asc you@example.org--print-wkd-url는 sub-domain 형식을 사용해 클라이언트가 가져올 URL을 출력한다. --install-key는 WKD 레이아웃을 반영한 로컬 디렉터리 트리에 키를 기록한다. 이 트리는 기본적으로 openpgpkey이라는 최상위 디렉터리 아래에 생성되며, -C dir로 변경할 수 있다. 이 트리를 웹 루트에 복사한다. hu 디렉터리 옆에 필요한 policy 파일도 추가한다. 빈 파일도 유효하다. 그런 다음 curl로 자신의 URL을 가져와 404가 아니라 키 데이터가 반환되는지 확인한다.
단일 VPS에서의 스토리지
Stalwart는 스토리지를 네 가지 역할로 나눕니다. 메일함 상태와 같은 구조화된 레코드를 위한 데이터 저장소, 원시 메시지 바이트와 첨부 파일을 위한 블롭(blob) 저장소, 전문 검색 인덱싱을 위한 검색 저장소, 그리고 속도 제한, 인증 토큰, 세션 데이터를 위한 인메모리 저장소가 그것입니다. 각 역할은 서로 다른 백엔드를 가리킬 수 있습니다. 지원되는 목록에는 RocksDB, FoundationDB, PostgreSQL, MySQL, SQLite, S3 호환 객체 스토리지, Azure Blob Storage, Redis, ElasticSearch, Meilisearch가 포함됩니다.
단일 VPS 환경에서의 답은 간단합니다. 문서에서는 RocksDB를 "속도와 신뢰성 때문에 Stalwart의 단일 노드 설치에 권장되는 백엔드"라고 설명합니다. Redis는 인메모리 저장소로만 지원되며 데이터 저장소나 블롭 저장소로 사용할 수 없으므로, 시작하기 위해 별도의 Redis 컨테이너가 필요하지 않습니다. 메일함이 디스크 용량을 초과하면 나중에 블롭 저장소를 S3로 옮기면 됩니다.
백업은 백엔드에 따라 다릅니다. 외부 데이터베이스의 경우 해당 데이터베이스의 자체 절차를 사용하십시오. 내장 데이터베이스의 경우 FAQ에서는 /var/lib/stalwart 디렉터리를 복사하라고 안내합니다. 이 작업은 서비스를 중지한 상태에서 수행하거나, 파일 시스템 또는 볼륨 스냅샷을 통해 수행해야 합니다. 실행 중인 키-값 저장소를 파일 수준에서 복사하면 쓰기 도중의 상태가 캡처될 수 있으며, 복원을 시도하기 전까지는 데이터 손상 여부를 알 수 없습니다.
Rspamd를 대체하는 스팸 필터
필터링은 동일한 프로세스 내부에서 실행되므로, 별도로 유지해야 할 두 번째 데몬은 없습니다. 분류기는 Settings, Spam Filter, Classifier 아래의 SpamClassifier 싱글톤에서 설정합니다. 이 분류기는 특성 해싱(feature hashing)과 함께 FTRL-Proximal 알고리즘을 사용합니다. 대부분의 배포 환경에서는 FtrlFh을 기본값으로 권장합니다. FtrlCcfh는 해시 충돌을 줄이기 위해 쿠쿠(cuckoo) 특성 해싱으로 교체하며, 대규모 배포 환경을 대상으로 합니다. 학습은 지속적으로 이루어집니다. 사용자가 메시지를 스팸이나 정상 메일(ham)로 표시하면, 해당 레이블이 즉시 반영되어 향후 결정에 사용됩니다.
분류기 주변에는 DNS 차단 목록, 그레이리스팅(greylisting), 피싱 탐지, 스팸 트랩, Pyzor가 배치되어 있으며, 포기할 수 없는 규칙이 있는 경우 milter를 통해 SpamAssassin을 호출하는 옵션도 제공합니다.
mailcow가 여전히 적합한 경우
Stalwart에는 webmail이 없습니다. 이것이 가장 큰 차이이며, 아직 격차가 좁혀지지 않았습니다. 12 October 2026에 확인한 mailcow 문서에는 SOGo를 포함해 16개의 구성 요소가 나열되어 있습니다. SOGo를 사용하면 사용자는 기본으로 브라우저 inbox와 CalDAV 및 CardDAV 인터페이스를 이용할 수 있습니다. 20 June 2025에 게시된 Stalwart 로드맵 글에서는 내장 webmail이 “계획에는 있지만 현재 당장의 우선순위는 아니다”라고 설명합니다. 또한 v1.0 이후 Dioxus를 사용해 Rust로 구현할 예정이며, “아마도 2026년 중”이라고 밝힙니다. 12 October 2026 현재 프로젝트 블로그에는 webmail 출시를 알리는 글이 없습니다. 따라서 Stalwart를 사용하면 직접 Roundcube를 배포하거나, 모든 사용자에게 mail client를 설정하라고 안내해야 합니다.
Stalwart에는 web admin interface가 있으므로, 이것이 사람들이 예상하는 격차는 아닙니다. 두 번째 격차는 버전 성숙도입니다. FAQ에 따르면 Stalwart는 0.x 단계이며 v1.0 이전에 data layout과 configuration이 변경될 수 있습니다. 이 경우 migration이 필요할 수 있습니다. 프로젝트가 2026년 6월에 게시한 글의 제목은 “Zero open bug reports: The road to Stalwart 1.0”입니다. 이는 현재 상태를 보여줍니다. 거의 도달했지만 아직 v1.0은 아닙니다. 12 October 2026 현재 최신 release도 여전히 v0.16.26입니다.
세 번째 격차는 누구도 feature list에 넣지 않는 부분입니다. Postfix, Dovecot, Rspamd에는 10년에 걸쳐 축적된 문서화된 답변이 있습니다. 새벽 2시에 mail이 queue에 쌓이고 사용자가 기다리는 상황에서는, 일치하는 error string을 반환하는 검색 결과가 우아한 architecture보다 더 큰 가치가 있습니다. 이것이 현재 상황이라면 mailcow 설치 가이드에서 전체 stack을 처음부터 끝까지 설명하므로 더 짧은 밤을 보낼 수 있습니다.
하나의 binary, 하나의 config file, JMAP을 원하고 early adopter가 되는 데 익숙하다면 Stalwart를 선택하십시오. 지금 바로 webmail이 필요하고 기존 답변이 풍부한 환경을 원한다면 mailcow를 선택하십시오.
기존 메일 이전
일반적인 경로는 imapsync를 사용하는 IMAP 간 이전이며, 양쪽 끝에서 어떤 소프트웨어를 실행하는지는 중요하지 않습니다. 먼저 드라이 런(dry run)을 수행하십시오.
imapsync --dry \
--host1 old.example.org --user1 you@example.org --passfile1 /root/.old.pw \
--host2 mail.example.org --user2 you@example.org --passfile2 /root/.new.pw--dry 플래그를 사용하면 imapsync는 "실제 작업을 수행하지 않고 수행될 작업만 출력"하므로, 플래그를 제거하기 전에 해당 출력을 확인하십시오. 각 암호 파일은 첫 번째 줄에 암호를 포함하므로, chmod 600 두 파일을 모두 사용한 뒤 나중에 삭제하십시오.
Stalwart는 대부분의 타사 가이드가 아직 다루지 못한 최신 도구도 함께 제공합니다. Stalwart 블로그에는 JMAP 가져오기 및 내보내기 도구인 Vandelay(2026년 5월 29일)와 무중단 업그레이드를 위한 마이그레이션 프록시(2026년 6월 10일)에 대한 문서가 있습니다. 다른 곳에서 찾을 수 있는 거의 모든 자료보다 최신 정보이므로 대규모 이전을 계획하기 전에 두 문서를 모두 읽어보시기 바랍니다.
실패 원인과 확인하게 될 문자열
관리자 인터페이스가 전혀 로드되지 않는다. FAQ에서 이 문제를 직접 설명한다. 웹 인터페이스 번들은 첫 실행 시 GitHub에서 다운로드된다. 따라서 github.com로 나가는 HTTPS 연결이 없는 서버에서는 서비스가 실행되더라도 웹 인터페이스의 모든 경로가 404를 반환한다. 서버에서 curl -sI https://github.com/stalwartlabs/webui/releases/latest/로 확인한다. FAQ에 나오는 다른 일반적인 원인은 HTTP 또는 HTTPS 스킴이 일치하지 않는 경우와 리버스 프록시가 클라이언트 IP를 전달하지 않는 경우다.
로그에 bootstrap 비밀번호가 없다. bootstrap 모드에서는 시작할 때 한 번만 출력된다. 그 후 서비스가 재시작되었다면 sudo journalctl -u stalwart --since today | grep -A8 'bootstrap mode'로 확인 범위를 넓힌다. 실제로 사라졌다면 /etc/stalwart/stalwart.env에서 STALWART_RECOVERY_ADMIN을 설정하고 재시작한다.
로컬 프록시를 통한 relay가 거부된다. v0.16.19 릴리스 노트에는 host resolves loopback address로 relay 경로가 거부되는 문제의 수정 사항이 기록되어 있다. 이 문자열이 그대로 나타난다면 이전 빌드를 실행 중인 것이다. 우회하지 말고 최신 버전으로 고정한다.
직접 unit을 작성한 뒤 서비스가 시작되지 않는다. AmbientCapabilities=CAP_NET_BIND_SERVICE이 없으면 stalwart 사용자는 port 25, 443, 465 또는 993에 bind할 수 없으며, 첫 번째 listener에서 시작이 실패한다. 설치 프로그램이 생성한 unit에서 capability 행을 복사한다.
인증서가 발급되지 않는다. HTTP-01에는 port 80에 연결할 수 있어야 하며 해당 port가 사용 중이 아니어야 한다. TLS-ALPN-01에서는 Stalwart 자체가 443에서 TLS handshake에 응답해야 한다. 서버의 다른 프로세스가 둘 중 하나의 port를 사용하고 있으면 다른 기능은 정상적으로 보이는 동안에도 ACME가 계속 조용히 실패한다.
FAQ
Stalwart는 하나의 VPS에서 Postfix, Dovecot, Rspamd를 대체합니까?
그렇습니다. 하나의 Rust 바이너리가 SMTP, IMAP, POP3, JMAP, CalDAV, CardDAV, WebDAV에 응답하며, 스팸 필터, 메시지 저장소, ACME 클라이언트도 포함합니다. 4개의 데몬과 이를 연결하는 설정 대신 /etc/stalwart/config.json에 systemd unit 1개와 설정 파일 1개만 있으면 됩니다. 다만 DNS zone과 provider의 port 25 정책은 대체하지 않습니다. 실제 self-hosted 메일 운영의 성패는 이 두 요소에 달려 있습니다.
Stalwart 메일 서버에는 RAM이 얼마나 필요합니까?
2026년 10월 12일에 확인한 Stalwart 시스템 요구 사항 페이지에서는 유휴 상태에서 약 100 MB가 필요하며, 5~10명의 소규모 배포에는 RAM 1 GB가 적합하다고 설명합니다. 트래픽이 적고 사용자가 약 5명인 구성은 단일 CPU core에서 실행됩니다. 기본 제한은 모든 서비스에 걸쳐 동시에 연결할 수 있는 연결 수 8,192개이며 구성할 수 있습니다. 따라서 한계는 사용자 수만이 아니라 연결 수와 메일 양에 따라 높아집니다. 공개된 최소 disk 크기는 없으므로 보관할 메일을 기준으로 disk 크기를 정해야 합니다.
Stalwart로 전환하면 메일 deliverability가 향상됩니까?
아닙니다. deliverability는 VPS에서 outbound TCP port 25가 열려 있는지, IP의 reverse DNS가 설정되어 있는지, domain에 SPF, DKIM, DMARC가 설정되어 있는지에 따라 결정됩니다. Stalwart는 DANE, MTA-STS, SMTP TLS reporting을 지원하며 MTA-STS policy를 대신 게시할 수도 있습니다. 그러나 이러한 기능은 transport security를 관리할 뿐, 수신 network가 해당 주소를 신뢰할지는 결정하지 않습니다. 무엇이든 설치하기 전에 nc -vz -w 5 alt1.aspmx.l.google.com 25로 port 25를 테스트하십시오.
Stalwart에 webmail이 포함됩니까?
2026년 10월 12일 기준으로는 포함되지 않습니다. web admin interface는 제공하지만 webmail과는 다른 기능입니다. 프로젝트의 2025년 6월 20일 roadmap 게시물에는 version 1.0 이후에 Rust와 Dioxus로 webmail client를 개발할 계획이며 “아마도 2026년 중”이라고 설명되어 있습니다. 아직 프로젝트 blog에는 해당 기능의 발표가 없습니다. 지금 사용자가 browser inbox를 사용해야 한다면 Roundcube를 함께 배포하거나 SOGo를 포함하는 stack을 사용하십시오.
Stalwart의 저장 데이터 암호화는 무엇으로부터 보호합니까?
각 사용자의 OpenPGP 또는 S/MIME public key로 메시지를 암호화한 뒤 disk에 기록합니다. 따라서 도난당한 disk나 backup을 확보한 사람이 메시지를 읽거나 administrator가 저장소를 읽어 내용을 복구하는 것을 막을 수 있습니다. 이는 end-to-end encryption이 아닙니다. 메시지는 plain text로 도착한 뒤 전달 시 암호화되므로, 그 전까지의 모든 hop에서는 메시지를 볼 수 있습니다. encryptOnAppend의 기본값은 false이므로 값을 변경하지 않으면 client가 저장하는 Sent와 Drafts는 평문으로 남습니다. 문서는 메시지 내용만 다루며 metadata, index entry, key를 업로드하기 전에 저장된 메일의 재암호화에 대해서는 설명하지 않습니다. 따라서 이를 당연히 지원한다고 가정하지 말고 직접 확인해야 합니다.