# Docker Compose로 Mattermost 사내 메신저 구축하기

Docker Compose로 Mattermost 사내 메신저 구축하기 (Teams 대체)

Teams를 더 이상 쓰지 않게 되면서, 무료 오픈소스 메신저를 자체 서버에 설치해보기로 했다. 여러 후보(Rocket.Chat, Zulip, Matrix) 중 Mattermost를 선택했다. Slack과 가장 비슷한 UX를 제공하고, 오픈소스 Team Edition은 인원 제한 없이 완전 무료이기 때문이다.

왜 Mattermost인가

  • 2015년부터 개발된 오픈소스 프로젝트로 커뮤니티/문서가 풍부하다.
  • 채팅, 채널, 파일 업로드, 검색, 멘션, DM, 이모지 반응 등 기본 기능은 전부 무료(Team Edition)이며 인원 제한이 없다.
  • LDAP/SSO, 고가용성 클러스터링, 컴플라이언스 리포트 같은 기능만 유료(Professional/Enterprise) 티어에서 제공된다. 일반적인 사내 채팅+파일공유 용도라면 무료판으로 충분하다.

docker-compose.yml 작성

Mattermost(Team Edition) + PostgreSQL 조합으로 구성했다.

version: "3.8"

services:
  postgres:
    image: postgres:15-alpine
    container_name: mattermost-postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: mmuser
      POSTGRES_PASSWORD: <비밀번호>
      POSTGRES_DB: mattermost
    volumes:
      - /mnt/data/mattermost/postgres:/var/lib/postgresql/data
    networks:
      - mattermost-network

  mattermost:
    image: mattermost/mattermost-team-edition:latest
    container_name: mattermost
    restart: unless-stopped
    depends_on:
      - postgres
    ports:
      - "8065:8065"
    environment:
      MM_SQLSETTINGS_DRIVERNAME: postgres
      MM_SQLSETTINGS_DATASOURCE: "postgres://mmuser:<비밀번호>@postgres:5432/mattermost?sslmode=disable&connect_timeout=10"
      MM_SERVICESETTINGS_SITEURL: "http://서버IP:8065"
      MM_FILESETTINGS_DRIVERNAME: "local"
      MM_FILESETTINGS_DIRECTORY: "/mattermost/data"
    volumes:
      - /mnt/data/mattermost/config:/mattermost/config
      - /mnt/data/mattermost/data:/mattermost/data
      - /mnt/data/mattermost/logs:/mattermost/logs
      - /mnt/data/mattermost/plugins:/mattermost/plugins
      - /mnt/data/mattermost/client-plugins:/mattermost/client/plugins
      - /mnt/data/mattermost/bleve-indexes:/mattermost/bleve-indexes
    networks:
      - mattermost-network

networks:
  mattermost-network:
    driver: bridge

포인트

  • 포트 충돌 방지: 서버에 이미 여러 서비스(Jenkins, GitLab, Elasticsearch 등)가 떠 있었기 때문에, docker ps로 사용 중인 포트를 먼저 확인하고 8065 포트가 비어있는지 체크했다.
  • 볼륨 경로: 루트 파티션 용량이 넉넉하지 않아서(df -h 확인 결과 71% 사용 중), 별도로 마운트된 대용량 디스크(/mnt/data) 아래에 데이터를 저장하도록 경로를 잡았다.
  • DB 격리: postgres 서비스는 호스트에 포트를 노출하지 않고 컨테이너 내부 네트워크에서만 통신하도록 설정했다. 컨테이너 이름과 데이터 경로가 기존에 떠 있던 다른 postgres/mariadb 컨테이너들과 전혀 겹치지 않아 기존 서비스에 영향이 없다.

트러블슈팅 1: 권한 에러 (permission denied)

처음 docker compose up -d 실행 시 아래 에러가 반복적으로 발생했다.

Error: failed to load configuration: could not create config file: open /mattermost/config/config.json: permission denied

원인: Mattermost 컨테이너는 root가 아닌 UID 2000 사용자로 실행되는데, mkdir -p로 만든 호스트 폴더의 소유자가 root(또는 sudo 실행 계정)라서 컨테이너 내부에서 쓰기 권한이 없었다.

해결:

sudo chown -R 2000:2000 /mnt/data/mattermost/config
sudo chown -R 2000:2000 /mnt/data/mattermost/data
sudo chown -R 2000:2000 /mnt/data/mattermost/logs
sudo chown -R 2000:2000 /mnt/data/mattermost/plugins
sudo chown -R 2000:2000 /mnt/data/mattermost/client-plugins
sudo chown -R 2000:2000 /mnt/data/mattermost/bleve-indexes

이후 재기동하니 정상적으로 마이그레이션이 진행되고 서버가 떴다.

초기 설정: 관리자 계정 & 팀 초대

  • 첫 계정 생성 시 사용자명(Username)은 영문 소문자 + 숫자만 가능하다. 한글 사용자명 입력 시 에러가 난다.
  • 팀원 초대는 두 가지 방식이 있다.
    1. 초대 링크 복사 → 카톡 등으로 직접 전달
    2. 관리자 도구 → 사이트 구성 → "누구나 이 팀에 참여할 수 있습니다" 토글 활성화 → 서버 주소만 알려주면 팀원이 알아서 가입
  • 이메일 초대는 SMTP 서버가 설정되어 있지 않으면 전송되지 않는다("SMTP가 시스템 콘솔에서 구성되지 않았습니다"). 소규모 팀이면 SMTP 설정 없이 링크/오픈서버 방식으로 충분하다.
  • 데스크톱 앱에서 초대 링크 클립보드 복사가 안 되는 경우가 있었는데, 이때는 System Console에서 "공개 서버"를 활성화하고 서버 주소만 공유하는 방식으로 우회했다. (관리자 도구 → 사이트 구성 → 인증 → 가입 → 공개 서버: 활성화)

트러블슈팅 2: 개인 메시지가 실시간으로 안 뜸

DM을 보내도 바로 화면에 안 뜨고, 다른 채널로 갔다 와야 보이는 증상이 있었다.

확인한 것들:

  • docker stats로 CPU/메모리 확인 → 리소스는 충분히 여유로움 (CPU 0.1%대)
  • 컨테이너 로그에서 웹소켓 에러 확인 → 특이 에러 없음

이 증상은 전형적으로 웹소켓 연결 문제일 때 나타난다. Mattermost는 실시간 메시지 전달을 위해 웹소켓을 쓰는데, MM_SERVICESETTINGS_SITEURL 값이 실제 접속 주소와 다르면 웹소켓 연결이 불안정해질 수 있어 이 값을 서버 실제 IP로 맞춰주는 것이 첫 번째 점검 포인트였다.

트러블슈팅 3: 모바일 푸시 알림이 안 옴

모바일 앱에서 "이 서버의 알림을 수신할 수 없습니다" 메시지가 계속 떴다.

1차 조치: System Console → 환경 → 푸시 알림 서버에서 다음과 같이 설정

  • 모바일 푸시 알림: TPNS 연결을 사용하여 iOS 및 Android 앱에 알림을 전송합니다
  • 푸시 알림 서버: https://push.mattermost.com (기본값인 push-test.mattermost.com이 아니라 test가 빠진 정식 URL로 변경)

이 값을 바꾸고 컨테이너를 재시작해도 동일한 에러가 계속 발생했다.

진짜 원인: 서버 주소가 http://(HTTPS 아님)였기 때문이다. Mattermost 공식 모바일 앱은 보안 정책상 서버가 HTTPS로 연결되어 있어야만 푸시 알림 등록을 허용한다. 즉 TPNS 설정 자체는 문제가 아니었고, 사이트 자체가 HTTPS가 아닌 것이 근본 원인이었다.

해결 방향:

  • 사내망 + IP 접속이라면 → 자체 서명 SSL 인증서 + Nginx 리버스 프록시 (기기에서 인증서 신뢰 예외 처리 필요)
  • 사용 가능한 도메인이 있다면 → Let's Encrypt 무료 인증서로 정식 HTTPS 적용 (권장)

정리

항목 무료 여부
채팅, 채널, DM, 파일 업로드 무료 (Team Edition)
데스크톱/모바일 알림 무료, 단 모바일 푸시는 HTTPS 필요
SSO/LDAP, 고가용성, 컴플라이언스 유료 (Professional/Enterprise)

소규모 팀의 사내 채팅용으로는 무료 Team Edition만으로 충분했고, docker-compose 하나로 기존에 운영 중이던 다른 서비스들과 완전히 격리된 상태로 손쉽게 추가할 수 있었다. 다만 모바일 푸시 알림까지 정상적으로 받으려면 HTTPS 적용이 사실상 필수라는 점은 미리 알아두면 좋을 것 같다.

파일 공유/동기화가 추가로 필요하다면 OneDrive 대체제로 Nextcloud를 같은 방식(docker-compose)으로 조합해서 쓸 수 있다.